From 5ba5f68b842cfb0fdc3a4993be397ca8fcf7b178 Mon Sep 17 00:00:00 2001 From: Mysticial Date: Sun, 1 Oct 2023 08:06:34 -0700 Subject: [PATCH] Add stats reader for summaries. --- SerialPrograms/CMakeLists.txt | 2 + .../CommonFramework/OCR/OCR_NumberReader.cpp | 1 + .../CommonFramework/OCR/OCR_NumberReader.h | 3 +- .../DevPrograms/TestProgramComputer.cpp | 3 +- .../DevPrograms/TestProgramSwitch.cpp | 65 ++++- .../Source/Pokemon/Pokemon_NatureChecker.cpp | 179 ++++++------ .../Source/Pokemon/Pokemon_NatureChecker.h | 73 +++-- .../Pokemon/Pokemon_StatsCalculation.cpp | 180 +++++++++++- .../Source/Pokemon/Pokemon_StatsCalculation.h | 66 ++++- .../Boxes/PokemonSV_IVCheckerReader.cpp | 12 +- .../Inference/PokemonSV_StatHexagonReader.cpp | 260 ++++++++++++++++++ .../Inference/PokemonSV_StatHexagonReader.h | 98 +++++++ .../Farming/PokemonSV_TournamentFarmer.cpp | 2 +- 13 files changed, 817 insertions(+), 127 deletions(-) create mode 100644 SerialPrograms/Source/PokemonSV/Inference/PokemonSV_StatHexagonReader.cpp create mode 100644 SerialPrograms/Source/PokemonSV/Inference/PokemonSV_StatHexagonReader.h diff --git a/SerialPrograms/CMakeLists.txt b/SerialPrograms/CMakeLists.txt index 52bafbac9..3e5064582 100644 --- a/SerialPrograms/CMakeLists.txt +++ b/SerialPrograms/CMakeLists.txt @@ -1338,6 +1338,8 @@ file(GLOB MAIN_SOURCES Source/PokemonSV/Inference/PokemonSV_WhiteButtonDetector.h Source/PokemonSV/Inference/PokemonSV_ZeroGateWarpPromptDetector.cpp Source/PokemonSV/Inference/PokemonSV_ZeroGateWarpPromptDetector.h + Source/PokemonSV/Inference/PokemonSV_StatHexagonReader.cpp + Source/PokemonSV/Inference/PokemonSV_StatHexagonReader.h Source/PokemonSV/Inference/Tera/PokemonSV_TeraCardDetector.cpp Source/PokemonSV/Inference/Tera/PokemonSV_TeraCardDetector.h Source/PokemonSV/Inference/Tera/PokemonSV_TeraCodeReader.cpp diff --git a/SerialPrograms/Source/CommonFramework/OCR/OCR_NumberReader.cpp b/SerialPrograms/Source/CommonFramework/OCR/OCR_NumberReader.cpp index 617b3da2e..540e1348c 100644 --- a/SerialPrograms/Source/CommonFramework/OCR/OCR_NumberReader.cpp +++ b/SerialPrograms/Source/CommonFramework/OCR/OCR_NumberReader.cpp @@ -6,6 +6,7 @@ #include #include "CommonFramework/Language.h" +#include "CommonFramework/Logging/Logger.h" #include "OCR_RawOCR.h" #include "OCR_NumberReader.h" diff --git a/SerialPrograms/Source/CommonFramework/OCR/OCR_NumberReader.h b/SerialPrograms/Source/CommonFramework/OCR/OCR_NumberReader.h index 41a2c8525..824140a6c 100644 --- a/SerialPrograms/Source/CommonFramework/OCR/OCR_NumberReader.h +++ b/SerialPrograms/Source/CommonFramework/OCR/OCR_NumberReader.h @@ -7,9 +7,8 @@ #ifndef PokemonAutomation_OCR_NumberReader_H #define PokemonAutomation_OCR_NumberReader_H -#include "CommonFramework/Logging/Logger.h" - namespace PokemonAutomation{ + class Logger; class ImageViewRGB32; namespace OCR{ diff --git a/SerialPrograms/Source/NintendoSwitch/DevPrograms/TestProgramComputer.cpp b/SerialPrograms/Source/NintendoSwitch/DevPrograms/TestProgramComputer.cpp index 3d5d9d95d..aca1e4944 100644 --- a/SerialPrograms/Source/NintendoSwitch/DevPrograms/TestProgramComputer.cpp +++ b/SerialPrograms/Source/NintendoSwitch/DevPrograms/TestProgramComputer.cpp @@ -235,6 +235,7 @@ void TestProgramComputer::program(ProgramEnvironment& env, CancellableScope& sco // using namespace NintendoSwitch::PokemonSwSh::MaxLairInternal; +#if 0 uint8_t low_iv; uint8_t high_iv; @@ -247,7 +248,7 @@ void TestProgramComputer::program(ProgramEnvironment& env, CancellableScope& sco cout << "ok = " << ok << endl; cout << "low = " << (int)low_iv << endl; cout << "high = " << (int)high_iv << endl; - +#endif diff --git a/SerialPrograms/Source/NintendoSwitch/DevPrograms/TestProgramSwitch.cpp b/SerialPrograms/Source/NintendoSwitch/DevPrograms/TestProgramSwitch.cpp index eeb409e36..9b9c04ee7 100644 --- a/SerialPrograms/Source/NintendoSwitch/DevPrograms/TestProgramSwitch.cpp +++ b/SerialPrograms/Source/NintendoSwitch/DevPrograms/TestProgramSwitch.cpp @@ -109,6 +109,7 @@ #include "PokemonSV/Inference/PokemonSV_BagDetector.h" #include #include "PokemonSwSh/MaxLair/Inference/PokemonSwSh_MaxLair_Detect_Lobby.h" +#include "PokemonSV/Inference/PokemonSV_StatHexagonReader.h" @@ -236,12 +237,74 @@ void TestProgram::program(MultiSwitchProgramEnvironment& env, CancellableScope& VideoOverlaySet overlays(overlay); + SummaryStatsReader reader; + reader.make_overlays(overlays); + + auto snapshot = console.video().snapshot(); + +#if 1 + NatureAdjustments nature = reader.read_nature(logger, snapshot); + cout << "attack = " << (int)nature.attack << endl; + cout << "defense = " << (int)nature.defense << endl; + cout << "spatk = " << (int)nature.spatk << endl; + cout << "spdef = " << (int)nature.spdef << endl; + cout << "speed = " << (int)nature.speed << endl; + + StatReads stats = reader.read_stats(logger, snapshot); + cout << "hp = " << stats.hp << endl; + cout << "attack = " << stats.attack << endl; + cout << "defense = " << stats.defense << endl; + cout << "spatk = " << stats.spatk << endl; + cout << "spdef = " << stats.spdef << endl; + cout << "speed = " << stats.speed << endl; + + BaseStats base_stats{68, 65, 65, 125, 115, 80}; + EVs evs{252, 0, 6, 252, 0, 0}; + IvRanges ivs = reader.calc_ivs(logger, snapshot, base_stats, evs); + cout << "hp = " << (int)ivs.hp.low << " - " << (int)ivs.hp.high << endl; + cout << "attack = " << (int)ivs.attack.low << " - " << (int)ivs.attack.high << endl; + cout << "defense = " << (int)ivs.defense.low << " - " << (int)ivs.defense.high << endl; + cout << "spatk = " << (int)ivs.spatk.low << " - " << (int)ivs.spatk.high << endl; + cout << "spdef = " << (int)ivs.spdef.low << " - " << (int)ivs.spdef.high << endl; + cout << "speed = " << (int)ivs.speed.low << " - " << (int)ivs.speed.high << endl; +#endif + + +#if 0 + cout << "attack: "; + reader.read_stat_adjustment({0.874, 0.293, 0.014, 0.024}, snapshot); + cout << "spatk: "; + reader.read_stat_adjustment({0.770, 0.293, 0.014, 0.024}, snapshot); + cout << "speed: "; + reader.read_stat_adjustment({0.822, 0.452, 0.014, 0.024}, snapshot); +#endif + + +#if 0 + ImageFloatBox hp (0.823, 0.244, 0.012, 0.022); + ImageFloatBox atk (0.875, 0.294, 0.012, 0.022); + ImageFloatBox def (0.875, 0.402, 0.012, 0.022); + ImageFloatBox spatk (0.771, 0.294, 0.012, 0.022); + ImageFloatBox spdef (0.771, 0.402, 0.012, 0.022); + ImageFloatBox spd (0.823, 0.453, 0.012, 0.022); + + overlays.add(COLOR_RED, hp); + overlays.add(COLOR_RED, atk); + overlays.add(COLOR_RED, def); + overlays.add(COLOR_RED, spatk); + overlays.add(COLOR_RED, spdef); + overlays.add(COLOR_RED, spd); +#endif + + + +#if 0 PokemonSwSh::MaxLairInternal::LobbyJoinedDetector detector(2, false); auto snapshot = console.video().snapshot(); // detector.VisualInferenceCallback::process_frame(snapshot); detector.joined(snapshot, snapshot.timestamp); - +#endif // size_t errors = 0; // attach_item_from_bag(env.program_info(), console, context, errors); diff --git a/SerialPrograms/Source/Pokemon/Pokemon_NatureChecker.cpp b/SerialPrograms/Source/Pokemon/Pokemon_NatureChecker.cpp index db2dcde41..242bb6327 100644 --- a/SerialPrograms/Source/Pokemon/Pokemon_NatureChecker.cpp +++ b/SerialPrograms/Source/Pokemon/Pokemon_NatureChecker.cpp @@ -5,108 +5,119 @@ */ #include -#include "Common/Cpp/Exceptions.h" #include "Pokemon_NatureChecker.h" namespace PokemonAutomation{ namespace Pokemon{ -const std::map NatureCheckerValue_TOKEN_TO_ENUM{ - {"Adamant", NatureCheckerValue::Adamant}, - {"Bashful", NatureCheckerValue::Bashful}, - {"Bold", NatureCheckerValue::Bold}, - {"Brave", NatureCheckerValue::Brave}, - {"Calm", NatureCheckerValue::Calm}, - {"Careful", NatureCheckerValue::Careful}, - {"Docile", NatureCheckerValue::Docile}, - {"Gentle", NatureCheckerValue::Gentle}, - {"Hardy", NatureCheckerValue::Hardy}, - {"Hasty", NatureCheckerValue::Hasty}, - {"Impish", NatureCheckerValue::Impish}, - {"Jolly", NatureCheckerValue::Jolly}, - {"Lax", NatureCheckerValue::Lax}, - {"Lonely", NatureCheckerValue::Lonely}, - {"Mild", NatureCheckerValue::Mild}, - {"Modest", NatureCheckerValue::Modest}, - {"Naive", NatureCheckerValue::Naive}, - {"Naughty", NatureCheckerValue::Naughty}, - {"Quiet", NatureCheckerValue::Quiet}, - {"Quirky", NatureCheckerValue::Quirky}, - {"Rash", NatureCheckerValue::Rash}, - {"Relaxed", NatureCheckerValue::Relaxed}, - {"Sassy", NatureCheckerValue::Sassy}, - {"Serious", NatureCheckerValue::Serious}, - {"Timid", NatureCheckerValue::Timid}, -}; -const std::map NatureCheckerValue_ENUM_TO_TOKEN{ - {NatureCheckerValue::UnableToDetect, "Unable to Detect"}, - {NatureCheckerValue::Neutral, "Neutral"}, - {NatureCheckerValue::Adamant, "Adamant"}, - {NatureCheckerValue::Bashful, "Bashful"}, - {NatureCheckerValue::Bold, "Bold"}, - {NatureCheckerValue::Brave, "Brave"}, - {NatureCheckerValue::Calm, "Calm"}, - {NatureCheckerValue::Careful, "Careful"}, - {NatureCheckerValue::Gentle, "Gentle"}, - {NatureCheckerValue::Hardy, "Hardy"}, - {NatureCheckerValue::Hasty, "Hasty"}, - {NatureCheckerValue::Impish, "Impish"}, - {NatureCheckerValue::Jolly, "Jolly"}, - {NatureCheckerValue::Lax, "Lax"}, - {NatureCheckerValue::Lonely, "Lonely"}, - {NatureCheckerValue::Mild, "Mild"}, - {NatureCheckerValue::Modest, "Modest"}, - {NatureCheckerValue::Naive, "Naive"}, - {NatureCheckerValue::Naughty, "Naughty"}, - {NatureCheckerValue::Quiet, "Quiet"}, - {NatureCheckerValue::Quirky, "Quirky"}, - {NatureCheckerValue::Rash, "Rash"}, - {NatureCheckerValue::Relaxed, "Relaxed"}, - {NatureCheckerValue::Sassy, "Sassy"}, - {NatureCheckerValue::Serious, "Serious"}, - {NatureCheckerValue::Timid, "Timid"}, -}; - -const std::map, NatureCheckerValue> NatureCheckerValue_HELPHINDER_TO_ENUM{ - {{ 0, 2 }, NatureCheckerValue::Adamant}, - {{ -1, -1 }, NatureCheckerValue::Neutral}, //Bashful, Docile, Hardy, Quirky, Serious - {{ 1, 0 }, NatureCheckerValue::Bold}, - {{ 0, 4 }, NatureCheckerValue::Brave}, - {{ 3, 0 }, NatureCheckerValue::Calm}, - {{ 3, 2 }, NatureCheckerValue::Careful}, - {{ 3, 1 }, NatureCheckerValue::Gentle}, - {{ 4, 1 }, NatureCheckerValue::Hasty}, - {{ 1, 2 }, NatureCheckerValue::Impish}, - {{ 4, 2 }, NatureCheckerValue::Jolly}, - {{ 1, 3 }, NatureCheckerValue::Lax}, - {{ 0, 1 }, NatureCheckerValue::Lonely}, - {{ 2, 1 }, NatureCheckerValue::Mild}, - {{ 2, 0 }, NatureCheckerValue::Modest}, - {{ 4, 3 }, NatureCheckerValue::Naive}, - {{ 0, 3 }, NatureCheckerValue::Naughty}, - {{ 2, 4 }, NatureCheckerValue::Quiet}, - {{ 2, 3 }, NatureCheckerValue::Rash}, - {{ 1, 4 }, NatureCheckerValue::Relaxed}, - {{ 3, 4 }, NatureCheckerValue::Sassy}, - {{ 4, 0 }, NatureCheckerValue::Timid}, -}; +const std::map& NatureCheckerValue_TOKEN_TO_ENUM(){ + static std::map data{ + {"Adamant", NatureCheckerValue::Adamant}, + {"Bashful", NatureCheckerValue::Bashful}, + {"Bold", NatureCheckerValue::Bold}, + {"Brave", NatureCheckerValue::Brave}, + {"Calm", NatureCheckerValue::Calm}, + {"Careful", NatureCheckerValue::Careful}, + {"Docile", NatureCheckerValue::Docile}, + {"Gentle", NatureCheckerValue::Gentle}, + {"Hardy", NatureCheckerValue::Hardy}, + {"Hasty", NatureCheckerValue::Hasty}, + {"Impish", NatureCheckerValue::Impish}, + {"Jolly", NatureCheckerValue::Jolly}, + {"Lax", NatureCheckerValue::Lax}, + {"Lonely", NatureCheckerValue::Lonely}, + {"Mild", NatureCheckerValue::Mild}, + {"Modest", NatureCheckerValue::Modest}, + {"Naive", NatureCheckerValue::Naive}, + {"Naughty", NatureCheckerValue::Naughty}, + {"Quiet", NatureCheckerValue::Quiet}, + {"Quirky", NatureCheckerValue::Quirky}, + {"Rash", NatureCheckerValue::Rash}, + {"Relaxed", NatureCheckerValue::Relaxed}, + {"Sassy", NatureCheckerValue::Sassy}, + {"Serious", NatureCheckerValue::Serious}, + {"Timid", NatureCheckerValue::Timid}, + }; + return data; +} +const std::map& NatureCheckerValue_ENUM_TO_TOKEN(){ + static std::map data{ + {NatureCheckerValue::UnableToDetect, "Unable to Detect"}, + {NatureCheckerValue::Neutral, "Neutral"}, + {NatureCheckerValue::Adamant, "Adamant"}, + {NatureCheckerValue::Bashful, "Bashful"}, + {NatureCheckerValue::Bold, "Bold"}, + {NatureCheckerValue::Brave, "Brave"}, + {NatureCheckerValue::Calm, "Calm"}, + {NatureCheckerValue::Careful, "Careful"}, + {NatureCheckerValue::Gentle, "Gentle"}, + {NatureCheckerValue::Hardy, "Hardy"}, + {NatureCheckerValue::Hasty, "Hasty"}, + {NatureCheckerValue::Impish, "Impish"}, + {NatureCheckerValue::Jolly, "Jolly"}, + {NatureCheckerValue::Lax, "Lax"}, + {NatureCheckerValue::Lonely, "Lonely"}, + {NatureCheckerValue::Mild, "Mild"}, + {NatureCheckerValue::Modest, "Modest"}, + {NatureCheckerValue::Naive, "Naive"}, + {NatureCheckerValue::Naughty, "Naughty"}, + {NatureCheckerValue::Quiet, "Quiet"}, + {NatureCheckerValue::Quirky, "Quirky"}, + {NatureCheckerValue::Rash, "Rash"}, + {NatureCheckerValue::Relaxed, "Relaxed"}, + {NatureCheckerValue::Sassy, "Sassy"}, + {NatureCheckerValue::Serious, "Serious"}, + {NatureCheckerValue::Timid, "Timid"}, + }; + return data; +} +const std::map, NatureCheckerValue>& NatureCheckerValue_HELPHINDER_TO_ENUM(){ + static std::map, NatureCheckerValue> data{ + {{ 0, 2 }, NatureCheckerValue::Adamant}, + {{ -1, -1 }, NatureCheckerValue::Neutral}, //Bashful, Docile, Hardy, Quirky, Serious + {{ 1, 0 }, NatureCheckerValue::Bold}, + {{ 0, 4 }, NatureCheckerValue::Brave}, + {{ 3, 0 }, NatureCheckerValue::Calm}, + {{ 3, 2 }, NatureCheckerValue::Careful}, + {{ 3, 1 }, NatureCheckerValue::Gentle}, + {{ 4, 1 }, NatureCheckerValue::Hasty}, + {{ 1, 2 }, NatureCheckerValue::Impish}, + {{ 4, 2 }, NatureCheckerValue::Jolly}, + {{ 1, 3 }, NatureCheckerValue::Lax}, + {{ 0, 1 }, NatureCheckerValue::Lonely}, + {{ 2, 1 }, NatureCheckerValue::Mild}, + {{ 2, 0 }, NatureCheckerValue::Modest}, + {{ 4, 3 }, NatureCheckerValue::Naive}, + {{ 0, 3 }, NatureCheckerValue::Naughty}, + {{ 2, 4 }, NatureCheckerValue::Quiet}, + {{ 2, 3 }, NatureCheckerValue::Rash}, + {{ 1, 4 }, NatureCheckerValue::Relaxed}, + {{ 3, 4 }, NatureCheckerValue::Sassy}, + {{ 4, 0 }, NatureCheckerValue::Timid}, + }; + return data; +} NatureCheckerValue NatureCheckerValue_string_to_enum(const std::string& token){ - auto iter = NatureCheckerValue_TOKEN_TO_ENUM.find(token); - if (iter == NatureCheckerValue_TOKEN_TO_ENUM.end()){ + auto iter = NatureCheckerValue_TOKEN_TO_ENUM().find(token); + if (iter == NatureCheckerValue_TOKEN_TO_ENUM().end()){ return NatureCheckerValue::UnableToDetect; } return iter->second; } NatureCheckerValue NatureCheckerValue_helphinder_to_enum(const std::pair& token) { - auto iter = NatureCheckerValue_HELPHINDER_TO_ENUM.find(token); - if (iter == NatureCheckerValue_HELPHINDER_TO_ENUM.end()) { + auto iter = NatureCheckerValue_HELPHINDER_TO_ENUM().find(token); + if (iter == NatureCheckerValue_HELPHINDER_TO_ENUM().end()) { return NatureCheckerValue::UnableToDetect; } return iter->second; } + + + + const EnumDatabase& NatureCheckerValue_Database(){ static EnumDatabase database({ {NatureCheckerValue::Adamant, "adamant", "Adamant"}, diff --git a/SerialPrograms/Source/Pokemon/Pokemon_NatureChecker.h b/SerialPrograms/Source/Pokemon/Pokemon_NatureChecker.h index 954fb7873..2297cf4bb 100644 --- a/SerialPrograms/Source/Pokemon/Pokemon_NatureChecker.h +++ b/SerialPrograms/Source/Pokemon/Pokemon_NatureChecker.h @@ -16,66 +16,79 @@ namespace Pokemon{ enum class NatureCheckerValue{ UnableToDetect, +// Any, + Neutral, - Any, - Adamant, + Bashful, + Docile, + Hardy, + Quirky, + Serious, + Bold, - Brave, + Modest, Calm, - Careful, - Docile, + Timid, + + Lonely, + Mild, Gentle, - Hardy, Hasty, + + Adamant, Impish, + Careful, Jolly, - Lax, - Lonely, - Mild, - Modest, - Naive, + Naughty, - Quiet, - Quirky, + Lax, Rash, + Naive, + + Brave, Relaxed, + Quiet, Sassy, - Serious, - Timid }; const EnumDatabase& NatureCheckerValue_Database(); NatureCheckerValue NatureCheckerValue_string_to_enum(const std::string& token); NatureCheckerValue NatureCheckerValue_helphinder_to_enum(const std::pair& token); -enum class NatureCheckerFilter { +enum class NatureCheckerFilter{ Any, - Adamant, + Bashful, + Docile, + Hardy, + Quirky, + Serious, + Bold, - Brave, + Modest, Calm, - Careful, - Docile, + Timid, + + Lonely, + Mild, Gentle, - Hardy, Hasty, + + Adamant, Impish, + Careful, Jolly, - Lax, - Lonely, - Mild, - Modest, - Naive, + Naughty, - Quiet, - Quirky, + Lax, Rash, + Naive, + + Brave, Relaxed, + Quiet, Sassy, - Serious, - Timid }; const EnumDatabase& NatureCheckerFilter_Database(); diff --git a/SerialPrograms/Source/Pokemon/Pokemon_StatsCalculation.cpp b/SerialPrograms/Source/Pokemon/Pokemon_StatsCalculation.cpp index 8da87f1e9..215f1a4bf 100644 --- a/SerialPrograms/Source/Pokemon/Pokemon_StatsCalculation.cpp +++ b/SerialPrograms/Source/Pokemon/Pokemon_StatsCalculation.cpp @@ -4,12 +4,125 @@ * */ +#include "Common/Cpp/Exceptions.h" #include "Pokemon_StatsCalculation.h" namespace PokemonAutomation{ namespace Pokemon{ +#if 0 +NatureAdjustments get_nature_adjustments(NatureCheckerValue nature){ + NatureAdjustments ret; + ret.attack = NatureAdjustment::NEUTRAL; + ret.defense = NatureAdjustment::NEUTRAL; + ret.spatk = NatureAdjustment::NEUTRAL; + ret.spdef = NatureAdjustment::NEUTRAL; + ret.speed = NatureAdjustment::NEUTRAL; + + switch (nature){ + case NatureCheckerValue::UnableToDetect: + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "Cannot query invalid nature."); + + case NatureCheckerValue::Neutral: + case NatureCheckerValue::Bashful: + case NatureCheckerValue::Docile: + case NatureCheckerValue::Hardy: + case NatureCheckerValue::Quirky: + case NatureCheckerValue::Serious: + return ret; + + case NatureCheckerValue::Bold: + ret.attack = NatureAdjustment::NEGATIVE; + ret.defense = NatureAdjustment::POSITIVE; + return ret; + case NatureCheckerValue::Modest: + ret.attack = NatureAdjustment::NEGATIVE; + ret.spatk = NatureAdjustment::POSITIVE; + return ret; + case NatureCheckerValue::Calm: + ret.attack = NatureAdjustment::NEGATIVE; + ret.spdef = NatureAdjustment::POSITIVE; + return ret; + case NatureCheckerValue::Timid: + ret.attack = NatureAdjustment::NEGATIVE; + ret.speed = NatureAdjustment::POSITIVE; + return ret; + + case NatureCheckerValue::Lonely: + ret.defense = NatureAdjustment::NEGATIVE; + ret.attack = NatureAdjustment::POSITIVE; + return ret; + case NatureCheckerValue::Mild: + ret.defense = NatureAdjustment::NEGATIVE; + ret.spatk = NatureAdjustment::POSITIVE; + return ret; + case NatureCheckerValue::Gentle: + ret.defense = NatureAdjustment::NEGATIVE; + ret.spdef = NatureAdjustment::POSITIVE; + return ret; + case NatureCheckerValue::Hasty: + ret.defense = NatureAdjustment::NEGATIVE; + ret.speed = NatureAdjustment::POSITIVE; + return ret; + + case NatureCheckerValue::Adamant: + ret.spatk = NatureAdjustment::NEGATIVE; + ret.attack = NatureAdjustment::POSITIVE; + return ret; + case NatureCheckerValue::Impish: + ret.spatk = NatureAdjustment::NEGATIVE; + ret.defense = NatureAdjustment::POSITIVE; + return ret; + case NatureCheckerValue::Careful: + ret.spatk = NatureAdjustment::NEGATIVE; + ret.spdef = NatureAdjustment::POSITIVE; + return ret; + case NatureCheckerValue::Jolly: + ret.spatk = NatureAdjustment::NEGATIVE; + ret.speed = NatureAdjustment::POSITIVE; + return ret; + + case NatureCheckerValue::Naughty: + ret.spdef = NatureAdjustment::NEGATIVE; + ret.attack = NatureAdjustment::POSITIVE; + return ret; + case NatureCheckerValue::Lax: + ret.spdef = NatureAdjustment::NEGATIVE; + ret.defense = NatureAdjustment::POSITIVE; + return ret; + case NatureCheckerValue::Rash: + ret.spdef = NatureAdjustment::NEGATIVE; + ret.spatk = NatureAdjustment::POSITIVE; + return ret; + case NatureCheckerValue::Naive: + ret.spdef = NatureAdjustment::NEGATIVE; + ret.speed = NatureAdjustment::POSITIVE; + return ret; + + case NatureCheckerValue::Brave: + ret.speed = NatureAdjustment::NEGATIVE; + ret.attack = NatureAdjustment::POSITIVE; + return ret; + case NatureCheckerValue::Relaxed: + ret.speed = NatureAdjustment::NEGATIVE; + ret.defense = NatureAdjustment::POSITIVE; + return ret; + case NatureCheckerValue::Quiet: + ret.speed = NatureAdjustment::NEGATIVE; + ret.spatk = NatureAdjustment::POSITIVE; + return ret; + case NatureCheckerValue::Sassy: + ret.speed = NatureAdjustment::NEGATIVE; + ret.spdef = NatureAdjustment::POSITIVE; + return ret; + + default: + throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "Unkwown Nature: " + std::to_string((int)nature)); + } +} +#endif + uint16_t calc_stats_hp(uint8_t base_stat, uint8_t level, uint8_t iv, uint8_t ev){ uint16_t stat = (uint16_t)2 * base_stat + iv + ev/4; @@ -40,7 +153,7 @@ uint16_t calc_stats_nonhp(uint8_t base_stat, uint8_t level, uint8_t iv, uint8_t } - +#if 0 bool calc_iv_range( uint8_t& low_iv, uint8_t& high_iv, bool is_hp, uint8_t base_stat, uint8_t level, uint8_t ev, @@ -73,6 +186,71 @@ bool calc_iv_range( return set; } +#endif +IvRange calc_iv_range( + bool is_hp, uint8_t base_stat, uint8_t level, uint8_t ev, + uint16_t stat, NatureAdjustment nature +){ + IvRange ret; + + bool set = false; + for (uint8_t iv = 0; iv < 32; iv++){ + uint16_t current_stat = is_hp + ? calc_stats_hp(base_stat, level, iv, ev) + : calc_stats_nonhp(base_stat, level, iv, ev, nature); + + if (stat != current_stat){ + continue; + } + + if (!set){ + set = true; + ret.low = iv; + ret.high = iv; + continue; + } + + if ((uint8_t)ret.low > iv){ + ret.low = iv; + } + if ((uint8_t)ret.high < iv){ + ret.high = iv; + } + } + + return ret; +} + + +IvRanges calc_iv_ranges( + const BaseStats& base_stats, uint8_t level, const EVs& evs, + const StatReads& actual_stats, const NatureAdjustments& natures +){ + IvRanges ret; + + if (actual_stats.hp >= 10){ + ret.hp = calc_iv_range(true, base_stats.hp, level, evs.hp, actual_stats.hp, NatureAdjustment::NEUTRAL); + } + if (actual_stats.attack >= 5){ + ret.attack = calc_iv_range(false, base_stats.attack, level, evs.attack, actual_stats.attack, natures.attack); + } + if (actual_stats.defense >= 5){ + ret.defense = calc_iv_range(false, base_stats.defense, level, evs.defense, actual_stats.defense, natures.defense); + } + if (actual_stats.spatk >= 5){ + ret.spatk = calc_iv_range(false, base_stats.spatk, level, evs.spatk, actual_stats.spatk, natures.spatk); + } + if (actual_stats.spdef >= 5){ + ret.spdef = calc_iv_range(false, base_stats.spdef, level, evs.spdef, actual_stats.spdef, natures.spdef); + } + if (actual_stats.speed >= 5){ + ret.speed = calc_iv_range(false, base_stats.speed, level, evs.speed, actual_stats.speed, natures.speed); + } + + return ret; +} + + diff --git a/SerialPrograms/Source/Pokemon/Pokemon_StatsCalculation.h b/SerialPrograms/Source/Pokemon/Pokemon_StatsCalculation.h index feafa7e96..69c7be27a 100644 --- a/SerialPrograms/Source/Pokemon/Pokemon_StatsCalculation.h +++ b/SerialPrograms/Source/Pokemon/Pokemon_StatsCalculation.h @@ -8,29 +8,93 @@ #define PokemonAutomation_Pokemon_StatsCalculation_H #include +#include "Pokemon_NatureChecker.h" namespace PokemonAutomation{ namespace Pokemon{ +struct BaseStats{ + uint8_t hp; + uint8_t attack; + uint8_t defense; + uint8_t spatk; + uint8_t spdef; + uint8_t speed; +}; +struct EVs{ + uint8_t hp = 0; + uint8_t attack = 0; + uint8_t defense = 0; + uint8_t spatk = 0; + uint8_t spdef = 0; + uint8_t speed = 0; +}; + + +struct StatReads{ + int16_t hp = -1; + int16_t attack = -1; + int16_t defense = -1; + int16_t spatk = -1; + int16_t spdef = -1; + int16_t speed = -1; +}; + +struct IvRange{ + int8_t low = -1; + int8_t high = -1; +}; +struct IvRanges{ + IvRange hp; + IvRange attack; + IvRange defense; + IvRange spatk; + IvRange spdef; + IvRange speed; +}; + + enum class NatureAdjustment{ NEUTRAL, - POSITIVE, NEGATIVE, + POSITIVE, +}; +struct NatureAdjustments{ + NatureAdjustment attack; + NatureAdjustment defense; + NatureAdjustment spatk; + NatureAdjustment spdef; + NatureAdjustment speed; }; +//NatureAdjustments get_nature_adjustments(NatureCheckerValue nature); + + + uint16_t calc_stats_hp(uint8_t base_stat, uint8_t level, uint8_t iv, uint8_t ev); uint16_t calc_stats_nonhp(uint8_t base_stat, uint8_t level, uint8_t iv, uint8_t ev, NatureAdjustment nature); +#if 0 bool calc_iv_range( uint8_t& low_iv, uint8_t& high_iv, bool is_hp, uint8_t base_stat, uint8_t level, uint8_t ev, uint16_t stat, NatureAdjustment nature ); +#endif +IvRange calc_iv_range( + bool is_hp, uint8_t base_stat, uint8_t level, uint8_t ev, + uint16_t stat, NatureAdjustment nature +); +IvRanges calc_iv_ranges( + const BaseStats& base_stats, uint8_t level, const EVs& evs, + const StatReads& actual_stats, const NatureAdjustments& natures +); + } diff --git a/SerialPrograms/Source/PokemonSV/Inference/Boxes/PokemonSV_IVCheckerReader.cpp b/SerialPrograms/Source/PokemonSV/Inference/Boxes/PokemonSV_IVCheckerReader.cpp index 183a82473..6e0af2e39 100644 --- a/SerialPrograms/Source/PokemonSV/Inference/Boxes/PokemonSV_IVCheckerReader.cpp +++ b/SerialPrograms/Source/PokemonSV/Inference/Boxes/PokemonSV_IVCheckerReader.cpp @@ -21,12 +21,12 @@ const IVCheckerReader& IV_READER(){ IVCheckerReaderScope::IVCheckerReaderScope(VideoOverlay& overlay, Language language) : m_language(language) - , m_box_hp(overlay, {0.825, 0.192, 0.110, 0.052}) - , m_box_attack(overlay, {0.886, 0.302, 0.110, 0.052}) - , m_box_defense(overlay, {0.886, 0.406, 0.110, 0.052}) - , m_box_spatk(overlay, {0.660, 0.302, 0.110, 0.052}) - , m_box_spdef(overlay, {0.660, 0.406, 0.110, 0.052}) - , m_box_speed(overlay, {0.825, 0.470, 0.110, 0.052}) + , m_box_hp (overlay, {0.825, 0.192, 0.110, 0.052}) + , m_box_attack (overlay, {0.886, 0.302, 0.110, 0.052}) + , m_box_defense (overlay, {0.886, 0.406, 0.110, 0.052}) + , m_box_spatk (overlay, {0.660, 0.302, 0.110, 0.052}) + , m_box_spdef (overlay, {0.660, 0.406, 0.110, 0.052}) + , m_box_speed (overlay, {0.825, 0.470, 0.110, 0.052}) {} diff --git a/SerialPrograms/Source/PokemonSV/Inference/PokemonSV_StatHexagonReader.cpp b/SerialPrograms/Source/PokemonSV/Inference/PokemonSV_StatHexagonReader.cpp new file mode 100644 index 000000000..78547caf3 --- /dev/null +++ b/SerialPrograms/Source/PokemonSV/Inference/PokemonSV_StatHexagonReader.cpp @@ -0,0 +1,260 @@ +/* Stat Hexagon Reader + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#include "Kernels/Waterfill/Kernels_Waterfill_Session.h" +#include "CommonFramework/Exceptions/OperationFailedException.h" +#include "CommonFramework/ImageTypes/ImageViewRGB32.h" +#include "CommonFramework/ImageTypes/ImageRGB32.h" +#include "CommonFramework/ImageTools/ImageBoxes.h" +#include "CommonFramework/ImageTools/ImageStats.h" +#include "CommonFramework/ImageTools/ImageFilter.h" +#include "CommonFramework/ImageTools/BinaryImage_FilterRgb32.h" +#include "CommonFramework/OCR/OCR_NumberReader.h" +#include "PokemonSV_StatHexagonReader.h" + +//#include +//using std::cout; +//using std::endl; + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSV{ + + +StatHexagonReader::StatHexagonReader( + Color color, + const ImageFloatBox& level, + const ImageFloatBox& ball_attack, + const ImageFloatBox& ball_defense, + const ImageFloatBox& ball_spatk, + const ImageFloatBox& ball_spdef, + const ImageFloatBox& ball_speed, + const ImageFloatBox& stat_hp, + const ImageFloatBox& stat_attack, + const ImageFloatBox& stat_defense, + const ImageFloatBox& stat_spatk, + const ImageFloatBox& stat_spdef, + const ImageFloatBox& stat_speed +) + : m_color(color) + , m_level(level) + , m_ball_attack(ball_attack) + , m_ball_defense(ball_defense) + , m_ball_spatk(ball_spatk) + , m_ball_spdef(ball_spdef) + , m_ball_speed(ball_speed) + , m_stat_hp(stat_hp) + , m_stat_attack(stat_attack) + , m_stat_defense(stat_defense) + , m_stat_spatk(stat_spatk) + , m_stat_spdef(stat_spdef) + , m_stat_speed(stat_speed) +{} +void StatHexagonReader::make_overlays(VideoOverlaySet& items) const{ + items.add(m_color, m_ball_attack); + items.add(m_color, m_ball_defense); + items.add(m_color, m_ball_spatk); + items.add(m_color, m_ball_spdef); + items.add(m_color, m_ball_speed); + items.add(m_color, m_stat_hp); + items.add(m_color, m_stat_attack); + items.add(m_color, m_stat_defense); + items.add(m_color, m_stat_spatk); + items.add(m_color, m_stat_spdef); + items.add(m_color, m_stat_speed); +} +NatureAdjustment StatHexagonReader::read_stat_adjustment(ImageStats& stats, const ImageFloatBox& box, const ImageViewRGB32& screen) const{ + using namespace Kernels::Waterfill; + + ImageViewRGB32 region = extract_box_reference(screen, box); + +// cout << "--------------" << endl; + { + auto matrix = compress_rgb32_to_binary_range(region, 0xffc0c0c0, 0xffffffff); + auto session = make_WaterfillSession(matrix); + auto iter = session->make_iterator(20); + + WaterfillObject object; + while (iter->find_next(object, false)){ +// cout << object.area << " : " << object.width() << " x " << object.height() +// << ", aspect = " << object.aspect_ratio() << ", area ratio = " << object.area_ratio() << endl; + + double aspect_ratio = object.aspect_ratio(); + if (!(0.8 < aspect_ratio && aspect_ratio < 1.2)){ + continue; + } + if (object.area_ratio() < 0.65){ + continue; + } + + return NatureAdjustment::NEUTRAL; + } + } + + stats = image_stats(region); +// cout << stats.average << "," << stats.stddev << endl; + + return stats.average.r > stats.average.b + ? NatureAdjustment::POSITIVE + : NatureAdjustment::NEGATIVE; +} +NatureAdjustments StatHexagonReader::read_nature(Logger& logger, const ImageViewRGB32& screen) const{ + NatureAdjustments ret; + + std::vector> non_neutral; + + ImageStats stats; + + ret.attack = read_stat_adjustment(stats, m_ball_attack, screen); + if (ret.attack != NatureAdjustment::NEUTRAL){ + non_neutral.emplace_back(&ret.attack, stats); + } + + ret.defense = read_stat_adjustment(stats, m_ball_defense, screen); + if (ret.defense != NatureAdjustment::NEUTRAL){ + non_neutral.emplace_back(&ret.defense, stats); + } + + ret.spatk = read_stat_adjustment(stats, m_ball_spatk, screen); + if (ret.spatk != NatureAdjustment::NEUTRAL){ + non_neutral.emplace_back(&ret.spatk, stats); + } + + ret.spdef = read_stat_adjustment(stats, m_ball_spdef, screen); + if (ret.spdef != NatureAdjustment::NEUTRAL){ + non_neutral.emplace_back(&ret.spdef, stats); + } + + ret.speed = read_stat_adjustment(stats, m_ball_speed, screen); + if (ret.speed != NatureAdjustment::NEUTRAL){ + non_neutral.emplace_back(&ret.speed, stats); + } + + if (non_neutral.size() == 0){ + return ret; + } + if (non_neutral.size() != 2){ + throw OperationFailedException(ErrorReport::SEND_ERROR_REPORT, logger, "Unable to read nature."); + } + + if (*non_neutral[0].first != *non_neutral[1].first){ + return ret; + } + +// cout << non_neutral[0].second.average << non_neutral[1].second.average << endl; + + if (non_neutral[0].second.average.r > non_neutral[1].second.average.r && + non_neutral[0].second.average.b < non_neutral[1].second.average.b + ){ + *non_neutral[0].first = NatureAdjustment::POSITIVE; + *non_neutral[1].first = NatureAdjustment::NEGATIVE; + return ret; + } + if (non_neutral[0].second.average.r < non_neutral[1].second.average.r && + non_neutral[0].second.average.b > non_neutral[1].second.average.b + ){ + *non_neutral[0].first = NatureAdjustment::NEGATIVE; + *non_neutral[1].first = NatureAdjustment::POSITIVE; + return ret; + } + + throw OperationFailedException(ErrorReport::SEND_ERROR_REPORT, logger, "Unable to read nature."); +} + + +int8_t StatHexagonReader::read_level(Logger& logger, const ImageViewRGB32& screen) const{ + ImageViewRGB32 region = extract_box_reference(screen, m_level); + ImageRGB32 filtered = to_blackwhite_rgb32_range(region, 0xff808080, 0xffffffff, true); + + int number = OCR::read_number(logger, filtered); + if (number < 0 || number > 100){ + number = -1; + } + return (int8_t)number; +} +int16_t StatHexagonReader::read_stat(Logger& logger, const ImageFloatBox& box, const ImageViewRGB32& screen) const{ + ImageViewRGB32 region = extract_box_reference(screen, box); + ImageRGB32 filtered = to_blackwhite_rgb32_range(region, 0xff808080, 0xffffffff, true); + + int number = OCR::read_number(logger, filtered); + if (number < 5 || number > 999){ + number = -1; + } + return (int16_t)number; +} +StatReads StatHexagonReader::read_stats(Logger& logger, const ImageViewRGB32& screen) const{ + StatReads ret; + + // TODO: Handle the slash. (99 / 100) + ret.hp = read_stat(logger, m_stat_hp, screen); + + ret.attack = read_stat(logger, m_stat_attack, screen); + ret.defense = read_stat(logger, m_stat_defense, screen); + ret.spatk = read_stat(logger, m_stat_spatk, screen); + ret.spdef = read_stat(logger, m_stat_spdef, screen); + ret.speed = read_stat(logger, m_stat_speed, screen); + return ret; +} +IvRanges StatHexagonReader::calc_ivs( + Logger& logger, const ImageViewRGB32& screen, + const BaseStats& base_stats, const EVs& evs +) const{ + int16_t level = read_level(logger, screen); + + NatureAdjustments nature = read_nature(logger, screen); + StatReads stats = read_stats(logger, screen); + return calc_iv_ranges(base_stats, (uint8_t)level, evs, stats, nature); +} + + + +#if 0 +BoxStatsReader::BoxStatsReader(Color color) + : StatHexagonReader( + color, +// {0.823, 0.244, 0.012, 0.022}, + {0.875, 0.294, 0.012, 0.022}, + {0.875, 0.402, 0.012, 0.022}, + {0.771, 0.294, 0.012, 0.022}, + {0.771, 0.402, 0.012, 0.022}, + {0.823, 0.453, 0.012, 0.022}, + {0.828, 0.198, 0.080, 0.045}, + {0.890, 0.408, 0.050, 0.045}, + {0.890, 0.198, 0.050, 0.045}, + {0.720, 0.408, 0.050, 0.045}, + {0.720, 0.198, 0.050, 0.045}, + {0.828, 0.475, 0.050, 0.045} + ) +{} +#endif + +SummaryStatsReader::SummaryStatsReader(Color color) + : StatHexagonReader( + color, + {0.125, 0.860, 0.050, 0.044}, +// {0.755, 0.253, 0.012, 0.022}, + {0.834, 0.335, 0.012, 0.022}, + {0.834, 0.483, 0.012, 0.022}, + {0.677, 0.335, 0.012, 0.022}, + {0.677, 0.483, 0.012, 0.022}, + {0.755, 0.565, 0.012, 0.022}, + {0.767, 0.205, 0.040, 0.050}, + {0.848, 0.340, 0.040, 0.050}, + {0.848, 0.520, 0.040, 0.050}, + {0.637, 0.340, 0.040, 0.050}, + {0.637, 0.520, 0.040, 0.050}, + {0.742, 0.620, 0.040, 0.050} + ) +{} + + + + + + +} +} +} diff --git a/SerialPrograms/Source/PokemonSV/Inference/PokemonSV_StatHexagonReader.h b/SerialPrograms/Source/PokemonSV/Inference/PokemonSV_StatHexagonReader.h new file mode 100644 index 000000000..20981a3dd --- /dev/null +++ b/SerialPrograms/Source/PokemonSV/Inference/PokemonSV_StatHexagonReader.h @@ -0,0 +1,98 @@ +/* Stat Hexagon Reader + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#ifndef PokemonAutomation_PokemonSV_StatHexagonReader_H +#define PokemonAutomation_PokemonSV_StatHexagonReader_H + +#include "CommonFramework/VideoPipeline/VideoOverlayScopes.h" +#include "Pokemon/Pokemon_StatsCalculation.h" +#include "Pokemon/Pokemon_NatureChecker.h" + +namespace PokemonAutomation{ + +class Logger; +struct ImageStats; + +namespace NintendoSwitch{ +namespace PokemonSV{ + +using namespace Pokemon; + + + +class StatHexagonReader{ +public: + StatHexagonReader( + Color color, + const ImageFloatBox& level, + const ImageFloatBox& ball_attack, + const ImageFloatBox& ball_defense, + const ImageFloatBox& ball_spatk, + const ImageFloatBox& ball_spdef, + const ImageFloatBox& ball_speed, + const ImageFloatBox& stat_hp, + const ImageFloatBox& stat_attack, + const ImageFloatBox& stat_defense, + const ImageFloatBox& stat_spatk, + const ImageFloatBox& stat_spdef, + const ImageFloatBox& stat_speed + ); + + void make_overlays(VideoOverlaySet& items) const; + + NatureAdjustments read_nature(Logger& logger, const ImageViewRGB32& screen) const; + int8_t read_level(Logger& logger, const ImageViewRGB32& screen) const; + StatReads read_stats(Logger& logger, const ImageViewRGB32& screen) const; + IvRanges calc_ivs( + Logger& logger, const ImageViewRGB32& screen, + const BaseStats& base_stats, const EVs& evs = EVs() + ) const; + + +private: + NatureAdjustment read_stat_adjustment(ImageStats& stats, const ImageFloatBox& box, const ImageViewRGB32& screen) const; + int16_t read_stat(Logger& logger, const ImageFloatBox& box, const ImageViewRGB32& screen) const; + + +private: + Color m_color; + + ImageFloatBox m_level; + +// ImageFloatBox m_ball_hp; + ImageFloatBox m_ball_attack; + ImageFloatBox m_ball_defense; + ImageFloatBox m_ball_spatk; + ImageFloatBox m_ball_spdef; + ImageFloatBox m_ball_speed; + + ImageFloatBox m_stat_hp; + ImageFloatBox m_stat_attack; + ImageFloatBox m_stat_defense; + ImageFloatBox m_stat_spatk; + ImageFloatBox m_stat_spdef; + ImageFloatBox m_stat_speed; +}; + + +#if 0 // This doesn't work due to HP and level reads being variable position. +class BoxStatsReader : public StatHexagonReader{ +public: + BoxStatsReader(Color color = COLOR_RED); +}; +#endif + +class SummaryStatsReader : public StatHexagonReader{ +public: + SummaryStatsReader(Color color = COLOR_RED); +}; + + + +} +} +} +#endif diff --git a/SerialPrograms/Source/PokemonSV/Programs/Farming/PokemonSV_TournamentFarmer.cpp b/SerialPrograms/Source/PokemonSV/Programs/Farming/PokemonSV_TournamentFarmer.cpp index 9f1d7c380..9acd85a08 100644 --- a/SerialPrograms/Source/PokemonSV/Programs/Farming/PokemonSV_TournamentFarmer.cpp +++ b/SerialPrograms/Source/PokemonSV/Programs/Farming/PokemonSV_TournamentFarmer.cpp @@ -111,7 +111,7 @@ TournamentFarmer::TournamentFarmer() , SAVE_NUM_ROUNDS( "Save every this many tournaments:
Zero disables saving. Will save win or lose.", LockWhileRunning::UNLOCKED, - 0, 0 + 1, 0 ) , MONEY_LIMIT( "Stop after earning this amount of money:
Zero disables this check. Does not count losses. In-game maximum is 9,999,999. This can be set up to 999,999,999.",