Skip to content

Commit d354ba0

Browse files
committed
2 parents c46166a + 60f1385 commit d354ba0

File tree

7 files changed

+235
-54
lines changed

7 files changed

+235
-54
lines changed

SerialPrograms/Source/NintendoSwitch/Programs/NintendoSwitch_GameEntry.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ namespace PokemonAutomation{
1616
namespace NintendoSwitch{
1717

1818

19+
// Press Home button to go from game to Switch Home screen
1920
void go_home(ConsoleHandle& console, ProControllerContext& context);
21+
// Press Home button to go from game to Switch Home screen
2022
void go_home(ConsoleHandle& console, JoyconContext& context);
2123
void ensure_at_home(ConsoleHandle& console, ProControllerContext& context);
2224
void ensure_at_home(ConsoleHandle& console, JoyconContext& context);

SerialPrograms/Source/PokemonLZA/Inference/Boxes/PokemonLZA_BoxDetection.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
*/
66

77
#include "Common/Cpp/Exceptions.h"
8-
// #include "CommonFramework/Exceptions/FatalProgramException.h"
98
#include "CommonFramework/ImageTools/ImageStats.h"
109
#include "CommonFramework/VideoPipeline/VideoOverlay.h"
1110
#include "CommonFramework/VideoPipeline/VideoOverlayScopes.h"
@@ -211,6 +210,11 @@ void BoxDetector::move_cursor(
211210
const ProgramInfo& info, VideoStream& stream, ProControllerContext& context,
212211
uint8_t row, uint8_t col
213212
){
213+
if (row >= 6 || col >= 6){
214+
throw InternalProgramError(&stream.logger(), "BoxDetector::move_cursor",
215+
"row or col out of range: " + std::to_string(row) + ", " + std::to_string(col));
216+
}
217+
214218
WallClock start = current_time();
215219
while (true){
216220
if (current_time() - start > std::chrono::seconds(60)){

SerialPrograms/Source/PokemonLZA/Inference/Boxes/PokemonLZA_BoxInfoDetector.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,36 @@ bool BoxAlphaDetector::detect(const ImageViewRGB32& screen){
157157
return found;
158158
}
159159

160+
BoxPageInfoWatcher::BoxPageInfoWatcher(VideoOverlay* overlay)
161+
: VisualInferenceCallback("BoxPageInfoWatcher")
162+
, m_shiny_watcher(COLOR_BLACK, overlay)
163+
, m_alpha_watcher(COLOR_RED, overlay)
164+
{}
165+
166+
void BoxPageInfoWatcher::make_overlays(VideoOverlaySet& items) const{
167+
m_shiny_watcher.make_overlays(items);
168+
m_alpha_watcher.make_overlays(items);
169+
}
170+
171+
bool BoxPageInfoWatcher::process_frame(const ImageViewRGB32& frame, WallClock timestamp){
172+
bool shiny_determined = m_shiny_watcher.process_frame(frame, timestamp);
173+
bool alpha_determined = m_alpha_watcher.process_frame(frame, timestamp);
174+
return shiny_determined && alpha_determined;
175+
}
176+
177+
std::string BoxPageInfoWatcher::info_str() const{
178+
const bool is_shiny = m_shiny_watcher.consistent_result();
179+
const bool is_alpha = m_alpha_watcher.consistent_result();
180+
181+
if (is_shiny && is_alpha){
182+
return "Shiny Alpha";
183+
} else if (is_shiny){
184+
return "Shiny";
185+
} else if (is_alpha){
186+
return "Alpha";
187+
}
188+
return "Normal";
189+
}
160190

161191

162192

SerialPrograms/Source/PokemonLZA/Inference/Boxes/PokemonLZA_BoxInfoDetector.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ class BoxShinyDetector : public StaticScreenDetector{
3939
std::optional<OverlayBoxScope> m_last_detected_box;
4040
};
4141

42+
// Detect whether there is a shiny symbol on the current pokemon in the box system.
43+
// Call BoxShinyWatcher::consistent_result() to know if the pokemon is shiny or not.
4244
class BoxShinyWatcher : public DetectorToFinder<BoxShinyDetector>{
4345
public:
4446
BoxShinyWatcher(
@@ -71,6 +73,8 @@ class BoxAlphaDetector : public StaticScreenDetector{
7173
std::optional<OverlayBoxScope> m_last_detected_box;
7274
};
7375

76+
// Detect whether there is an alpha symbol on the current pokemon in the box system.
77+
// Call BoxAlphaWatcher::consistent_result() to know if the pokemon is alpha or not.
7478
class BoxAlphaWatcher : public DetectorToFinder<BoxAlphaDetector>{
7579
public:
7680
BoxAlphaWatcher(
@@ -82,7 +86,37 @@ class BoxAlphaWatcher : public DetectorToFinder<BoxAlphaDetector>{
8286
{}
8387
};
8488

89+
// Check both shiny and alpha-ness of the current pokemon in the box view
90+
class BoxPageInfoWatcher : public VisualInferenceCallback{
91+
public:
92+
BoxPageInfoWatcher(VideoOverlay* overlay);
93+
94+
virtual void make_overlays(VideoOverlaySet& items) const override;
8595

96+
// return true when the watcher determines the shiny and alpha-ness of the pokemon
97+
virtual bool process_frame(const ImageViewRGB32& frame, WallClock timestamp) override;
98+
99+
// reset internal state so the watcher is ready for detect another pokemon's info
100+
void reset_state(){
101+
m_shiny_watcher.reset_state();
102+
m_alpha_watcher.reset_state();
103+
}
104+
105+
// After detection is done, call this to check shiny-ness
106+
bool is_shiny() const { return m_shiny_watcher.consistent_result(); }
107+
// After detection is done, call this to check alpha-ness
108+
bool is_alpha() const { return m_alpha_watcher.consistent_result(); }
109+
// Return string of detected shiny and alpha-ness for logging. It can be:
110+
// - "Normal"
111+
// - "Shiny"
112+
// - "Alpha"
113+
// - "Shiny Alpha"
114+
std::string info_str() const;
115+
116+
private:
117+
BoxShinyWatcher m_shiny_watcher;
118+
BoxAlphaWatcher m_alpha_watcher;
119+
};
86120

87121

88122
}

SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_AutoFossil.cpp

Lines changed: 137 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,19 @@
77
#include "CommonFramework/ProgramStats/StatsTracking.h"
88
#include "CommonTools/Async/InferenceRoutines.h"
99
#include "CommonFramework/Exceptions/OperationFailedException.h"
10+
#include "CommonFramework/Notifications/ProgramNotifications.h"
11+
#include "CommonFramework/VideoPipeline/VideoFeed.h"
1012
#include "NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h"
1113
#include "Pokemon/Pokemon_Strings.h"
14+
#include "Pokemon/Pokemon_Notification.h"
15+
#include "PokemonLZA/Inference/Boxes/PokemonLZA_BoxDetection.h"
16+
#include "PokemonLZA/Inference/Boxes/PokemonLZA_BoxInfoDetector.h"
1217
#include "PokemonLZA/Inference/PokemonLZA_ButtonDetector.h"
1318
#include "PokemonLZA/Inference/PokemonLZA_SelectionArrowDetector.h"
1419
#include "PokemonLZA/Inference/PokemonLZA_DialogDetector.h"
20+
#include "PokemonLZA/Programs/PokemonLZA_GameEntry.h"
1521
#include "PokemonLZA/Programs/PokemonLZA_MenuNavigation.h"
16-
#include "PokemonLZA/Inference/Boxes/PokemonLZA_BoxDetection.h"
22+
#include "NintendoSwitch/Programs/NintendoSwitch_GameEntry.h"
1723
#include "PokemonLZA_AutoFossil.h"
1824

1925
#include <sstream>
@@ -53,13 +59,19 @@ class AutoFossil_Descriptor::Stats : public StatsTracker{
5359
public:
5460
Stats()
5561
: fossils(m_stats["Fossils"])
62+
, alphas(m_stats["Alphas"])
63+
, shinies(m_stats["Shinies"])
5664
, errors(m_stats["Errors"])
5765
{
5866
m_display_order.emplace_back("Fossils");
67+
m_display_order.emplace_back("Alphas");
68+
m_display_order.emplace_back("Shinies");
5969
m_display_order.emplace_back("Errors", HIDDEN_IF_ZERO);
6070
}
6171

6272
std::atomic<uint64_t>& fossils;
73+
std::atomic<uint64_t>& alphas;
74+
std::atomic<uint64_t>& shinies;
6375
std::atomic<uint64_t>& errors;
6476
};
6577

@@ -68,23 +80,60 @@ std::unique_ptr<StatsTracker> AutoFossil_Descriptor::make_stats() const{
6880
}
6981

7082

71-
AutoFossil::AutoFossil(){}
83+
AutoFossil::AutoFossil()
84+
: NUM_BOXES("<b>Boxes of Fossils to Revive:</b>",
85+
LockMode::LOCK_WHILE_RUNNING,
86+
1, 1, 32
87+
)
88+
, FOUND_SHINY_OR_ALPHA(
89+
"Found Shiny or Alpha",
90+
true, true,
91+
ImageAttachmentMode::JPG,
92+
{"Notifs", "Showcase"}
93+
)
94+
, NOTIFICATION_STATUS("Status Update", true, false, std::chrono::seconds(3600))
95+
, NOTIFICATIONS({
96+
&NOTIFICATION_STATUS,
97+
&FOUND_SHINY_OR_ALPHA,
98+
&NOTIFICATION_PROGRAM_FINISH,
99+
&NOTIFICATION_ERROR_RECOVERABLE,
100+
&NOTIFICATION_ERROR_FATAL,
101+
})
102+
{
103+
PA_ADD_OPTION(STOP_ON);
104+
PA_ADD_OPTION(NUM_BOXES);
105+
PA_ADD_OPTION(NOTIFICATIONS);
106+
}
72107

73108

74109
void AutoFossil::program(SingleSwitchProgramEnvironment& env, ProControllerContext& context){
75-
// AutoFossil_Descriptor::Stats& stats = env.current_stats<AutoFossil_Descriptor::Stats>();
76-
overworld_to_box_system(env.console, context);
77-
return;
78-
79-
// Example loop structure
80-
size_t num_fossils_to_revive = 3;
81-
for(size_t i = 0; i < num_fossils_to_revive; i++){
82-
revive_one_fossil(env, context);
83-
std::ostringstream os;
84-
os << "Got Fossil " << i + 1 << "/" << num_fossils_to_revive;
85-
std::string log_str = os.str();
86-
env.log(log_str);
87-
env.console.overlay().add_log(log_str);
110+
while(true){
111+
size_t num_fossils_to_revive = size_t(NUM_BOXES) * 30;
112+
for(size_t i = 0; i < num_fossils_to_revive; i++){
113+
revive_one_fossil(env, context);
114+
std::ostringstream os;
115+
os << "Got Fossil " << i + 1 << "/" << num_fossils_to_revive;
116+
std::string log_str = os.str();
117+
env.log(log_str);
118+
env.console.overlay().add_log(log_str);
119+
}
120+
121+
overworld_to_box_system(env.console, context);
122+
for(uint8_t i = 0; i < NUM_BOXES; i++){
123+
bool found_match = check_fossils_in_one_box(env, context);
124+
if (found_match){
125+
return;
126+
}
127+
if (i != NUM_BOXES - 1){
128+
// go to next page
129+
pbf_press_button(context, BUTTON_R, 200ms, 200ms);
130+
}
131+
}
132+
// checked all boxes, no match
133+
go_home(env.console, context);
134+
reset_game_from_home(env, env.console, context);
135+
136+
send_program_status_notification(env, NOTIFICATION_STATUS);
88137
}
89138
}
90139

@@ -149,27 +198,96 @@ void AutoFossil::revive_one_fossil(SingleSwitchProgramEnvironment& env, ProContr
149198
case 3:
150199
env.log("Detected blue dialog.");
151200
pbf_press_button(context, BUTTON_B, 80ms, 40ms);
201+
// in normal cases when blue dialog box is detected, we already seen the selection
202+
// arrow. But just in case somehow the program misses the selection arrow detection,
203+
// we can still set it to true here to ensure subsequent program execution is smooth.
204+
seen_selection_arrow = true;
152205
continue;
153206

154207
default:
155208
stats.errors++;
156209
env.update_stats();
157210
OperationFailedException::fire(
158211
ErrorReport::SEND_ERROR_REPORT,
159-
"run_lobby(): No recognized state after 60 seconds.",
212+
"revive_one_fossil(): No recognized state after 60 seconds.",
160213
env.console
161214
);
162215
}
163216
}
164217
}
165218

166219
// start at box system, check fossils one by one
167-
void AutoFossil::check_fossils_in_box(SingleSwitchProgramEnvironment& env, ProControllerContext& context, size_t num_boxes){
168-
uint8_t box_x = 0, box_y = 0;
220+
bool AutoFossil::check_fossils_in_one_box(SingleSwitchProgramEnvironment& env, ProControllerContext& context){
221+
AutoFossil_Descriptor::Stats& stats = env.current_stats<AutoFossil_Descriptor::Stats>();
222+
223+
uint8_t box_row = 1, box_col = 0;
224+
bool next_cell_right = true;
169225
BoxDetector box_detector(COLOR_RED, &env.console.overlay());
226+
BoxPageInfoWatcher info_watcher(&env.console.overlay());
170227
for(size_t i = 0; i < 30; i++){
171-
box_detector.move_cursor(env.program_info(), env.console, context, box_x, box_y);
228+
env.console.overlay().add_log("To cell: (" + std::to_string(box_row) + ", " + std::to_string(box_col) + ")");
229+
box_detector.move_cursor(env.program_info(), env.console, context, box_row, box_col);
230+
231+
info_watcher.reset_state();
232+
wait_until(env.console, context, WallClock::max(), {info_watcher});
233+
234+
std::ostringstream os;
235+
os << i + 1 << "/" << int(NUM_BOXES)*30 << ": " << info_watcher.info_str();
236+
std::string log_str = os.str();
237+
env.log(log_str);
238+
env.console.overlay().add_log(log_str);
239+
if (info_watcher.is_alpha()){
240+
stats.alphas++;
241+
env.update_stats();
242+
}
243+
if (info_watcher.is_shiny()){
244+
stats.shinies++;
245+
env.update_stats();
246+
}
247+
248+
bool is_match = false;
249+
switch (STOP_ON){
250+
case PokemonLA::StopOn::Shiny:
251+
is_match = info_watcher.is_shiny();
252+
break;
253+
case PokemonLA::StopOn::Alpha:
254+
is_match = info_watcher.is_alpha();
255+
break;
256+
case PokemonLA::StopOn::ShinyOrAlpha:
257+
is_match = info_watcher.is_shiny() || info_watcher.is_alpha();
258+
break;
259+
case PokemonLA::StopOn::ShinyAndAlpha:
260+
is_match = info_watcher.is_shiny() && info_watcher.is_alpha();
261+
break;
262+
}
263+
if (is_match){
264+
send_program_notification(
265+
env, FOUND_SHINY_OR_ALPHA,
266+
Pokemon::COLOR_STAR_SHINY,
267+
"Found " + info_watcher.info_str() + "!",
268+
{}, "",
269+
env.console.video().snapshot(), true
270+
);
271+
return true;
272+
}
273+
274+
if (next_cell_right){
275+
if (box_col == 5){
276+
box_row++;
277+
next_cell_right = false;
278+
} else{
279+
box_col++;
280+
}
281+
} else{
282+
if (box_col == 0){
283+
box_row++;
284+
next_cell_right = true;
285+
} else{
286+
box_col--;
287+
}
288+
}
172289
}
290+
return false;
173291
}
174292

175293
}

SerialPrograms/Source/PokemonLZA/Programs/PokemonLZA_AutoFossil.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@
99
#define PokemonAutomation_PokemonLZA_AutoFossil_H
1010

1111
#include "Common/Cpp/Options/ButtonOption.h"
12+
#include "Common/Cpp/Options/SimpleIntegerOption.h"
13+
#include "CommonFramework/Notifications/EventNotificationOption.h"
14+
#include "CommonFramework/Notifications/EventNotificationsTable.h"
1215
#include "NintendoSwitch/NintendoSwitch_SingleSwitchProgram.h"
16+
#include "PokemonLA/Options/PokemonLA_MiscOptions.h"
1317

1418
namespace PokemonAutomation{
1519
namespace NintendoSwitch{
@@ -34,7 +38,16 @@ class AutoFossil : public SingleSwitchProgramInstance{
3438
private:
3539
void revive_one_fossil(SingleSwitchProgramEnvironment& env, ProControllerContext& context);
3640

37-
void check_fossils_in_box(SingleSwitchProgramEnvironment& env, ProControllerContext& context, size_t num_boxes);
41+
// return true if found a match
42+
bool check_fossils_in_one_box(SingleSwitchProgramEnvironment& env, ProControllerContext& context);
43+
44+
PokemonLA::StopOnOption STOP_ON;
45+
46+
SimpleIntegerOption<uint8_t> NUM_BOXES;
47+
48+
EventNotificationOption FOUND_SHINY_OR_ALPHA;
49+
EventNotificationOption NOTIFICATION_STATUS;
50+
EventNotificationsOption NOTIFICATIONS;
3851
};
3952

4053

0 commit comments

Comments
 (0)