Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 118 additions & 16 deletions SerialPrograms/Source/Pokemon/Pokemon_AdvRng.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ void level_up_observed_pokemon(AdvObservedPokemon& pokemon, const StatReads& new
pokemon.evs.emplace_back(new_evs);
}

uint32_t increment_internal_rng_state(uint32_t& state){
uint32_t increment_internal_rng_state(uint32_t state){
return state * 0x41c64e6d + 0x6073;
}

AdvRngState rngstate_from_internal_state(uint16_t seed, uint64_t advances, uint32_t& state, AdvRngMethod method){
AdvRngState rngstate_from_internal_state(uint16_t seed, uint64_t advances, uint32_t state, AdvRngMethod method){
uint32_t s0 = state;
uint32_t s1 = increment_internal_rng_state(s0);
uint32_t s2 = increment_internal_rng_state(s1);
Expand Down Expand Up @@ -63,27 +63,40 @@ void advance_rng_state(AdvRngState& state){
state.s5 = increment_internal_rng_state(state.s5);
}

uint32_t pid_from_states(uint32_t& s0, uint32_t& s1){
uint32_t pid_from_states(uint32_t s0, uint32_t s1){
return (s1 & 0xffff0000) + (s0 >> 16);
}

uint8_t gender_value_from_pid(uint32_t& pid){
uint8_t gender_value_from_pid(uint32_t pid){
return pid & 0xff;
}

AdvGender gender_from_gender_value(uint8_t gender_value, int16_t threshold){
if (threshold < -1){ // genderless Pokemon
return AdvGender::Any;
}
return (gender_value <= threshold) ? AdvGender::Female : AdvGender::Male;
}

AdvNature nature_from_pid(uint32_t& pid){
AdvNature nature_from_pid(uint32_t pid){
return AdvNature (pid % 25);
}

AdvAbility ability_from_pid(uint32_t& pid){
AdvAbility ability_from_pid(uint32_t pid){
return AdvAbility (pid % 2);
}

AdvIvGroup iv_group_from_state(uint32_t& state, bool roaming = false){
uint8_t unown_form_from_pid(uint32_t pid){
return (((pid & 0x3000000) >> 18) | ((pid & 0x30000) >> 12) | ((pid & 0x300) >> 6) | (pid & 0x3)) % 0x1c;
// return (
// (((pid >> 24) & 0x03) << 6) |
// (((pid >> 16) & 0x03) << 4) |
// (((pid >> 8) & 0x03) << 2) |
// (pid & 0x03)
// ) % 28;
}

AdvIvGroup iv_group_from_state(uint32_t state, bool roaming = false){
AdvIvGroup ivgroup;
uint32_t remainingbits = state >> 16;
if (roaming){
Expand All @@ -99,7 +112,7 @@ AdvIvGroup iv_group_from_state(uint32_t& state, bool roaming = false){
return ivgroup;
}

AdvPokemonResult pokemon_from_state(AdvRngState& state, uint32_t pid, AdvNature nature, bool roaming = false){
AdvPokemonResult pokemon_from_state(const AdvRngState& state, uint32_t pid, AdvNature nature, bool roaming = false){
uint8_t gender = gender_value_from_pid(pid);
AdvAbility ability = ability_from_pid(pid);

Expand Down Expand Up @@ -139,7 +152,7 @@ AdvPokemonResult pokemon_from_state(AdvRngState& state, uint32_t pid, AdvNature
return {pid, gender, nature, ability, ivs};
}

AdvPokemonResult pokemon_from_state(AdvRngState& state, bool roaming = false){
AdvPokemonResult pokemon_from_state(const AdvRngState& state, bool roaming = false){
uint32_t pid = pid_from_states(state.s0, state.s1);
AdvNature nature = nature_from_pid(pid);
return pokemon_from_state(state, pid, nature, roaming);
Expand All @@ -153,10 +166,23 @@ AdvPokemonResult reroll_wild_pokemon(uint8_t nature, uint16_t seed, uint64_t adv
if ((pid % 25) == nature){
AdvRngState rngstate = rngstate_from_internal_state(seed, advances, lowstate, method);
return pokemon_from_state(rngstate, pid, AdvNature(nature));
}else{
lowstate = increment_internal_rng_state(highstate);
highstate = increment_internal_rng_state(lowstate);
}
lowstate = increment_internal_rng_state(highstate);
highstate = increment_internal_rng_state(lowstate);
}
}

AdvPokemonResult reroll_unown(uint8_t unownform, uint16_t seed, uint64_t advances, uint32_t state, AdvRngMethod method){
uint32_t lowstate = state;
uint32_t highstate = increment_internal_rng_state(state);
while (true){
uint32_t pid = pid_from_states(highstate, lowstate);
if (unown_form_from_pid(pid) == unownform){
AdvRngState rngstate = rngstate_from_internal_state(seed, advances, lowstate, method);
return pokemon_from_state(rngstate, pid, nature_from_pid(pid));
}
lowstate = increment_internal_rng_state(highstate);
highstate = increment_internal_rng_state(lowstate);
}
}

Expand Down Expand Up @@ -233,6 +259,74 @@ uint8_t slot_number_from_roll(uint8_t roll, size_t size, bool super_rod) {
}
}

int slot_to_unownform(AdvEncounterSlot slot){
if (slot.species.find("unown") == std::string::npos){
return -1;
}else{
char form = slot.species.back();
switch (form){
case 'a':
return 0;
case 'b':
return 1;
case 'c':
return 2;
case 'd':
return 3;
case 'e':
return 4;
case 'f':
return 5;
case 'g':
return 6;
case 'h':
return 7;
case 'i':
return 8;
case 'j':
return 9;
case 'k':
return 10;
case 'l':
return 11;
case 'm':
return 12;
case 'n':
return 13;
case 'o':
return 14;
case 'p':
return 15;
case 'q':
return 16;
case 'r':
return 17;
case 's':
return 18;
case 't':
return 19;
case 'u':
return 20;
case 'v':
return 21;
case 'w':
return 22;
case 'x':
return 23;
case 'y':
return 24;
case 'z':
return 25;
case '!':
return 26;
case '?':
return 27;
default:
return -1;
}
}
}

AdvWildPokemonResult wild_pokemon_from_state(AdvRngState state, std::vector<AdvEncounterSlot> slots, bool super_rod){

uint8_t slot_roll = (state.s0 >> 16) % 100;
Expand All @@ -241,14 +335,21 @@ AdvWildPokemonResult wild_pokemon_from_state(AdvRngState state, std::vector<AdvE

uint8_t slot_num = slot_number_from_roll(slot_roll, slots.size(), super_rod);
AdvEncounterSlot slot = slots[slot_num];
int unownform = slot_to_unownform(slot);

uint8_t diff = slot.maxlevel - slot.minlevel;
if (slot.maxlevel < slot.minlevel) {
diff = 0;
}
uint8_t level = slot.minlevel + (level_roll % (diff + 1));

AdvPokemonResult temp_poke = reroll_wild_pokemon(nature, state.seed, state.advance, state.s3, state.method);

AdvPokemonResult temp_poke;
if (unownform >= 0){
temp_poke = reroll_unown(static_cast<uint8_t>(unownform), state.seed, state.advance, state.s2, state.method);
}else{
temp_poke = reroll_wild_pokemon(nature, state.seed, state.advance, state.s3, state.method);
}

return {
slot.species,
Expand Down Expand Up @@ -409,7 +510,8 @@ bool check_for_match(AdvPokemonResult res, AdvRngFilters target, int16_t gender_
}

bool check_for_match(AdvWildPokemonResult res, AdvRngFilters target, int16_t gender_threshold, uint16_t tid_xor_sid){
return (target.species == res.species)
std::string res_name = res.species.find("unown") != std::string::npos ? "unown" : res.species;
return (target.species == res_name)
&& (target.level == res.level)
&& (target.nature == AdvNature::Any || (res.nature == target.nature))
&& (target.ability == AdvAbility::Any || (res.ability == target.ability))
Expand Down Expand Up @@ -618,12 +720,12 @@ std::string nature_to_string(const AdvNature& nature){



void shrink_iv_range(IvRange& mutated_range, IvRange& fixed_range){
void shrink_iv_range(IvRange& mutated_range, const IvRange& fixed_range){
mutated_range.low = std::max(mutated_range.low, fixed_range.low);
mutated_range.high = std::min(mutated_range.high, fixed_range.high);
}

void shrink_iv_ranges(IvRanges& mutated_ranges, IvRanges& fixed_ranges){
void shrink_iv_ranges(IvRanges& mutated_ranges, const IvRanges& fixed_ranges){
shrink_iv_range(mutated_ranges.hp, fixed_ranges.hp);
shrink_iv_range(mutated_ranges.attack, fixed_ranges.attack);
shrink_iv_range(mutated_ranges.defense, fixed_ranges.defense);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,15 @@ std::string RngTargetDisplay::result_to_string(const AdvPokemonResult& pokemon,
}

std::string RngTargetDisplay::result_to_string(const AdvWildPokemonResult& pokemon, const int16_t& gender_threshold){
std::string name;
if (pokemon.species.find("unown") != std::string::npos){
char form = char(toupper(pokemon.species.substr(6,1)[0]));
name = get_pokemon_name("unown").display_name() + " (" + form + ")";
}else{
name = get_pokemon_name(pokemon.species).display_name();
}
std::string out;
out += get_pokemon_name(pokemon.species).display_name() + " ";
out += name + " ";
out += "Lv" + std::to_string(pokemon.level) + " ";
out += gender_to_string(gender_from_gender_value(pokemon.gender, gender_threshold)) + " ";
out += nature_to_string(pokemon.nature) + " ";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,22 @@ RngStatsDatabase::RngStatsDatabase(const char* json_path){
}

const RngStats& RngStatsDatabase::get_throw(const std::string& slug) const{
auto iter = m_database.find(slug);
std::string sl = slug;
if (slug.find("unown") != std::string::npos){
sl = "unown";
}
auto iter = m_database.find(sl);
if (iter == m_database.end()){
throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "Slug not found in database: " + slug);
throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "Slug not found in database: " + sl);
}
return iter->second;
}
const RngStats* RngStatsDatabase::get_nothrow(const std::string& slug) const{
auto iter = m_database.find(slug);
std::string sl = slug;
if (slug.find("unown") != std::string::npos){
sl = "unown";
}
auto iter = m_database.find(sl);
if (iter == m_database.end()){
return nullptr;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ void WildRng::program(SingleSwitchProgramEnvironment& env, ProControllerContext&

std::map<std::string, std::vector<AdvEncounterSlot>> location_map = encounters_data.get_throw(enc_slug);
if (location_map.find(loc_slug)==location_map.end()){
throw UserSetupError(env.console, "The target Seed is missing from the list of nearby seeds.");
throw UserSetupError(env.console, "The target encounter type / location combination could not be found.");
}

std::vector<AdvEncounterSlot> ENCOUNTER_SLOTS = location_map.find(loc_slug)->second;
Expand All @@ -300,12 +300,17 @@ void WildRng::program(SingleSwitchProgramEnvironment& env, ProControllerContext&

std::set<std::string> SPECIES_LIST;
for (auto slot : ENCOUNTER_SLOTS){
SPECIES_LIST.emplace(slot.species);
if (slot.species.find("unown") != std::string::npos){
SPECIES_LIST.emplace("unown");
}else{
SPECIES_LIST.emplace(slot.species);
}
}

const bool SUPER_ROD = ENCOUNTER_TYPE == EncounterType::superrod;


const bool UNOWN_BUMPS = (loc_slug == "tanoby_ruins_monean_chamber" ||
loc_slug == "tanoby_ruins_viapois_chamber");

// prepare timings

Expand Down Expand Up @@ -388,11 +393,12 @@ void WildRng::program(SingleSwitchProgramEnvironment& env, ProControllerContext&
env.log(" SpD: " + std::to_string(target_result.ivs.spdef));
env.log(" Spe: " + std::to_string(target_result.ivs.speed));

RngCalibrations calibrations = {
const RngCalibrations initial_calibrations = {
RNG_CALIBRATION.seed_calibration / FRLG_FRAME_DURATION,
RNG_CALIBRATION.csf_calibration,
RNG_CALIBRATION.advances_calibration
};
RngCalibrations calibrations = initial_calibrations;
log_calibrations(env.console, calibrations, true);

Milliseconds launch_delay = INITIAL_LAUNCH_DELAY;
Expand All @@ -401,6 +407,7 @@ void WildRng::program(SingleSwitchProgramEnvironment& env, ProControllerContext&
RngCalibrationHistory calibration_history;

uint16_t failed_searches = 0;
uint16_t advances_bump = 0;

while (true){
if (calibration_history.results.size() > 0){
Expand Down Expand Up @@ -438,10 +445,22 @@ void WildRng::program(SingleSwitchProgramEnvironment& env, ProControllerContext&

if (calibration_history.results.size() > 0){
calibrations = get_calibrations(env.console, calibration_history, SEED_VALUES, SEED_POSITION, ADVANCES);
}else{
calibrations = initial_calibrations;
}

// if previous resets had uncertain advances, slightly modify the seed delay to try to hit a different target
apply_seed_bump(calibrations, uncertain_history);
// there can be long runs of identical unowns (especially for A and Z),
// so a moderate offset may be needed to gain additional info about advances position
if (UNOWN_BUMPS){
if (uncertain_history.results.empty()){
advances_bump = 0;
}else{
calibrations.ingame_offset += advances_bump;
}
}else{
// if previous resets had uncertain advances, slightly modify the seed delay to try to hit a different target
apply_seed_bump(calibrations, uncertain_history);
}

uint64_t ingame_advances = ADVANCES - CONTINUE_SCREEN_FRAMES;

Expand Down Expand Up @@ -521,7 +540,7 @@ void WildRng::program(SingleSwitchProgramEnvironment& env, ProControllerContext&
species_stats = STATS_DATA.get_throw(pokemon.species);
}catch (const InternalProgramError& err){
env.log(err.message());
env.log("Failed to load base stats.");
env.log("Failed to load base stats.", COLOR_RED);
continue;
}
BaseStats base_stats = species_stats.base_stats;
Expand All @@ -548,6 +567,15 @@ void WildRng::program(SingleSwitchProgramEnvironment& env, ProControllerContext&
env.log("RNG search finished.");
update_failed_searches(failed_searches, search_hits);

if ( UNOWN_BUMPS
&& search_hits.size() > 0
&& same_seeds(search_hits)
&& search_hits[0].seed == TARGET_SEED
){
// keep increasing advances until hitting something that provides new information (A/Z form with different nature/IVs or a !/? form)
// only bump advances for the target seed to ensure nothing is missed
advances_bump++;
}
}

if (GO_HOME_WHEN_DONE){
Expand Down
Loading