Skip to content

Commit a56384c

Browse files
author
Gin
committed
add alpha to home box sort
1 parent c01b1fa commit a56384c

File tree

4 files changed

+73
-53
lines changed

4 files changed

+73
-53
lines changed

SerialPrograms/Source/PokemonHome/Inference/PokemonHome_BoxGenderDetector.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ class BoxGenderDetector{
2222
public:
2323
static void make_overlays(VideoOverlaySet& items);
2424

25+
// Return StatsHuntGenderFilter::Male, Female or Genderless
2526
static Pokemon::StatsHuntGenderFilter detect(const ImageViewRGB32& screen);
2627
};
2728

SerialPrograms/Source/PokemonHome/Options/PokemonHome_BoxSortingTable.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const EnumDropdownDatabase<BoxSortingSortType>& BallType_Database(){
1616
{BoxSortingSortType::NationalDexNo, "dex", "National Dex Number"},
1717
{BoxSortingSortType::Shiny, "shiny", "Shiny"},
1818
{BoxSortingSortType::Gigantamax, "gigantamax", "Gigantamax"},
19+
{BoxSortingSortType::Alpha, "alpha", "Alpha"},
1920
{BoxSortingSortType::Ball_Slug, "ball_slug", "Ball Type"},
2021
{BoxSortingSortType::Gender, "gender", "Gender (Male, Female, Genderless)"},
2122
});

SerialPrograms/Source/PokemonHome/Options/PokemonHome_BoxSortingTable.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ enum class BoxSortingSortType
2121
NationalDexNo,
2222
Shiny,
2323
Gigantamax,
24+
Alpha,
2425
Ball_Slug,
2526
Gender,
2627
};

SerialPrograms/Source/PokemonHome/Programs/PokemonHome_BoxSorting.cpp

Lines changed: 70 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -93,17 +93,17 @@ std::unique_ptr<StatsTracker> BoxSorting_Descriptor::make_stats() const{
9393

9494
BoxSorting::BoxSorting()
9595
: BOX_NUMBER(
96-
"<b>Number of boxes to order:</b>",
96+
"<b>Number of Boxes to Sort:</b>",
9797
LockMode::LOCK_WHILE_RUNNING,
9898
1, 1, MAX_BOXES
9999
)
100100
, VIDEO_DELAY(
101-
"<b>Delay of your capture card:</b>",
101+
"<b>Capture Card Delay:</b>",
102102
LockMode::LOCK_WHILE_RUNNING,
103103
50
104104
)
105105
, GAME_DELAY(
106-
"<b>Delay of your Pokemon Home app:</b>",
106+
"<b>" + STRING_POKEMON + " Home App Delay:</b>",
107107
LockMode::LOCK_WHILE_RUNNING,
108108
30
109109
)
@@ -112,13 +112,13 @@ BoxSorting::BoxSorting()
112112
)
113113
, OUTPUT_FILE(
114114
false,
115-
"<b>Output File:</b><br>JSON file for output of storage boxes.",
115+
"<b>Output File:</b><br>JSON file basename to catalogue box info found by the program.",
116116
LockMode::LOCK_WHILE_RUNNING,
117117
"box_order",
118118
"box_order"
119119
)
120120
, DRY_RUN(
121-
"<b>Dry Run:</b><br>Catalogue and make sort plan without executing. (Will output to OUTPUT_FILE and OUTPUT_FILE.sortplan)",
121+
"<b>Dry Run:</b><br>Catalogue and make sorting plan without execution. Check output at <output_file>.json and <output_file>-sorted.json)",
122122
LockMode::LOCK_WHILE_RUNNING,
123123
false
124124
)
@@ -174,75 +174,84 @@ struct Pokemon{
174174
uint16_t national_dex_number = 0;
175175
bool shiny = false;
176176
bool gmax = false;
177+
bool alpha = false;
177178
std::string ball_slug = "";
178179
StatsHuntGenderFilter gender = StatsHuntGenderFilter::Genderless;
179-
uint32_t ot_id = 0;
180+
uint32_t ot_id = 0; // original trainer ID
180181
};
181182

182183
bool operator==(const Pokemon& lhs, const Pokemon& rhs){
183184
// NOTE edit when adding new struct members
184185
return lhs.national_dex_number == rhs.national_dex_number &&
185186
lhs.shiny == rhs.shiny &&
186187
lhs.gmax == rhs.gmax &&
188+
lhs.alpha == rhs.alpha &&
187189
lhs.ball_slug == rhs.ball_slug &&
188190
lhs.gender == rhs.gender &&
189191
lhs.ot_id == rhs.ot_id;
190192
}
191193

194+
// Sort two pokemon slot. "Smaller" pokemon is placed closer to front.
195+
// If a slot is empty, it is always larger than non-empty slot so all the empty slots are at end after sorting.
196+
// For two pokemon, check each preference rule. If we cannot determine the order based on the first rule, go
197+
// to the second rule, and so on.
198+
// Available rules:
199+
// - National dex number: smaller ID should be at front.
200+
// - Shiny: shiny pokemon should be at front.
201+
// - Gigantamax: Gigantamax pokemon should be at front.
202+
// - Alpha: Alpha pokemon should be at front.
203+
// - Ball slug: pokemon with a ball slug that's smaller in the alphabetical order should be at front.
204+
// - Gender: Male should be at front, then comes female, and genderless is last
205+
// Each rule type also has a "reverse" which if true, reverses above ordering for that rule type.
206+
// If user does not give a preference ruleset, sort by national dex number.
192207
bool operator<(const std::optional<Pokemon>& lhs, const std::optional<Pokemon>& rhs){
193-
if (!lhs.has_value()){
208+
if (!lhs.has_value()){ // lhs is empty
194209
return false;
195210
}
196-
if (!rhs.has_value()){
211+
if (!rhs.has_value()){ // lhs not empty but rhs is empty
197212
return true;
198213
}
199-
214+
// both sides are not empty:
200215
for (const BoxSortingSelection preference : *lhs->preferences){
201-
std::optional<bool> ret{};
202216
switch(preference.sort_type){
203217
// NOTE edit when adding new struct members
204218
case BoxSortingSortType::NationalDexNo:
205219
if (lhs->national_dex_number != rhs->national_dex_number){
206-
ret = lhs->national_dex_number < rhs->national_dex_number;
220+
// we use (boolean != reverse) to apply the reverse effect
221+
return (lhs->national_dex_number < rhs->national_dex_number) != preference.reverse;
207222
}
208223
break;
209224
case BoxSortingSortType::Shiny:
210225
if (lhs->shiny != rhs->shiny){
211-
ret = lhs->shiny;
226+
return lhs->shiny != preference.reverse;
212227
}
213228
break;
214229
case BoxSortingSortType::Gigantamax:
215230
if (lhs->gmax != rhs->gmax){
216-
ret = lhs->gmax;
231+
return lhs->gmax != preference.reverse;
217232
}
218233
break;
219-
case BoxSortingSortType::Ball_Slug:
220-
if (lhs->ball_slug < rhs->ball_slug){
221-
ret = true;
234+
case BoxSortingSortType::Alpha:
235+
if (lhs->alpha != rhs->alpha){
236+
return lhs->gmax != preference.reverse;
222237
}
223-
if (lhs->ball_slug > rhs->ball_slug){
224-
ret = false;
238+
break;
239+
case BoxSortingSortType::Ball_Slug:
240+
if (lhs->ball_slug != rhs->ball_slug){
241+
return (lhs->ball_slug < rhs->ball_slug) != preference.reverse;
225242
}
226243
break;
227244
case BoxSortingSortType::Gender:
228-
if (lhs->gender < rhs->gender){
229-
ret = true;
230-
}
231-
if (lhs->gender > rhs->gender){
232-
ret = false;
245+
if (lhs->gender != rhs->gender){
246+
return (lhs->gender < rhs->gender) != preference.reverse;
233247
}
234248
break;
235-
}
236-
if (ret.has_value()){
237-
bool value = *ret;
238-
if (preference.reverse){
239-
return !value;
240-
}else{
241-
return value;
242-
}
243-
}
244-
}
249+
default:
250+
throw InternalProgramError(nullptr, PA_CURRENT_FUNCTION, "unknown BoxSortingSortType");
251+
} // end switch
252+
} // end for preference
245253

254+
// Default to sort by national dex number
246255
return lhs->national_dex_number < rhs->national_dex_number;
247256
}
248257

@@ -254,6 +263,7 @@ std::ostream& operator<<(std::ostream& os, const std::optional<Pokemon>& pokemon
254263
os << "national_dex_number:" << pokemon->national_dex_number << " ";
255264
os << "shiny:" << (pokemon->shiny ? "true" : "false") << " ";
256265
os << "gmax:" << (pokemon->gmax ? "true" : "false") << " ";
266+
os << "alpha:" << (pokemon->alpha ? "true" : "false") << " ";
257267
os << "ball_slug:" << pokemon->ball_slug << " ";
258268
os << "gender:" << gender_to_string(pokemon->gender) << " ";
259269
os << "ot_id:" << pokemon->ot_id << " ";
@@ -382,7 +392,7 @@ void print_boxes_data(const std::vector<std::optional<Pokemon>>& boxes_data, Sin
382392
env.console.log(ss.str());
383393
}
384394

385-
void output_boxes_data_json(const std::vector<std::optional<Pokemon>>& boxes_data, const std::string& json_path){
395+
void output_boxes_data_json(const std::vector<std::optional<Pokemon>>& boxes_data, const std::string& json_path_basename){
386396
JsonArray pokemon_data;
387397
for (size_t poke_nb = 0; poke_nb < boxes_data.size(); poke_nb++){
388398
Cursor cursor = get_cursor(poke_nb);
@@ -396,13 +406,14 @@ void output_boxes_data_json(const std::vector<std::optional<Pokemon>>& boxes_dat
396406
pokemon["national_dex_number"] = current_pokemon->national_dex_number;
397407
pokemon["shiny"] = current_pokemon->shiny;
398408
pokemon["gmax"] = current_pokemon->gmax;
409+
pokemon["alpha"] = current_pokemon->alpha;
399410
pokemon["ball_slug"] = current_pokemon->ball_slug;
400411
pokemon["gender"] = gender_to_string(current_pokemon->gender);
401412
pokemon["ot_id"] = current_pokemon->ot_id;
402413
}
403414
pokemon_data.push_back(std::move(pokemon));
404415
}
405-
pokemon_data.dump(json_path + ".json");
416+
pokemon_data.dump(json_path_basename + ".json");
406417
}
407418

408419
void do_sort(
@@ -485,7 +496,7 @@ void BoxSorting::program(SingleSwitchProgramEnvironment& env, ProControllerConte
485496
ImageFloatBox ot_box(0.492, 0.719, 0.165, 0.049); // OT
486497
ImageFloatBox nature_box(0.157, 0.783, 0.212, 0.042); // Nature
487498
ImageFloatBox ability_box(0.158, 0.838, 0.213, 0.042); // Ability
488-
// TODO: add alpha detection
499+
ImageFloatBox alpha_box(0.787, 0.095, 0.024, 0.046); // Alpha symbol
489500

490501

491502
// vector that will store data for each slot
@@ -631,41 +642,47 @@ void BoxSorting::program(SingleSwitchProgramEnvironment& env, ProControllerConte
631642
//cycle through each summary of the current box and fill pokemon information
632643
for (size_t row = 0; row < MAX_ROWS; row++){
633644
for (size_t column = 0; column < MAX_COLUMNS; column++){
634-
if (!boxes_data[get_index(box_idx, row, column)].has_value()){
645+
const size_t global_idx = get_index(box_idx, row, column);
646+
if (!boxes_data[global_idx].has_value()){
635647
continue;
636648
}
637649

638650
screen = env.console.video().snapshot();
639651

640-
int national_dex_number = OCR::read_number_waterfill(env.console, extract_box_reference(screen, national_dex_number_box), 0xff808080, 0xffffffff);
652+
const int national_dex_number = OCR::read_number_waterfill(env.console, extract_box_reference(screen, national_dex_number_box), 0xff808080, 0xffffffff);
641653
if (national_dex_number <= 0 || national_dex_number > 1025) { // Current last pokemon is pecharunt
642654
dump_image(env.console, ProgramInfo(), "ReadSummary_national_dex_number", screen);
643655
}
644-
boxes_data[get_index(box_idx, row, column)]->national_dex_number = (uint16_t)national_dex_number;
656+
boxes_data[global_idx]->national_dex_number = (uint16_t)national_dex_number;
645657

646-
int shiny_stddev_value = (int)image_stddev(extract_box_reference(screen, shiny_symbol_box)).sum();
647-
bool is_shiny = shiny_stddev_value > 30;
648-
boxes_data[get_index(box_idx, row, column)]->shiny = is_shiny;
658+
const int shiny_stddev_value = (int)image_stddev(extract_box_reference(screen, shiny_symbol_box)).sum();
659+
const bool is_shiny = shiny_stddev_value > 30;
660+
boxes_data[global_idx]->shiny = is_shiny;
649661
env.console.log("Shiny detection stddev:" + std::to_string(shiny_stddev_value) + " is shiny:" + std::to_string(is_shiny));
650662

651-
int gmax_stddev_value = (int)image_stddev(extract_box_reference(screen, gmax_symbol_box)).sum();
652-
bool is_gmax = gmax_stddev_value > 30;
653-
boxes_data[get_index(box_idx, row, column)]->gmax = is_gmax;
663+
const int gmax_stddev_value = (int)image_stddev(extract_box_reference(screen, gmax_symbol_box)).sum();
664+
const bool is_gmax = gmax_stddev_value > 30;
665+
boxes_data[global_idx]->gmax = is_gmax;
654666
env.console.log("Gmax detection stddev:" + std::to_string(gmax_stddev_value) + " is gmax:" + std::to_string(is_gmax));
655667

668+
const int alpha_stddev_value = (int)image_stddev(extract_box_reference(screen, alpha_box)).sum();
669+
const bool is_alpha = alpha_stddev_value > 40;
670+
boxes_data[global_idx]->alpha = is_alpha;
671+
env.console.log("Alpha detection stddev:" + std::to_string(alpha_stddev_value) + " is alpha:" + std::to_string(is_alpha));
672+
656673
BallReader ball_reader(env.console);
657-
boxes_data[get_index(box_idx, row, column)]->ball_slug = ball_reader.read_ball(screen);
674+
boxes_data[global_idx]->ball_slug = ball_reader.read_ball(screen);
658675

659676
BoxGenderDetector::make_overlays(box_render);
660-
StatsHuntGenderFilter gender = BoxGenderDetector::detect(screen);
677+
const StatsHuntGenderFilter gender = BoxGenderDetector::detect(screen);
661678
env.console.log("Gender: " + gender_to_string(gender), COLOR_GREEN);
662-
boxes_data[get_index(box_idx, row, column)]->gender = gender;
679+
boxes_data[global_idx]->gender = gender;
663680

664-
int ot_id = OCR::read_number_waterfill(env.console, extract_box_reference(screen, ot_id_box), 0xff808080, 0xffffffff);
681+
const int ot_id = OCR::read_number_waterfill(env.console, extract_box_reference(screen, ot_id_box), 0xff808080, 0xffffffff);
665682
if (ot_id < 0 || ot_id > 999'999) {
666683
dump_image(env.console, ProgramInfo(), "ReadSummary_OT", screen);
667684
}
668-
boxes_data[get_index(box_idx, row, column)]->ot_id = ot_id;
685+
boxes_data[global_idx]->ot_id = ot_id;
669686

670687
const std::string& species_slug = NATIONAL_DEX_SLUGS()[national_dex_number-1];
671688
const std::string& display_name = get_pokemon_name(species_slug).display_name();
@@ -719,12 +736,12 @@ void BoxSorting::program(SingleSwitchProgramEnvironment& env, ProControllerConte
719736

720737
env.console.log("Current boxes data :");
721738
print_boxes_data(boxes_data, env);
722-
const std::string json_path = OUTPUT_FILE;
723-
output_boxes_data_json(boxes_data, json_path);
739+
const std::string json_path_basename = OUTPUT_FILE;
740+
output_boxes_data_json(boxes_data, json_path_basename);
724741

725742
env.console.log("Sorted boxes data :");
726743
print_boxes_data(boxes_sorted, env);
727-
const std::string sorted_path = json_path + "-sorted";
744+
const std::string sorted_path = json_path_basename + "-sorted";
728745
output_boxes_data_json(boxes_sorted, sorted_path);
729746

730747
if (!DRY_RUN){

0 commit comments

Comments
 (0)