Skip to content

Commit 4e8bcae

Browse files
author
Gin
committed
clean box sorter
1 parent 99e0d18 commit 4e8bcae

File tree

6 files changed

+113
-66
lines changed

6 files changed

+113
-66
lines changed

SerialPrograms/Source/NintendoSwitch/NintendoSwitch_SingleSwitchProgram.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ namespace PokemonAutomation{
2020
namespace NintendoSwitch{
2121

2222

23+
void SingleSwitchProgramEnvironment::add_overlay_log(std::string msg, Color color){
24+
console.overlay().add_log(std::move(msg), color);
25+
}
26+
2327

2428
SingleSwitchProgramDescriptor::SingleSwitchProgramDescriptor(
2529
std::string identifier,

SerialPrograms/Source/NintendoSwitch/NintendoSwitch_SingleSwitchProgram.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ class SingleSwitchProgramEnvironment : public ProgramEnvironment{
2727
public:
2828
ConsoleHandle console;
2929

30+
// Call console.overlay().add_log(msg, color) to add a log message to overlay display
31+
void add_overlay_log(std::string msg, Color color = COLOR_WHITE);
32+
3033
private:
3134
friend class SingleSwitchProgramSession;
3235
friend class SingleSwitchProgramWidget;

SerialPrograms/Source/Pokemon/Resources/Pokemon_PokemonNames.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ namespace Pokemon{
1717
const std::string PokemonNames::NULL_SLUG;
1818

1919

20+
// A database of pokemon names. Each name corresponds to one national dex number.
21+
// This database does not differentiate any form differences. Its size equals the highest available national
22+
// dex number.
2023
struct PokemonNameDatabase{
2124
PokemonNameDatabase();
2225
static const PokemonNameDatabase& instance(){

SerialPrograms/Source/Pokemon/Resources/Pokemon_PokemonNames.h

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ namespace PokemonAutomation{
1616
namespace Pokemon{
1717

1818

19+
// Store a pokemon's display name for a pokemon species.
20+
// It does not differentiate any form differences. The total different PokemonNames
21+
// equals the highest available national dex number.
1922
class PokemonNames{
2023
public:
2124
const std::string& display_name() const{ return m_display_name; }
@@ -31,14 +34,30 @@ class PokemonNames{
3134
};
3235

3336

37+
// From pokemon slug to its display name.
38+
// The slug is for pokemon species. Each slug corresponds to one national dex number.
39+
// Throw InternalProgramError if the given slug is not found in the database.
3440
const PokemonNames& get_pokemon_name(const std::string& slug);
41+
// From pokemon slug to its display name.
42+
// The slug is for pokemon species. Each slug corresponds to one national dex number.
43+
// Return nullptr if the given slug is not found in the database.
3544
const PokemonNames* get_pokemon_name_nothrow(const std::string& slug);
45+
// From a pokemon English display name to its slug.
46+
// The display name is for pokemon species. Each name corresponds to one national dex number.
47+
// Throw InternalProgramError if the given name is not found in the database.
3648
const std::string& parse_pokemon_name(const std::string& display_name);
49+
// From a pokemon English display name to its slug.
50+
// The display name is for pokemon species. Each name corresponds to one national dex number.
51+
// Return nullptr if the given name is not found in the database.
3752
const std::string& parse_pokemon_name_nothrow(const std::string& display_name);
3853

54+
// Get max national dex number from the database.
55+
size_t max_national_dex_number();
56+
// Get pokemon slug from national dex number. Throw InternalProgramError if no such dex number.
57+
const std::string& get_pokemon_from_national_dex_number(size_t national_id);
3958

4059

41-
// Load a list of pokemon name slugs from a json file.
60+
// Load a list of pokemon name slugs from a json file. May throw file error.
4261
// json_path: the path relative to the resource root, accessed via
4362
// RESOURCE_PATH() declared in CommonFramework/Globals.h.
4463
std::vector<std::string> load_pokemon_slug_json_list(const char* json_path);

SerialPrograms/Source/Pokemon/Resources/Pokemon_PokemonSlugs.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,13 @@ namespace PokemonAutomation{
1616
namespace Pokemon{
1717

1818

19+
// Get a set of all pokemon slugs.
20+
// Each slug corresponds to one natioanl dex number.
1921
const std::set<std::string>& ALL_POKEMON_SLUGS();
22+
// Get a list of pokemon slugs in the order of national dex.
23+
// Each slug corresponds to one natioanl dex number.
2024
const std::vector<std::string>& NATIONAL_DEX_SLUGS();
25+
// Get a one-to-one mapping from pokemon slugs to their national dex numbers.
2126
const std::map<std::string, size_t>& SLUGS_TO_NATIONAL_DEX();
2227

2328

SerialPrograms/Source/PokemonHome/Programs/PokemonHome_BoxSorting.cpp

Lines changed: 78 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ language
4141
#include "CommonTools/StartupChecks/StartProgramChecks.h"
4242
#include "NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h"
4343
#include "Pokemon/Pokemon_Strings.h"
44+
#include "Pokemon/Resources/Pokemon_PokemonNames.h"
45+
#include "Pokemon/Resources/Pokemon_PokemonSlugs.h"
4446
#include "PokemonHome/Inference/PokemonHome_BoxGenderDetector.h"
4547
#include "PokemonHome/Inference/PokemonHome_BallReader.h"
4648
#include "PokemonHome_BoxSorting.h"
@@ -261,28 +263,28 @@ std::ostream& operator<<(std::ostream& os, const std::optional<Pokemon>& pokemon
261263
return os;
262264
}
263265

266+
// Move the red cursor to the first slot of the box
267+
// If the cursor is not add the first slot, move the cursor to the left and up one row at a time until it is at the first slot.
264268
bool go_to_first_slot(SingleSwitchProgramEnvironment& env, ProControllerContext& context, uint16_t VIDEO_DELAY){
265-
266-
ImageFloatBox cursor_check(0.07, 0.15, 0.01, 0.01); //cursor position of the first slot of the box
269+
ImageFloatBox first_slot_cursor_box(0.07, 0.15, 0.01, 0.01); //cursor position of the first slot of the box
267270
VideoSnapshot screen = env.console.video().snapshot();
268-
FloatPixel image_value = image_stats(extract_box_reference(screen, cursor_check)).average;
269-
env.console.log("Cursor color detection: " + image_value.to_string());
271+
FloatPixel first_slot_cursor_color = image_stats(extract_box_reference(screen, first_slot_cursor_box)).average;
272+
env.console.log("Cursor color detection: " + first_slot_cursor_color.to_string());
270273
VideoOverlaySet BoxRender(env.console);
274+
BoxRender.add(COLOR_BLUE, first_slot_cursor_box);
271275

272-
BoxRender.add(COLOR_BLUE, cursor_check);
273-
if(image_value.r <= image_value.g + image_value.b){
274-
276+
// If the cursor is not at the first slot
277+
if(first_slot_cursor_color.r <= first_slot_cursor_color.g + first_slot_cursor_color.b){
275278
bool cursor_found = false;
276-
277279
for (uint8_t rows = 0; rows < 7; rows++){
278280
for (uint8_t column = 0; column < 5; column++){
279281
pbf_press_dpad(context, DPAD_LEFT, 10, VIDEO_DELAY);
280282
context.wait_for_all_requests();
281283
screen = env.console.video().snapshot();
282-
image_value = image_stats(extract_box_reference(screen, cursor_check)).average;
283-
env.console.log("Cursor color detection: " + image_value.to_string());
284+
first_slot_cursor_color = image_stats(extract_box_reference(screen, first_slot_cursor_box)).average;
285+
env.console.log("Cursor color detection: " + first_slot_cursor_color.to_string());
284286

285-
if(image_value.r > image_value.g + image_value.b){
287+
if(first_slot_cursor_color.r > first_slot_cursor_color.g + first_slot_cursor_color.b){
286288
cursor_found = true;
287289
break;
288290
}
@@ -291,10 +293,10 @@ bool go_to_first_slot(SingleSwitchProgramEnvironment& env, ProControllerContext&
291293
pbf_press_dpad(context, DPAD_UP, 10, VIDEO_DELAY);
292294
context.wait_for_all_requests();
293295
screen = env.console.video().snapshot();
294-
image_value = image_stats(extract_box_reference(screen, cursor_check)).average;
295-
env.console.log("Cursor color detection: " + image_value.to_string());
296+
first_slot_cursor_color = image_stats(extract_box_reference(screen, first_slot_cursor_box)).average;
297+
env.console.log("Cursor color detection: " + first_slot_cursor_color.to_string());
296298

297-
if(image_value.r > image_value.g + image_value.b){
299+
if(first_slot_cursor_color.r > first_slot_cursor_color.g + first_slot_cursor_color.b){
298300
cursor_found = true;
299301
break;
300302
}
@@ -410,7 +412,10 @@ void do_sort(
410412
BoxSorting_Descriptor::Stats& stats,
411413
Cursor& cur_cursor,
412414
uint16_t GAME_DELAY
413-
){
415+
){
416+
env.log("Start sorting...");
417+
env.add_overlay_log("Start Sorting...");
418+
414419
std::ostringstream ss;
415420
// this need to be separated into functions when I will redo the whole thing but I just wanted it to work
416421

@@ -528,38 +533,38 @@ void BoxSorting::program(SingleSwitchProgramEnvironment& env, ProControllerConte
528533
box_render.clear();
529534

530535
Cursor dest_cursor;
531-
std::vector<size_t> first_poke_slot;
532536
Cursor nav_cursor = {0, 0, 0};
533-
bool find_first_poke;
534537

535538
//cycle through each box
536-
for (size_t box_nb = 0; box_nb < BOX_NUMBER; box_nb++){
537-
538-
if(box_nb != 0){
539+
for (size_t box_idx = 0; box_idx < BOX_NUMBER; box_idx++){
540+
if(box_idx != 0){
541+
// Press button R to move to next box
539542
pbf_press_button(context, BUTTON_R, 10, VIDEO_DELAY+100);
540-
context.wait_for_all_requests();
541543
}else{
542544
// Moving the cursor until it goes to the first slot
543545
if(!go_to_first_slot(env, context, VIDEO_DELAY)){
544546
env.console.log("ERROR: Could not move cursor to the first slot, please consider adjusting delay\n", COLOR_RED);
545547
return;
546548
}
547-
context.wait_for_all_requests();
548549
}
549550

551+
context.wait_for_all_requests();
552+
const std::string log_msg = "Checking box " + std::to_string(box_idx+1) + "/" + std::to_string(BOX_NUMBER);
553+
env.log(log_msg);
554+
env.console.overlay().add_log(log_msg);
555+
550556
screen = env.console.video().snapshot();
551557

552558
// Box grid to find empty slots (red boxes) and fill boxes_data with value to check or not for pokemon dex number
553559

554560
ss << "\n";
555561

556-
first_poke_slot = {0, 0};
557-
find_first_poke = false;
562+
std::array<size_t, 2> first_poke_slot = {0, 0};
563+
bool find_first_poke = false;
558564

565+
int num_empty_slots = 0;
559566
for (size_t row = 0; row < MAX_ROWS; row++){
560-
561567
for (size_t column = 0; column < MAX_COLUMNS; column++){
562-
563568
ImageFloatBox slot_box(0.06 + (0.072 * column), 0.2 + (0.1035 * row), 0.03, 0.057);
564569
int current_box_value = (int)image_stddev(extract_box_reference(screen, slot_box)).sum();
565570

@@ -569,6 +574,7 @@ void BoxSorting::program(SingleSwitchProgramEnvironment& env, ProControllerConte
569574
if(current_box_value < 10){
570575
box_render.add(COLOR_RED, slot_box);
571576
stats.empty++;
577+
num_empty_slots++;
572578
env.update_stats();
573579
boxes_data.push_back(std::nullopt); //empty optional to make sorting easier later
574580
ss << "\u274c " ; // "X"
@@ -590,16 +596,18 @@ void BoxSorting::program(SingleSwitchProgramEnvironment& env, ProControllerConte
590596
}
591597
ss << "\n";
592598
}
593-
594599
env.console.log(ss.str());
600+
env.add_overlay_log("Empty: " + std::to_string(num_empty_slots) + "/30");
595601
ss.str("");
596602

597603
// moving cursor to the first pokemon slot
598604
dest_cursor = {0, first_poke_slot[1], first_poke_slot[0]};
599605
nav_cursor = move_cursor_to(env, context, nav_cursor, dest_cursor, GAME_DELAY);
600606

601-
//enter the summary screen
602-
if (find_first_poke == true){
607+
//enter the summary screen to read pokemon info
608+
if (find_first_poke){
609+
env.add_overlay_log("Checking Summary...");
610+
603611
pbf_press_button(context, BUTTON_A, 10, GAME_DELAY);
604612
context.wait_for_all_requests();
605613
box_render.clear();
@@ -621,50 +629,55 @@ void BoxSorting::program(SingleSwitchProgramEnvironment& env, ProControllerConte
621629
//cycle through each summary of the current box and fill pokemon information
622630
for (size_t row = 0; row < MAX_ROWS; row++){
623631
for (size_t column = 0; column < MAX_COLUMNS; column++){
632+
if (!boxes_data[get_index(box_idx, row, column)].has_value()){
633+
continue;
634+
}
624635

625-
if (boxes_data[get_index(box_nb, row, column)].has_value()){
626-
screen = env.console.video().snapshot();
636+
screen = env.console.video().snapshot();
627637

628-
int national_dex_number = OCR::read_number_waterfill(env.console, extract_box_reference(screen, national_dex_number_box), 0xff808080, 0xffffffff);
629-
if (national_dex_number < 0 || national_dex_number > 1025) {
630-
dump_image(env.console, ProgramInfo(), "ReadSummary_national_dex_number", screen);
631-
}
632-
boxes_data[get_index(box_nb, row, column)]->national_dex_number = (uint16_t)national_dex_number;
638+
int national_dex_number = OCR::read_number_waterfill(env.console, extract_box_reference(screen, national_dex_number_box), 0xff808080, 0xffffffff);
639+
if (national_dex_number <= 0 || national_dex_number > 1025) { // Current last pokemon is pecharunt
640+
dump_image(env.console, ProgramInfo(), "ReadSummary_national_dex_number", screen);
641+
}
642+
boxes_data[get_index(box_idx, row, column)]->national_dex_number = (uint16_t)national_dex_number;
633643

634-
int shiny_stddev_value = (int)image_stddev(extract_box_reference(screen, shiny_symbol_box)).sum();
635-
bool is_shiny = shiny_stddev_value > 30;
636-
boxes_data[get_index(box_nb, row, column)]->shiny = is_shiny;
637-
env.console.log("Shiny detection stddev:" + std::to_string(shiny_stddev_value) + " is shiny:" + std::to_string(is_shiny));
644+
int shiny_stddev_value = (int)image_stddev(extract_box_reference(screen, shiny_symbol_box)).sum();
645+
bool is_shiny = shiny_stddev_value > 30;
646+
boxes_data[get_index(box_idx, row, column)]->shiny = is_shiny;
647+
env.console.log("Shiny detection stddev:" + std::to_string(shiny_stddev_value) + " is shiny:" + std::to_string(is_shiny));
638648

639-
int gmax_stddev_value = (int)image_stddev(extract_box_reference(screen, gmax_symbol_box)).sum();
640-
bool is_gmax = gmax_stddev_value > 30;
641-
boxes_data[get_index(box_nb, row, column)]->gmax = is_gmax;
642-
env.console.log("Gmax detection stddev:" + std::to_string(gmax_stddev_value) + " is gmax:" + std::to_string(is_gmax));
649+
int gmax_stddev_value = (int)image_stddev(extract_box_reference(screen, gmax_symbol_box)).sum();
650+
bool is_gmax = gmax_stddev_value > 30;
651+
boxes_data[get_index(box_idx, row, column)]->gmax = is_gmax;
652+
env.console.log("Gmax detection stddev:" + std::to_string(gmax_stddev_value) + " is gmax:" + std::to_string(is_gmax));
643653

644-
BallReader ball_reader(env.console);
645-
boxes_data[get_index(box_nb, row, column)]->ball_slug = ball_reader.read_ball(screen);
654+
BallReader ball_reader(env.console);
655+
boxes_data[get_index(box_idx, row, column)]->ball_slug = ball_reader.read_ball(screen);
646656

647-
BoxGenderDetector::make_overlays(box_render);
648-
StatsHuntGenderFilter gender = BoxGenderDetector::detect(screen);
649-
env.console.log("Gender: " + gender_to_string(gender), COLOR_GREEN);
650-
boxes_data[get_index(box_nb, row, column)]->gender = gender;
657+
BoxGenderDetector::make_overlays(box_render);
658+
StatsHuntGenderFilter gender = BoxGenderDetector::detect(screen);
659+
env.console.log("Gender: " + gender_to_string(gender), COLOR_GREEN);
660+
boxes_data[get_index(box_idx, row, column)]->gender = gender;
651661

652-
int ot_id = OCR::read_number_waterfill(env.console, extract_box_reference(screen, ot_id_box), 0xff808080, 0xffffffff);
653-
if (ot_id < 0 || ot_id > 999'999) {
654-
dump_image(env.console, ProgramInfo(), "ReadSummary_OT", screen);
655-
}
656-
boxes_data[get_index(box_nb, row, column)]->ot_id = ot_id;
662+
int ot_id = OCR::read_number_waterfill(env.console, extract_box_reference(screen, ot_id_box), 0xff808080, 0xffffffff);
663+
if (ot_id < 0 || ot_id > 999'999) {
664+
dump_image(env.console, ProgramInfo(), "ReadSummary_OT", screen);
665+
}
666+
boxes_data[get_index(box_idx, row, column)]->ot_id = ot_id;
657667

658-
// NOTE edit when adding new struct members (detections go here likely)
668+
const std::string& species_slug = NATIONAL_DEX_SLUGS()[national_dex_number-1];
669+
const std::string& display_name = get_pokemon_name(species_slug).display_name();
670+
env.add_overlay_log("Read " + display_name);
659671

660-
// level_box
661-
// ot_box
662-
// nature_box
663-
// ability_box
672+
// NOTE edit when adding new struct members (detections go here likely)
664673

665-
pbf_press_button(context, BUTTON_R, 10, VIDEO_DELAY+15);
666-
context.wait_for_all_requests();
667-
}
674+
// level_box
675+
// ot_box
676+
// nature_box
677+
// ability_box
678+
679+
pbf_press_button(context, BUTTON_R, 10, VIDEO_DELAY+15);
680+
context.wait_for_all_requests();
668681
}
669682
}
670683

@@ -675,7 +688,7 @@ void BoxSorting::program(SingleSwitchProgramEnvironment& env, ProControllerConte
675688
// print box information
676689
for (size_t row = 0; row < MAX_ROWS; row++){
677690
for (size_t column = 0; column < MAX_COLUMNS; column++){
678-
ss << boxes_data[get_index(box_nb, row, column)] << " ";
691+
ss << boxes_data[get_index(box_idx, row, column)] << " ";
679692
}
680693
ss << std::endl;
681694
}
@@ -694,7 +707,7 @@ void BoxSorting::program(SingleSwitchProgramEnvironment& env, ProControllerConte
694707
dest_cursor = {0, 0, 0};
695708
nav_cursor = move_cursor_to(env, context, nav_cursor, dest_cursor, GAME_DELAY);
696709
context.wait_for_all_requests();
697-
}
710+
} // end if (find_first_poke){
698711

699712
// copy boxes data to sort
700713
std::vector<std::optional<Pokemon>> boxes_sorted = boxes_data;

0 commit comments

Comments
 (0)