diff --git a/SerialPrograms/Source/Pokemon/Pokemon_AdvRng.cpp b/SerialPrograms/Source/Pokemon/Pokemon_AdvRng.cpp index c1ae25df4d..3d60d70d76 100644 --- a/SerialPrograms/Source/Pokemon/Pokemon_AdvRng.cpp +++ b/SerialPrograms/Source/Pokemon/Pokemon_AdvRng.cpp @@ -11,7 +11,7 @@ namespace PokemonAutomation{ namespace Pokemon{ -void level_up_observed_pokemon(AdvObservedPokemon& pokemon, StatReads& newstats, EVs& evyield){ +void level_up_observed_pokemon(AdvObservedPokemon& pokemon, const StatReads& newstats, const EVs& evyield){ uint8_t newlevel = pokemon.level.back() + 1; pokemon.level.emplace_back(newlevel); @@ -69,7 +69,7 @@ uint8_t gender_value_from_pid(uint32_t& pid){ return pid & 0xff; } -AdvGender gender_from_gender_value(uint8_t gender_value, uint8_t threshold){ +AdvGender gender_from_gender_value(uint8_t gender_value, int16_t threshold){ return (gender_value <= threshold) ? AdvGender::Female : AdvGender::Male; } @@ -145,7 +145,7 @@ AdvShinyType shiny_type_from_pid(uint32_t pid, uint16_t tid_xor_sid){ } -bool check_for_match(AdvPokemonResult res, AdvRngFilters target, uint16_t tid_xor_sid, uint8_t gender_threshold){ +bool check_for_match(AdvPokemonResult res, AdvRngFilters target, int16_t gender_threshold, uint16_t tid_xor_sid){ return (target.nature == AdvNature::Any || res.nature == target.nature) && (target.ability == AdvAbility::Any || res.ability == target.ability) && (target.gender == AdvGender::Any || gender_from_gender_value(res.gender, gender_threshold) == target.gender) @@ -192,8 +192,8 @@ void AdvRngSearcher::search_advance_range( AdvRngFilters& target, uint64_t min_advances, uint64_t max_advances, - uint16_t tid_xor_sid, - uint8_t gender_threshold + int16_t gender_threshold, + uint16_t tid_xor_sid ){ for (uint8_t m=0; m<3; m++){ set_state_advances(min_advances); @@ -220,7 +220,7 @@ void AdvRngSearcher::search_advance_range( for (uint64_t a=min_advances; a AdvRngSearcher::search( const std::vector& seeds, uint64_t min_advances, uint64_t max_advances, - uint16_t tid_xor_sid, - uint8_t gender_threshold + int16_t gender_threshold, + uint16_t tid_xor_sid ){ std::map hits; for (uint16_t seed : seeds){ set_seed(seed); - search_advance_range(hits, target, min_advances, max_advances, tid_xor_sid, gender_threshold); + search_advance_range(hits, target, min_advances, max_advances, gender_threshold, tid_xor_sid); } return hits; } @@ -248,13 +248,13 @@ std::map AdvRngSearcher::search( void AdvRngSearcher::refine_search( std::map& map, AdvRngFilters& target, - uint16_t tid_xor_sid, - uint8_t gender_threshold + int16_t gender_threshold, + uint16_t tid_xor_sid ){ for (auto iter = map.begin(); iter != map.end(); ){ state = iter->first; AdvPokemonResult res = pokemon_from_state(state); - if (!check_for_match(res, target, tid_xor_sid, gender_threshold)){ + if (!check_for_match(res, target, gender_threshold, tid_xor_sid)){ iter = map.erase(iter); }else{ iter++; diff --git a/SerialPrograms/Source/Pokemon/Pokemon_AdvRng.h b/SerialPrograms/Source/Pokemon/Pokemon_AdvRng.h index 388be6d7ea..5b0511cd8c 100644 --- a/SerialPrograms/Source/Pokemon/Pokemon_AdvRng.h +++ b/SerialPrograms/Source/Pokemon/Pokemon_AdvRng.h @@ -130,7 +130,10 @@ struct AdvRngFilters{ // updates the AdvObservedPokemon with info from leveling up // assumes levels are earned sequentially // input EVs are the ones earned since the last level up, not the total -void level_up_observed_pokemon(AdvObservedPokemon& pokemon, StatReads& newstats, EVs& evyield); +void level_up_observed_pokemon(AdvObservedPokemon& pokemon, const StatReads& newstats, const EVs& evyield); + +// returns the appropriate NatureAdjustments for an AdvNature +Pokemon::NatureAdjustments nature_to_adjustment(AdvNature nature); // returns search filters that correspond with observed stats AdvRngFilters observation_to_filters(const AdvObservedPokemon& observation, const BaseStats& basestats, AdvRngMethod method = AdvRngMethod::Method1); @@ -154,15 +157,15 @@ class AdvRngSearcher{ const std::vector& seeds, uint64_t min_advances, uint64_t max_advances, - uint16_t tid_xor_sid = 0, - uint8_t gender_threshold = 126 + int16_t gender_threshold = 126, + uint16_t tid_xor_sid = 0 ); void refine_search( std::map& map, AdvRngFilters& target, - uint16_t tid_xor_sid = 0, - uint8_t gender_threshold = 126 + int16_t gender_threshold = 126, + uint16_t tid_xor_sid = 0 ); private: @@ -171,8 +174,8 @@ class AdvRngSearcher{ AdvRngFilters& target, uint64_t min_advances, uint64_t max_advances, - uint16_t tid_xor_sid, - uint8_t gender_threshold + int16_t gender_threshold, + uint16_t tid_xor_sid ); }; diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_BlindNavigation.cpp b/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_BlindNavigation.cpp index 8f42bd5295..a37061114b 100644 --- a/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_BlindNavigation.cpp +++ b/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_BlindNavigation.cpp @@ -392,6 +392,8 @@ void check_timings( ); } return; + case PokemonFRLG_RngTarget::hitmonchan: + case PokemonFRLG_RngTarget::hitmonlee: case PokemonFRLG_RngTarget::hitmon: if (INGAME_DELAY < 4500){ OperationFailedException::fire( @@ -419,6 +421,9 @@ void check_timings( ); } return; + case PokemonFRLG_RngTarget::omanyte: + case PokemonFRLG_RngTarget::kabuto: + case PokemonFRLG_RngTarget::aerodactyl: case PokemonFRLG_RngTarget::fossils: if (INGAME_DELAY < 6000){ OperationFailedException::fire( @@ -431,6 +436,8 @@ void check_timings( case PokemonFRLG_RngTarget::gamecornerabra: case PokemonFRLG_RngTarget::gamecornerclefairy: case PokemonFRLG_RngTarget::gamecornerdratini: + case PokemonFRLG_RngTarget::gamecornerscyther: + case PokemonFRLG_RngTarget::gamecornerpinsir: case PokemonFRLG_RngTarget::gamecornerbug: case PokemonFRLG_RngTarget::gamecornerporygon: if (INGAME_DELAY < 8500){ @@ -608,6 +615,8 @@ void perform_blind_sequence( case PokemonFRLG_RngTarget::magikarp: collect_magikarp_after_delay(context, INGAME_DELAY); return; + case PokemonFRLG_RngTarget::hitmonchan: + case PokemonFRLG_RngTarget::hitmonlee: case PokemonFRLG_RngTarget::hitmon: collect_hitmon_after_delay(context, INGAME_DELAY); return; @@ -617,6 +626,9 @@ void perform_blind_sequence( case PokemonFRLG_RngTarget::lapras: collect_lapras_after_delay(context, INGAME_DELAY); return; + case PokemonFRLG_RngTarget::omanyte: + case PokemonFRLG_RngTarget::kabuto: + case PokemonFRLG_RngTarget::aerodactyl: case PokemonFRLG_RngTarget::fossils: collect_fossil_after_delay(context, INGAME_DELAY); return; @@ -629,6 +641,8 @@ void perform_blind_sequence( case PokemonFRLG_RngTarget::gamecornerdratini: collect_gamecorner_after_delay(context, INGAME_DELAY, 2); return; + case PokemonFRLG_RngTarget::gamecornerscyther: + case PokemonFRLG_RngTarget::gamecornerpinsir: case PokemonFRLG_RngTarget::gamecornerbug: collect_gamecorner_after_delay(context, INGAME_DELAY, 3); return; diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_BlindNavigation.h b/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_BlindNavigation.h index 338c0e12fa..8f3dd0c8d0 100644 --- a/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_BlindNavigation.h +++ b/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_BlindNavigation.h @@ -21,13 +21,20 @@ namespace PokemonFRLG{ starters, magikarp, hitmon, + hitmonchan, + hitmonlee, eevee, lapras, fossils, + omanyte, + kabuto, + aerodactyl, gamecornerabra, gamecornerclefairy, gamecornerdratini, gamecornerbug, + gamecornerscyther, + gamecornerpinsir, gamecornerporygon, togepi, staticencounter, diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_RngCalibration.cpp b/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_RngCalibration.cpp new file mode 100644 index 0000000000..b4ff0cb155 --- /dev/null +++ b/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_RngCalibration.cpp @@ -0,0 +1,374 @@ +/* RNG Calibration + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#include +#include "CommonFramework/Exceptions/OperationFailedException.h" +#include "PokemonFRLG_RngCalibration.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonFRLG{ + +void check_seed_validity(ConsoleHandle& console, std::string seed_string){ + static const std::map MAP{ + {'1', '1'}, + {'2', '2'}, + {'3', '3'}, + {'4', '4'}, + {'5', '5'}, + {'6', '6'}, + {'7', '7'}, + {'8', '8'}, + {'9', '9'}, + {'0', '0'}, + {'A', 'A'}, {'a', 'A'}, + {'B', 'B'}, {'b', 'B'}, + {'C', 'C'}, {'c', 'C'}, + {'D', 'D'}, {'d', 'D'}, + {'E', 'E'}, {'e', 'E'}, + {'F', 'F'}, {'f', 'F'} + }; + + if (seed_string.size() != 4){ + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "GiftRng(): Invalid seed length. Seeds should be 4 characters.", + console + ); + } + + for (char ch : seed_string){ + auto iter = MAP.find(ch); + if (iter == MAP.end()){ + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "GiftRng(): Invalid seed character. Seeds should be hex strings (valid characters are 0-9 and A-F).", + console + ); + } + } +} + +uint16_t parse_seed(ConsoleHandle& console, std::string seed_string){ + check_seed_validity(console, seed_string); + std::istringstream converter(seed_string); + uint16_t value; + converter >> std::hex >> value; + return value; +} + +std::vector parse_seed_list(ConsoleHandle& console, std::string seed_list_string){ + std::vector values; + auto ss = std::stringstream{seed_list_string}; + for (std::string line; std::getline(ss, line, '\n');){ + values.push_back(parse_seed(console, line)); + } + return values; +} + +int16_t seed_position_in_list(uint16_t seed, std::vector list){ + for (size_t i=0; i get_search_results( + ConsoleHandle& console, + AdvRngSearcher& searcher, + AdvRngFilters& filters, + const std::vector& SEED_VALUES, + const uint64_t& ADVANCES, + const uint64_t& advances_radius, + int16_t gender_threshold, + uint16_t tid_xor_sid + +){ + std::map search_hits; + for (int i=0; i<4; i++){ + uint64_t adv_radius = advances_radius * (uint64_t(1) << i); + uint64_t min_adv = ADVANCES - std::min(uint64_t(ADVANCES), adv_radius); + uint64_t max_adv = ADVANCES + adv_radius; + search_hits = searcher.search(filters, SEED_VALUES, min_adv, max_adv, gender_threshold, tid_xor_sid); + if (search_hits.size() > 0){ + console.log("Number of search hits: " + std::to_string(search_hits.size())); + return search_hits; + } + } + console.log("Number of search hits: " + std::to_string(search_hits.size())); + return search_hits; +} + +bool range_is_valid(IvRange iv){ + return ( + iv.low <= iv.high && + iv.low >= 0 && + iv.high >= 0 + ); +} + +bool ranges_are_valid(IvRanges ivs){ + return ( + range_is_valid(ivs.hp) && + range_is_valid(ivs.attack) && + range_is_valid(ivs.defense) && + range_is_valid(ivs.spatk) && + range_is_valid(ivs.spdef) && + range_is_valid(ivs.speed) + ); +} + +void update_filters( + AdvRngFilters& filters, + AdvObservedPokemon& pokemon, + const StatReads& stats, + const EVs& evyield, + const BaseStats& BASE_STATS +){ + level_up_observed_pokemon(pokemon, stats, evyield); + + while (true){ + // in the worst case (the new stats are the problem), start over + if (pokemon.level.size() == 0){ + filters.ivs = { {0,31}, {0,31}, {0,31}, {0,31}, {0,31}, {0,31} }; + return; + } + + AdvRngFilters new_filters = observation_to_filters(pokemon, BASE_STATS); + + if (!ranges_are_valid(new_filters.ivs)){ + IvRanges new_stat_ivs = calc_iv_ranges(BASE_STATS, pokemon.level.back(), pokemon.evs.back(), pokemon.stats.back(), nature_to_adjustment(pokemon.nature)); + if (!ranges_are_valid(new_stat_ivs)){ + // remove newest stats first if they aren't valid + pokemon.level.erase(pokemon.level.begin()); + pokemon.stats.erase(pokemon.stats.begin()); + pokemon.evs.erase(pokemon.evs.begin()); + }else{ + // remove oldest stats first + pokemon.level.pop_back(); + pokemon.stats.pop_back(); + pokemon.evs.pop_back(); + } + continue; + } + + filters.ivs = new_filters.ivs; + return; + } + +} + +double get_seed_calibration_frames( + const RngCalibrationHistory& HISTORY, + const std::vector& SEED_VALUES, + const int16_t& SEED_POSITION +){ + double sum = 0; + uint16_t len = 0; + for (size_t i=0; i& search_hits, + uint32_t max_advance_possibilities, + uint32_t advance_radius, + bool force_finish +){ + if (search_hits.size() == 0){ + console.log("No matches found."); + return true; + } + + if (!force_finish && search_hits.size() > max_advance_possibilities){ + return false; + } + + if (search_hits.size() == 1){ + console.log("Updating calibrations..."); + CALIBRATION_HISTORY.seed_calibrations.emplace_back(SEED_CALIBRATION_FRAMES); + CALIBRATION_HISTORY.advance_calibrations.emplace_back(ADVANCES_CALIBRATION); + CALIBRATION_HISTORY.continue_screen_adjustments.emplace_back(CONTINUE_SCREEN_ADJUSTMENT); + CALIBRATION_HISTORY.results.emplace_back(search_hits.begin()->first); + if (CALIBRATION_HISTORY.results.size() > MAX_HISTORY_LENGTH){ + CALIBRATION_HISTORY.seed_calibrations.erase(CALIBRATION_HISTORY.seed_calibrations.begin()); + CALIBRATION_HISTORY.advance_calibrations.erase(CALIBRATION_HISTORY.advance_calibrations.begin()); + CALIBRATION_HISTORY.continue_screen_adjustments.erase(CALIBRATION_HISTORY.continue_screen_adjustments.begin()); + CALIBRATION_HISTORY.results.erase(CALIBRATION_HISTORY.results.begin()); + } + ADVANCE_HISTORY.results.clear(); + ADVANCE_HISTORY.seed_calibrations.clear(); + return true; + } + + std::vector advances; + std::vector hits; + for(std::map::const_iterator it=search_hits.begin(); it!=search_hits.end(); ++it) { + advances.emplace_back(it->first.advance); + hits.emplace_back(it->first); + } + + // get unique advances + std::sort(advances.begin(), advances.end()); + std::vector::iterator iter; + iter = std::unique(advances.begin(), advances.begin() + advances.size()); + advances.resize(std::distance(advances.begin(), iter)); + + ADVANCE_HISTORY.seed_calibrations.emplace_back(SEED_CALIBRATION_FRAMES); + ADVANCE_HISTORY.results.emplace_back(hits); + + // check advance history for repeated values + std::vector counts; + uint64_t best = 0; + uint64_t mode = 0; + bool tie = false; + for (uint64_t& adv : advances){ + uint64_t count = 0; + for (auto& res : ADVANCE_HISTORY.results){ + for (auto& state : res){ + if (std::abs(int64_t(state.advance) - int64_t(adv)) <= advance_radius){ + count++; + break; // only count one possible hit from each attempt + } + } + } + if (count > best){ + mode = adv; + best = count; + tie = false; + }else if (count == best){ + tie = true; + } + } + + if (tie){ + console.log("More than 1 possible advances value hit."); + return true; + } + + + // add the closest possibility to the advances mode for each attempt to the calibration history + console.log("Inferred hits from previous " + std::to_string(ADVANCE_HISTORY.results.size()) + " attempts: "); + for (size_t i=0; i MAX_HISTORY_LENGTH){ + CALIBRATION_HISTORY.seed_calibrations.erase(CALIBRATION_HISTORY.seed_calibrations.begin()); + CALIBRATION_HISTORY.advance_calibrations.erase(CALIBRATION_HISTORY.advance_calibrations.begin()); + CALIBRATION_HISTORY.continue_screen_adjustments.erase(CALIBRATION_HISTORY.continue_screen_adjustments.begin()); + CALIBRATION_HISTORY.results.erase(CALIBRATION_HISTORY.results.begin()); + } + + ADVANCE_HISTORY.results.clear(); + ADVANCE_HISTORY.seed_calibrations.clear(); + + return true; +} + + + + +} +} +} diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_RngCalibration.h b/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_RngCalibration.h new file mode 100644 index 0000000000..51558b40e8 --- /dev/null +++ b/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_RngCalibration.h @@ -0,0 +1,93 @@ +/* RNG Calibration + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#ifndef PokemonAutomation_PokemonFRLG_RngCalibration_H +#define PokemonAutomation_PokemonFRLG_RngCalibration_H + +#include +#include +#include "NintendoSwitch/NintendoSwitch_ConsoleHandle.h" +#include "Pokemon/Pokemon_AdvRng.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonFRLG{ + +using namespace Pokemon; + + +struct RngAdvanceHistory{ + std::vector seed_calibrations; + std::vector> results; +}; + +struct RngCalibrationHistory{ + std::vector seed_calibrations; + std::vector advance_calibrations; + std::vector continue_screen_adjustments; + std::vector results; +}; + + +void check_seed_validity(ConsoleHandle& console, std::string seed_string); +uint16_t parse_seed(ConsoleHandle& console, std::string seed_string); +std::vector parse_seed_list(ConsoleHandle& console, std::string seed_list_string); +int16_t seed_position_in_list(uint16_t seed, std::vector list); + +Pokemon::AdvNature string_to_nature(std::string nature_string); + +// get search hits for any of the provided seed values and advances range +std::map get_search_results( + ConsoleHandle& console, + AdvRngSearcher& searcher, + AdvRngFilters& filters, + const std::vector& SEED_VALUES, + const uint64_t& ADVANCES, + const uint64_t& advances_radius, + int16_t gender_threshold = 126, + uint16_t tid_xor_sid = 0 +); + +// update IV ranges for RNG search filters with new stats/EVs after leveling up +void update_filters( + AdvRngFilters& filters, + AdvObservedPokemon& pokemon, + const StatReads& stats, + const EVs& evyield, + const BaseStats& BASE_STATS +); + +// get seed calibration based on average offset in the RNG calibration history +double get_seed_calibration_frames( + const RngCalibrationHistory& HISTORY, + const std::vector& SEED_VALUES, + const int16_t& SEED_POSITION +); + +// get advances calibration based on average offset in the RNG calibration history +double get_advances_calibration_frames(const RngCalibrationHistory& CALIBRATION_HISTORY, const uint64_t& ADVANCES); + +// infer hit seeds/advances, update the calibration history, and return whether or not the search is finished +bool update_history( + ConsoleHandle& console, + RngAdvanceHistory& ADVANCE_HISTORY, + RngCalibrationHistory& CALIBRATION_HISTORY, + const uint16_t& MAX_HISTORY_LENGTH, + const double& SEED_CALIBRATION_FRAMES, + const double& ADVANCES_CALIBRATION, + const double& CONTINUE_SCREEN_ADJUSTMENT, + const std::map& search_hits, + uint32_t max_advance_possibilities = 1, + uint32_t advance_radius = 2, + bool force_finish = false +); + + + +} +} +} +#endif diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_RngDisplays.cpp b/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_RngDisplays.cpp index 64bc3f4ff8..dc747105b0 100644 --- a/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_RngDisplays.cpp +++ b/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_RngDisplays.cpp @@ -155,14 +155,20 @@ void RngFilterDisplay::reset(){ nature.set("-"); } -PossibleHitsDisplay::PossibleHitsDisplay() - : GroupOption("Possible Hits", LockMode::READ_ONLY) +RngCalibrationDisplay::RngCalibrationDisplay() + : GroupOption("RNG Calibration", LockMode::READ_ONLY) + , seed_calibration("Seed Calibration (ms):", LockMode::LOCK_WHILE_RUNNING, 0.0) + , csf_calibration("Continue Screen Frames Calibration:", LockMode::LOCK_WHILE_RUNNING, 0.0) + , advances_calibration("In-Game Advances Calibration:", LockMode::LOCK_WHILE_RUNNING, 0.0) , hits(false, "Seeds/Advances:", LockMode::READ_ONLY, "-", "") { + PA_ADD_STATIC(seed_calibration); + PA_ADD_STATIC(csf_calibration); + PA_ADD_STATIC(advances_calibration); PA_ADD_STATIC(hits); } -std::vector PossibleHitsDisplay::get_rng_states_from_map(const std::map& hits_map){ +std::vector RngCalibrationDisplay::get_rng_states_from_map(const std::map& hits_map){ std::vector rng_states; for(std::map::const_iterator it = hits_map.begin(); it != hits_map.end(); ++it) { rng_states.emplace_back(it->first); @@ -170,7 +176,7 @@ std::vector PossibleHitsDisplay::get_rng_states_from_map(const std: return rng_states; } -std::string PossibleHitsDisplay::get_hits_string(const std::vector& rng_states){ +std::string RngCalibrationDisplay::get_hits_string(const std::vector& rng_states){ std::string hits_string; for (size_t i=0; i 0){ @@ -189,19 +195,35 @@ std::string PossibleHitsDisplay::get_hits_string(const std::vector& } return hits_string; } -std::string PossibleHitsDisplay::get_hits_string(const std::map& hits_map){ +std::string RngCalibrationDisplay::get_hits_string(const std::map& hits_map){ return get_hits_string(get_rng_states_from_map(hits_map)); } -void PossibleHitsDisplay::set(const std::vector& rng_states){ +void RngCalibrationDisplay::set( + double s_calibration, + double c_calibration, + double a_calibration, + std::vector& rng_states +){ + seed_calibration.set(s_calibration); + csf_calibration.set(c_calibration); + advances_calibration.set(a_calibration); hits.set(get_hits_string(rng_states)); } -void PossibleHitsDisplay::set(const std::map& hits_map){ +void RngCalibrationDisplay::set( + double s_calibration, + double c_calibration, + double a_calibration, + const std::map& hits_map +){ std::vector rng_states = get_rng_states_from_map(hits_map); - set(rng_states); + set(s_calibration, c_calibration, a_calibration, rng_states); } -void PossibleHitsDisplay::reset(){ +void RngCalibrationDisplay::reset(){ + // seed_calibration.set(0.0); + // csf_calibration.set(0.0); + // advances_calibration.set(0.0); hits.set("-"); } diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_RngDisplays.h b/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_RngDisplays.h index 103d99143e..804dbd027d 100644 --- a/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_RngDisplays.h +++ b/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_RngDisplays.h @@ -10,6 +10,7 @@ #include #include "Common/Cpp/Options/StringOption.h" #include "Common/Cpp/Options/TextEditOption.h" +#include "Common/Cpp/Options/FloatingPointOption.h" #include "CommonFramework/Notifications/EventNotificationsTable.h" #include "NintendoSwitch/NintendoSwitch_SingleSwitchProgram.h" #include "Pokemon/Pokemon_AdvRng.h" @@ -59,12 +60,22 @@ class RngFilterDisplay : public GroupOption{ }; -class PossibleHitsDisplay : public GroupOption{ +class RngCalibrationDisplay : public GroupOption{ public: - PossibleHitsDisplay(); - - void set(const std::vector& rng_states); - void set(const std::map& hits_map); + RngCalibrationDisplay(); + + void set( + double s_calibraiton, + double c_calibration, + double a_calibration, + std::vector& rng_states + ); + void set( + double s_calibration, + double c_calibration, + double a_calibration, + const std::map& hits_map + ); void reset(); private: @@ -72,6 +83,9 @@ class PossibleHitsDisplay : public GroupOption{ static std::string get_hits_string(const std::vector& rng_states); static std::string get_hits_string(const std::map& hits_map); public: + FloatingPointOption seed_calibration; + FloatingPointOption csf_calibration; + FloatingPointOption advances_calibration; StringOption hits; }; diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_SidHelper.cpp b/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_SidHelper.cpp index 8e7add03b7..b48f164fb7 100644 --- a/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_SidHelper.cpp +++ b/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_SidHelper.cpp @@ -151,9 +151,8 @@ void finish_intro_animations(SingleSwitchProgramEnvironment& env, ProControllerC ); if (ret2 < 0){ - OperationFailedException::fire( - ErrorReport::SEND_ERROR_REPORT, "SidHelper(): black screen lasted longer than 10 seconds", env.console - ); + env.log("finish_intro_animations(): end of black screen not detected within 10 seconds. Continuing anyway..."); + // if there's a problem, an error will be thrown when trying to navigate to the trainer card } pbf_wait(context, 2000ms); diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_StarterRng.cpp b/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_StarterRng.cpp index c3f43d10bc..3a5e14ea06 100644 --- a/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_StarterRng.cpp +++ b/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_StarterRng.cpp @@ -18,19 +18,15 @@ #include "CommonTools/StartupChecks/StartProgramChecks.h" #include "Pokemon/Pokemon_Strings.h" #include "Pokemon/Pokemon_StatsCalculation.h" -#include "Pokemon/Pokemon_AdvRng.h" #include "NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h" #include "NintendoSwitch/Commands/NintendoSwitch_Commands_Superscalar.h" #include "NintendoSwitch/NintendoSwitch_Settings.h" -#include "NintendoSwitch/Inference/NintendoSwitch_HomeMenuDetector.h" -#include "NintendoSwitch/Inference/NintendoSwitch_UpdatePopupDetector.h" #include "NintendoSwitch/Programs/NintendoSwitch_GameEntry.h" #include "PokemonFRLG/Inference/Dialogs/PokemonFRLG_BattleDialogs.h" #include "PokemonFRLG/Inference/Dialogs/PokemonFRLG_DialogDetector.h" #include "PokemonFRLG/Inference/Menus/PokemonFRLG_SummaryDetector.h" #include "PokemonFRLG/Inference/PokemonFRLG_BattlePokemonDetector.h" #include "PokemonFRLG/Inference/PokemonFRLG_BattleLevelUpReader.h" -#include "PokemonFRLG/Inference/PokemonFRLG_ShinySymbolDetector.h" #include "PokemonFRLG/Inference/PokemonFRLG_StatsReader.h" #include "PokemonFRLG/PokemonFRLG_Navigation.h" #include "PokemonFRLG_BlindNavigation.h" @@ -190,7 +186,7 @@ StarterRng::StarterRng() }) { PA_ADD_OPTION(RNG_FILTERS); - PA_ADD_OPTION(POSSIBLE_HITS); + PA_ADD_OPTION(RNG_CALIBRATION); PA_ADD_OPTION(LANGUAGE); PA_ADD_OPTION(STARTER); PA_ADD_OPTION(MAX_RESETS); @@ -208,104 +204,6 @@ StarterRng::StarterRng() } -namespace { - -void check_seed_validity(SingleSwitchProgramEnvironment& env, std::string seed_string){ - static const std::map MAP{ - {'1', '1'}, - {'2', '2'}, - {'3', '3'}, - {'4', '4'}, - {'5', '5'}, - {'6', '6'}, - {'7', '7'}, - {'8', '8'}, - {'9', '9'}, - {'0', '0'}, - {'A', 'A'}, {'a', 'A'}, - {'B', 'B'}, {'b', 'B'}, - {'C', 'C'}, {'c', 'C'}, - {'D', 'D'}, {'d', 'D'}, - {'E', 'E'}, {'e', 'E'}, - {'F', 'F'}, {'f', 'F'} - }; - - if (seed_string.size() != 4){ - OperationFailedException::fire( - ErrorReport::SEND_ERROR_REPORT, - "StarterRng(): Invalid seed length. Seeds should be 4 characters.", - env.console - ); - } - - for (char ch : seed_string){ - auto iter = MAP.find(ch); - if (iter == MAP.end()){ - OperationFailedException::fire( - ErrorReport::SEND_ERROR_REPORT, - "StarterRng(): Invalid seed character. Seeds should be hex strings (valid characters are 0-9 and A-F).", - env.console - ); - } - } -} - -uint16_t parse_seed(SingleSwitchProgramEnvironment& env, std::string seed_string){ - check_seed_validity(env, seed_string); - std::istringstream converter(seed_string); - uint16_t value; - converter >> std::hex >> value; - return value; -} - -std::vector parse_seed_list(SingleSwitchProgramEnvironment& env, std::string seed_list_string){ - std::vector values; - auto ss = std::stringstream{seed_list_string}; - for (std::string line; std::getline(ss, line, '\n');){ - values.push_back(parse_seed(env, line)); - } - return values; -} - -int16_t seed_position_in_list(uint16_t seed, std::vector list){ - for (size_t i=0; i StarterRng::get_starter_search_results( - SingleSwitchProgramEnvironment& env, - AdvRngSearcher& searcher, - AdvRngFilters& filters, - const std::vector& SEED_VALUES, - const uint64_t& ADVANCES, - const uint64_t& advances_radius, - const AdvObservedPokemon& pokemon -){ - std::map search_hits; - for (int i=0; i<4; i++){ - uint64_t adv_radius = advances_radius * (uint64_t(1) << i); - uint64_t min_adv = ADVANCES - std::min(uint64_t(ADVANCES), adv_radius); - uint64_t max_adv = ADVANCES + adv_radius; - search_hits = searcher.search(filters, SEED_VALUES, min_adv, max_adv, 0, 30); - if (search_hits.size() > 0){ - env.log("Number of search hits: " + std::to_string(search_hits.size())); - POSSIBLE_HITS.set(search_hits); - return search_hits; - } - } - env.log("Number of search hits: " + std::to_string(search_hits.size())); - POSSIBLE_HITS.set(search_hits); - return search_hits; -} - -double StarterRng::get_seed_calibration_frames( - const StarterRngCalibrationHistory& HISTORY, - const std::vector& SEED_VALUES, - const int16_t& SEED_POSITION -){ - double sum = 0; - uint16_t len = 0; - for (size_t i=0; i& search_hits, - bool force_finish -){ - const int MAX_ADVANCE_POSSIBILITIES = 5; - const uint32_t ADVANCE_RADIUS = 2; - - if (search_hits.size() == 0){ - env.log("No matches found."); - return true; - } - - if (!force_finish && search_hits.size() > MAX_ADVANCE_POSSIBILITIES){ - return false; - } - - if (search_hits.size() == 1){ - env.log("Updating calibrations..."); - CALIBRATION_HISTORY.seed_calibrations.emplace_back(SEED_CALIBRATION_FRAMES); - CALIBRATION_HISTORY.advance_calibrations.emplace_back(ADVANCES_CALIBRATION); - CALIBRATION_HISTORY.continue_screen_adjustments.emplace_back(CONTINUE_SCREEN_ADJUSTMENT); - CALIBRATION_HISTORY.results.emplace_back(search_hits.begin()->first); - if (CALIBRATION_HISTORY.results.size() > MAX_HISTORY_LENGTH){ - CALIBRATION_HISTORY.seed_calibrations.erase(CALIBRATION_HISTORY.seed_calibrations.begin()); - CALIBRATION_HISTORY.advance_calibrations.erase(CALIBRATION_HISTORY.advance_calibrations.begin()); - CALIBRATION_HISTORY.continue_screen_adjustments.erase(CALIBRATION_HISTORY.continue_screen_adjustments.begin()); - CALIBRATION_HISTORY.results.erase(CALIBRATION_HISTORY.results.begin()); - } - ADVANCE_HISTORY.results.clear(); - ADVANCE_HISTORY.seed_calibrations.clear(); - return true; - } - - std::vector advances; - std::vector hits; - for(std::map::const_iterator it=search_hits.begin(); it!=search_hits.end(); ++it) { - advances.emplace_back(it->first.advance); - hits.emplace_back(it->first); - } - - // get unique advances - std::sort(advances.begin(), advances.end()); - std::vector::iterator iter; - iter = std::unique(advances.begin(), advances.begin() + advances.size()); - advances.resize(std::distance(advances.begin(), iter)); - - ADVANCE_HISTORY.seed_calibrations.emplace_back(SEED_CALIBRATION_FRAMES); - ADVANCE_HISTORY.results.emplace_back(hits); - - // check advance history for repeated values - std::vector counts; - uint64_t best = 0; - uint64_t mode = 0; - bool tie = false; - for (uint64_t& adv : advances){ - uint64_t count = 0; - for (auto& res : ADVANCE_HISTORY.results){ - for (auto& state : res){ - if (std::abs(int64_t(state.advance) - int64_t(adv)) <= ADVANCE_RADIUS){ - count++; - break; // only count one possible hit from each attempt - } - } - } - if (count > best){ - mode = adv; - best = count; - tie = false; - }else if (count == best){ - tie = true; - } - } - - if (tie){ - env.log("More than 1 possible advances value hit."); - return true; - } - - - // add the closest possibility to the advances mode for each attempt to the calibration history - env.log("Inferred hits from previous " + std::to_string(ADVANCE_HISTORY.results.size()) + " attempts: "); - for (size_t i=0; i MAX_HISTORY_LENGTH){ - CALIBRATION_HISTORY.seed_calibrations.erase(CALIBRATION_HISTORY.seed_calibrations.begin()); - CALIBRATION_HISTORY.advance_calibrations.erase(CALIBRATION_HISTORY.advance_calibrations.begin()); - CALIBRATION_HISTORY.continue_screen_adjustments.erase(CALIBRATION_HISTORY.continue_screen_adjustments.begin()); - CALIBRATION_HISTORY.results.erase(CALIBRATION_HISTORY.results.begin()); - } - - ADVANCE_HISTORY.results.clear(); - ADVANCE_HISTORY.seed_calibrations.clear(); - - return true; -} - - - bool StarterRng::walk_to_rival_battle(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ // return to the overworld pbf_mash_button(context, BUTTON_B, 5000ms); @@ -639,7 +320,14 @@ bool StarterRng::walk_to_rival_battle(SingleSwitchProgramEnvironment& env, ProCo { black_screen } ); - return (ret < 0); + bool failed = ret < 0; + if (failed){ + send_program_recoverable_error_notification( + env, NOTIFICATION_ERROR_RECOVERABLE, + "walk_to_rival_battle(): failed to initiate battle." + ); + } + return failed; } bool StarterRng::auto_battle_rival( @@ -672,7 +360,10 @@ bool StarterRng::auto_battle_rival( { battle_ready } ); if (ret1 < 0){ - env.log("auto_battle_rival(): failed to detect the battle menu."); + send_program_recoverable_error_notification( + env, NOTIFICATION_ERROR_RECOVERABLE, + "auto_battle_rival(): failed to detect the battle menu." + ); return true; } env.log("Battle started. Using first move..."); @@ -689,7 +380,10 @@ bool StarterRng::auto_battle_rival( { battle_ready } ); if (ret2 < 0){ - env.log("auto_battle_rival(): failed to detect the battle menu."); + send_program_recoverable_error_notification( + env, NOTIFICATION_ERROR_RECOVERABLE, + "auto_battle_rival(): failed to detect the battle menu." + ); return true; } env.log("Oak tutorial dialogue finished. Mashing A..."); @@ -715,7 +409,10 @@ bool StarterRng::auto_battle_rival( pbf_mash_button(context, BUTTON_B, 20s); // exit battle and dialogue return false; default: - env.log("auto_battle_rival(): no fainting detected with 5 minutes."); + send_program_recoverable_error_notification( + env, NOTIFICATION_ERROR_RECOVERABLE, + "auto_battle_rival(): no fainting detected with 5 minutes." + ); return true; } @@ -755,6 +452,7 @@ bool StarterRng::auto_battle_rival( StatReads stats = reader.read_stats(env.logger(), screen); update_filters(filters, pokemon, stats, evyield, BASE_STATS); + RNG_FILTERS.set(filters); // exit battle pbf_mash_button(context, BUTTON_B, 20s); @@ -779,7 +477,10 @@ bool StarterRng::walk_to_route1_from_lab(SingleSwitchProgramEnvironment& env, Pr ); if (ret < 0){ - env.log("walk_to_route1_from_lab(): failed to exit lab."); + send_program_recoverable_error_notification( + env, NOTIFICATION_ERROR_RECOVERABLE, + "walk_to_route1_from_lab(): failed to exit lab." + ); return true; } @@ -809,7 +510,10 @@ bool StarterRng::walk_to_route1_from_home(SingleSwitchProgramEnvironment& env, P ); if (ret < 0){ - env.log("walk_to_route1_from_home(): failed to exit the house."); + send_program_recoverable_error_notification( + env, NOTIFICATION_ERROR_RECOVERABLE, + "walk_to_route1_from_home(): failed to exit the house." + ); return true; } @@ -842,7 +546,10 @@ int StarterRng::autolevel_on_route1( env.log("Triggering wild encounters..."); int ret = grass_spin(env.console, context, leftright); if (ret < 0){ - env.log("autolevel_on_route1(): failed to trigger encounter."); + send_program_recoverable_error_notification( + env, NOTIFICATION_ERROR_RECOVERABLE, + "autolevel_on_route1(): failed to trigger encounter." + ); return -1; } @@ -883,6 +590,7 @@ int StarterRng::autolevel_on_route1( screen = env.console.video().snapshot(); stats = reader.read_stats(env.logger(), screen); update_filters(filters, pokemon, stats, evyield, BASE_STATS); + RNG_FILTERS.set(filters); exit_wild_battle(env.console, context, false, true); return 0; case -1: @@ -920,10 +628,10 @@ void StarterRng::program(SingleSwitchProgramEnvironment& env, ProControllerConte home_black_border_check(env.console, context); RNG_FILTERS.reset(); - POSSIBLE_HITS.reset(); + RNG_CALIBRATION.reset(); - const uint16_t TARGET_SEED = parse_seed(env, SEED); - const std::vector SEED_VALUES = parse_seed_list(env, SEED_LIST); + const uint16_t TARGET_SEED = parse_seed(env.console, SEED); + const std::vector SEED_VALUES = parse_seed_list(env.console, SEED_LIST); const int16_t SEED_POSITION = seed_position_in_list(TARGET_SEED, SEED_VALUES); if (SEED_POSITION == -1){ @@ -951,6 +659,8 @@ void StarterRng::program(SingleSwitchProgramEnvironment& env, ProControllerConte break; } + const int16_t GENDER_THRESHOLD = 30; + const double FRAMERATE = 59.999977; // FPS const double FRAME_DURATION = 1000 / FRAMERATE; @@ -960,9 +670,9 @@ void StarterRng::program(SingleSwitchProgramEnvironment& env, ProControllerConte uint64_t CONTINUE_SCREEN_FRAMES = 200; const int64_t FIXED_SEED_OFFSET = -845; // milliseconds. approximate; - double SEED_CALIBRATION_FRAMES = 0; - double ADVANCES_CALIBRATION = 0; - double CONTINUE_SCREEN_ADJUSTMENT = 0; + double SEED_CALIBRATION_FRAMES = RNG_CALIBRATION.seed_calibration / FRAME_DURATION; + double ADVANCES_CALIBRATION = RNG_CALIBRATION.advances_calibration; + double CONTINUE_SCREEN_ADJUSTMENT = RNG_CALIBRATION.csf_calibration; AdvRngSearcher searcher(TARGET_SEED, ADVANCES, AdvRngMethod::Method1); AdvPokemonResult target_result = searcher.generate_pokemon(); @@ -974,8 +684,8 @@ void StarterRng::program(SingleSwitchProgramEnvironment& env, ProControllerConte env.log("SpD: " + std::to_string(target_result.ivs.spdef)); env.log("Spe: " + std::to_string(target_result.ivs.speed)); - StarterRngAdvanceHistory ADVANCE_HISTORY; - StarterRngCalibrationHistory CALIBRATION_HISTORY; + RngAdvanceHistory ADVANCE_HISTORY; + RngCalibrationHistory CALIBRATION_HISTORY; uint64_t INITIAL_ADVANCES_RADIUS = 1024; uint64_t resets = 0; bool wildshiny_found = false; @@ -1016,9 +726,11 @@ void StarterRng::program(SingleSwitchProgramEnvironment& env, ProControllerConte } env.log("Advances search radius: " + std::to_string(advances_radius)); - SEED_CALIBRATION_FRAMES = get_seed_calibration_frames(CALIBRATION_HISTORY, SEED_VALUES, SEED_POSITION); - ADVANCES_CALIBRATION = get_advances_calibration_frames(CALIBRATION_HISTORY, ADVANCES); - + if (CALIBRATION_HISTORY.results.size() > 0){ + SEED_CALIBRATION_FRAMES = get_seed_calibration_frames(CALIBRATION_HISTORY, SEED_VALUES, SEED_POSITION); + ADVANCES_CALIBRATION = get_advances_calibration_frames(CALIBRATION_HISTORY, ADVANCES); + } + if (CALIBRATION_HISTORY.results.size() > 0){ AdvRngState prev_hit = CALIBRATION_HISTORY.results.back(); double prev_csf_calibration = CALIBRATION_HISTORY.continue_screen_adjustments.back(); @@ -1030,6 +742,7 @@ void StarterRng::program(SingleSwitchProgramEnvironment& env, ProControllerConte }else{ CONTINUE_SCREEN_ADJUSTMENT = prev_csf_calibration + 0.5; } + CONTINUE_SCREEN_ADJUSTMENT = fmod(CONTINUE_SCREEN_ADJUSTMENT, 2); }else{ // we're still not that close. Slightly vary the seed to more reliably hone in on advances double seed_bump = SEED_BUMPS[ADVANCE_HISTORY.results.size() % 5]; @@ -1065,7 +778,7 @@ void StarterRng::program(SingleSwitchProgramEnvironment& env, ProControllerConte stats.resets++; RNG_FILTERS.reset(); - POSSIBLE_HITS.reset(); + RNG_CALIBRATION.reset(); bool shiny_found = check_for_shiny(env.console, context, PokemonFRLG_RngTarget::starters); @@ -1091,8 +804,14 @@ void StarterRng::program(SingleSwitchProgramEnvironment& env, ProControllerConte AdvRngFilters filters = observation_to_filters(pokemon, BASE_STATS); RNG_FILTERS.set(filters); - std::map search_hits = get_starter_search_results(env, searcher, filters, SEED_VALUES, ADVANCES, advances_radius, pokemon); - bool finished = update_history(env, ADVANCE_HISTORY, CALIBRATION_HISTORY, MAX_HISTORY_LENGTH, SEED_CALIBRATION_FRAMES, ADVANCES_CALIBRATION, CONTINUE_SCREEN_ADJUSTMENT, search_hits); + std::map search_hits = get_search_results(env.console, searcher, filters, SEED_VALUES, ADVANCES, advances_radius, GENDER_THRESHOLD); + RNG_CALIBRATION.set( + SEED_CALIBRATION_FRAMES * FRAME_DURATION, + CONTINUE_SCREEN_ADJUSTMENT, + ADVANCES_CALIBRATION - CONTINUE_SCREEN_ADJUSTMENT, + search_hits + ); + bool finished = update_history(env.console, ADVANCE_HISTORY, CALIBRATION_HISTORY, MAX_HISTORY_LENGTH, SEED_CALIBRATION_FRAMES, ADVANCES_CALIBRATION, CONTINUE_SCREEN_ADJUSTMENT, search_hits, 1); if (finished){ env.log("RNG search finished."); continue; @@ -1101,7 +820,6 @@ void StarterRng::program(SingleSwitchProgramEnvironment& env, ProControllerConte bool failed = walk_to_rival_battle(env, context); if (failed){ stats.errors++; - env.log("Failed to initiate rival battle."); continue; // reset game } @@ -1111,10 +829,10 @@ void StarterRng::program(SingleSwitchProgramEnvironment& env, ProControllerConte continue; // reset game } if (pokemon.level.size() > 1){ - searcher.refine_search(search_hits, filters, 0, 30); - POSSIBLE_HITS.set(search_hits); + search_hits = get_search_results(env.console, searcher, filters, SEED_VALUES, ADVANCES, advances_radius, GENDER_THRESHOLD); + RNG_CALIBRATION.set(SEED_CALIBRATION_FRAMES, CONTINUE_SCREEN_ADJUSTMENT, ADVANCES_CALIBRATION, search_hits); env.log("Number of search hits: " + std::to_string(search_hits.size())); - finished = update_history(env, ADVANCE_HISTORY, CALIBRATION_HISTORY, MAX_HISTORY_LENGTH, SEED_CALIBRATION_FRAMES, ADVANCES_CALIBRATION, CONTINUE_SCREEN_ADJUSTMENT, search_hits); + finished = update_history(env.console, ADVANCE_HISTORY, CALIBRATION_HISTORY, MAX_HISTORY_LENGTH, SEED_CALIBRATION_FRAMES, ADVANCES_CALIBRATION, CONTINUE_SCREEN_ADJUSTMENT, search_hits, 5); if (finished){ env.log("RNG search finished."); continue; @@ -1132,7 +850,7 @@ void StarterRng::program(SingleSwitchProgramEnvironment& env, ProControllerConte while(true){ if (num_levels > MAX_LEVELS){ env.log("RNG search not complete after 3 level-ups."); - update_history(env, ADVANCE_HISTORY, CALIBRATION_HISTORY, MAX_HISTORY_LENGTH, SEED_CALIBRATION_FRAMES, ADVANCES_CALIBRATION, CONTINUE_SCREEN_ADJUSTMENT, search_hits, true); + update_history(env.console, ADVANCE_HISTORY, CALIBRATION_HISTORY, MAX_HISTORY_LENGTH, SEED_CALIBRATION_FRAMES, ADVANCES_CALIBRATION, CONTINUE_SCREEN_ADJUSTMENT, search_hits, true); break; } @@ -1141,7 +859,6 @@ void StarterRng::program(SingleSwitchProgramEnvironment& env, ProControllerConte int ret2 = autolevel_on_route1(env, context, pokemon, filters, BASE_STATS); if (ret2 < 0){ - env.log("Error encountered while auto-leveling."); stats.errors++; break; }else if(ret2 == 1){ @@ -1167,10 +884,15 @@ void StarterRng::program(SingleSwitchProgramEnvironment& env, ProControllerConte if (pokemon.level.size() > num_levels){ num_levels = pokemon.level.size(); - searcher.refine_search(search_hits, filters, 0, 30); - POSSIBLE_HITS.set(search_hits); + search_hits = get_search_results(env.console, searcher, filters, SEED_VALUES, ADVANCES, advances_radius, GENDER_THRESHOLD); + RNG_CALIBRATION.set( + SEED_CALIBRATION_FRAMES * FRAME_DURATION, + CONTINUE_SCREEN_ADJUSTMENT, + ADVANCES_CALIBRATION - CONTINUE_SCREEN_ADJUSTMENT, + search_hits + ); env.log("Number of search hits: " + std::to_string(search_hits.size())); - finished = update_history(env, ADVANCE_HISTORY, CALIBRATION_HISTORY, MAX_HISTORY_LENGTH, SEED_CALIBRATION_FRAMES, ADVANCES_CALIBRATION, CONTINUE_SCREEN_ADJUSTMENT, search_hits); + finished = update_history(env.console, ADVANCE_HISTORY, CALIBRATION_HISTORY, MAX_HISTORY_LENGTH, SEED_CALIBRATION_FRAMES, ADVANCES_CALIBRATION, CONTINUE_SCREEN_ADJUSTMENT, search_hits, 5); if (finished){ env.log("RNG search finished."); break; diff --git a/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_StarterRng.h b/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_StarterRng.h index 679edba274..470e9d1270 100644 --- a/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_StarterRng.h +++ b/SerialPrograms/Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_StarterRng.h @@ -17,6 +17,7 @@ #include "NintendoSwitch/Options/NintendoSwitch_GoHomeWhenDoneOption.h" #include "Pokemon/Pokemon_StatsCalculation.h" #include "Pokemon/Pokemon_AdvRng.h" +#include "PokemonFRLG_RngCalibration.h" #include "PokemonFRLG_RngDisplays.h" namespace PokemonAutomation{ @@ -46,61 +47,10 @@ class StarterRng : public SingleSwitchProgramInstance{ charmander }; - struct StarterRngAdvanceHistory{ - std::vector seed_calibrations; - std::vector> results; - }; - - struct StarterRngCalibrationHistory{ - std::vector seed_calibrations; - std::vector advance_calibrations; - std::vector continue_screen_adjustments; - std::vector results; - - }; - bool have_hit_target(SingleSwitchProgramEnvironment& env, const uint32_t& TARGET_SEED, const AdvRngState& hit); AdvObservedPokemon read_summary(SingleSwitchProgramEnvironment& env, ProControllerContext& context); - void update_filters( - AdvRngFilters& filters, - AdvObservedPokemon& pokemon, - const StatReads& stats, - const EVs& evyield, - const BaseStats& BASE_STATS - ); - std::map get_starter_search_results( - SingleSwitchProgramEnvironment& env, - AdvRngSearcher& searcher, - AdvRngFilters& filters, - const std::vector& SEED_VALUES, - const uint64_t& ADVANCES, - const uint64_t& advances_radius, - const AdvObservedPokemon& pokemon - ); - double get_seed_calibration_frames( - const StarterRngCalibrationHistory& CALIBRATION_HISTORY, - const std::vector& SEED_VALUES, - const int16_t& SEED_POSITION - ); - double get_advances_calibration_frames( - const StarterRngCalibrationHistory& CALIBRATION_HISTORY, - const uint64_t& ADVANCES - ); - - bool update_history( - SingleSwitchProgramEnvironment& env, - StarterRngAdvanceHistory& ADVANCE_HISTORY, - StarterRngCalibrationHistory& CALIBRATION_HISTORY, - const uint16_t& MAX_HISTORY_LENGTH, - const double& SEED_CALIBRATION_FRAMES, - const double& ADVANCES_CALIBRATION, - const double& CONTINUE_SCREEN_ADJUSTMENT, - const std::map& search_hits, - bool force_finish = false - ); - bool walk_to_rival_battle(SingleSwitchProgramEnvironment& env, ProControllerContext& context); bool auto_battle_rival( SingleSwitchProgramEnvironment& env, @@ -128,7 +78,7 @@ class StarterRng : public SingleSwitchProgramInstance{ SimpleIntegerOption MAX_RESETS; RngFilterDisplay RNG_FILTERS; - PossibleHitsDisplay POSSIBLE_HITS; + RngCalibrationDisplay RNG_CALIBRATION; StringOption SEED; TextEditOption SEED_LIST; @@ -154,6 +104,3 @@ class StarterRng : public SingleSwitchProgramInstance{ } } #endif - - - diff --git a/SerialPrograms/cmake/SourceFiles.cmake b/SerialPrograms/cmake/SourceFiles.cmake index daf8b1f127..7f86b1699f 100644 --- a/SerialPrograms/cmake/SourceFiles.cmake +++ b/SerialPrograms/cmake/SourceFiles.cmake @@ -1535,6 +1535,8 @@ file(GLOB LIBRARY_SOURCES Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_RngDisplays.h Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_HardReset.cpp Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_HardReset.h + Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_RngCalibration.cpp + Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_RngCalibration.h Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_SidHelper.cpp Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_SidHelper.h Source/PokemonFRLG/Programs/RngManipulation/PokemonFRLG_RngHelper.cpp @@ -2829,4 +2831,4 @@ file(GLOB LIBRARY_SOURCES Source/ZeldaTotK/ZeldaTotK_Panels.h Source/ZeldaTotK/ZeldaTotK_Settings.cpp Source/ZeldaTotK/ZeldaTotK_Settings.h -) +) \ No newline at end of file