Skip to content

Commit 058d453

Browse files
committed
alolan trade work, shiny symbol detector work
1 parent e747f1e commit 058d453

File tree

7 files changed

+387
-2
lines changed

7 files changed

+387
-2
lines changed

SerialPrograms/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1360,6 +1360,10 @@ file(GLOB MAIN_SOURCES
13601360
Source/PokemonLA/Resources/PokemonLA_PokemonSprites.h
13611361
Source/PokemonLA/Resources/PokemonLA_WeatherAndTimeIcons.cpp
13621362
Source/PokemonLA/Resources/PokemonLA_WeatherAndTimeIcons.h
1363+
Source/PokemonLGPE/Inference/PokemonLGPE_ShinySymbolDetector.cpp
1364+
Source/PokemonLGPE/Inference/PokemonLGPE_ShinySymbolDetector.h
1365+
Source/PokemonLGPE/Programs/PokemonLGPE_AlolanTrade.cpp
1366+
Source/PokemonLGPE/Programs/PokemonLGPE_AlolanTrade.h
13631367
Source/PokemonLGPE/PokemonLGPE_Panels.cpp
13641368
Source/PokemonLGPE/PokemonLGPE_Panels.h
13651369
Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.cpp

SerialPrograms/SerialPrograms.pro

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -667,6 +667,8 @@ SOURCES += \
667667
Source/PokemonLA/Resources/PokemonLA_NameDatabase.cpp \
668668
Source/PokemonLA/Resources/PokemonLA_PokemonSprites.cpp \
669669
Source/PokemonLA/Resources/PokemonLA_WeatherAndTimeIcons.cpp \
670+
Source/PokemonLGPE/Inference/PokemonLGPE_ShinySymbolDetector.cpp \
671+
Source/PokemonLGPE/Programs/PokemonLGPE_AlolanTrade.cpp \
670672
Source/PokemonLGPE/PokemonLGPE_Panels.cpp \
671673
Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.cpp \
672674
Source/PokemonRSE/Inference/PokemonRSE_ShinyNumberDetector.cpp \
@@ -1847,6 +1849,8 @@ HEADERS += \
18471849
Source/PokemonLA/Resources/PokemonLA_NameDatabase.h \
18481850
Source/PokemonLA/Resources/PokemonLA_PokemonSprites.h \
18491851
Source/PokemonLA/Resources/PokemonLA_WeatherAndTimeIcons.h \
1852+
Source/PokemonLGPE/Inference/PokemonLGPE_ShinySymbolDetector.h \
1853+
Source/PokemonLGPE/Programs/PokemonLGPE_AlolanTrade.h \
18501854
Source/PokemonLGPE/PokemonLGPE_Panels.h \
18511855
Source/PokemonRSE/Inference/Dialogs/PokemonRSE_DialogDetector.h \
18521856
Source/PokemonRSE/Inference/PokemonRSE_ShinyNumberDetector.h \
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/* Shiny Symbol Detector
2+
*
3+
* From: https://github.com/PokemonAutomation/Arduino-Source
4+
*
5+
*/
6+
7+
#include "Common/Cpp/Color.h"
8+
#include "CommonFramework/ImageTools/ImageBoxes.h"
9+
#include "CommonFramework/ImageTools/ImageStats.h"
10+
#include "CommonFramework/ImageTypes/ImageRGB32.h"
11+
#include "CommonFramework/ImageTypes/ImageViewRGB32.h"
12+
#include "CommonFramework/VideoPipeline/VideoOverlayScopes.h"
13+
#include "CommonTools/Images/ImageFilter.h"
14+
#include "PokemonLGPE_ShinySymbolDetector.h"
15+
16+
//#include <iostream>
17+
//using std::cout;
18+
//using std::endl;
19+
20+
namespace PokemonAutomation{
21+
namespace NintendoSwitch{
22+
namespace PokemonLGPE{
23+
24+
ShinySymbolDetector::ShinySymbolDetector(Color color)
25+
: m_box_star(0.204, 0.095, 0.033, 0.053)
26+
{}
27+
void ShinySymbolDetector::make_overlays(VideoOverlaySet& items) const{
28+
items.add(COLOR_RED, m_box_star);
29+
}
30+
bool ShinySymbolDetector::read(Logger& logger, const ImageViewRGB32& frame){
31+
const bool replace_color_within_range = true;
32+
33+
//Filter out background
34+
ImageRGB32 filtered_region = filter_rgb32_range(
35+
extract_box_reference(frame, m_box_star),
36+
combine_rgb(138, 97, 221), combine_rgb(200, 181, 239), Color(0), replace_color_within_range
37+
);
38+
ImageStats stats = image_stats(filtered_region);
39+
40+
/*
41+
filtered_region.save("./filtered_only.png");
42+
cout << stats.average.r << endl;
43+
cout << stats.average.g << endl;
44+
cout << stats.average.b << endl;
45+
*/
46+
47+
/*
48+
Shiny:
49+
R: 196.632, G: 196.771, B: 145.863
50+
Not shiny:
51+
R: 181.862, G: 180.686, B: 193.999
52+
*/
53+
54+
if (stats.average.r + 100 > stats.average.b){
55+
return true;
56+
}
57+
return false;
58+
}
59+
60+
61+
}
62+
}
63+
}
64+
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/* Shiny Symbol Detector
2+
*
3+
* From: https://github.com/PokemonAutomation/Arduino-Source
4+
*
5+
*/
6+
7+
#ifndef PokemonAutomation_PokemonRSE_ShinyNumberDetector_H
8+
#define PokemonAutomation_PokemonRSE_ShinyNumberDetector_H
9+
10+
#include "Common/Cpp/AbstractLogger.h"
11+
#include "CommonFramework/VideoPipeline/VideoOverlayScopes.h"
12+
13+
namespace PokemonAutomation{
14+
namespace NintendoSwitch{
15+
namespace PokemonLGPE{
16+
17+
// After an in-game trade, the Pokemon's summary will appear.
18+
// Red star for shiny. No star if not.
19+
// Position is different when viewing in boxes. Do not use this for that.
20+
class ShinySymbolDetector{
21+
public:
22+
ShinySymbolDetector(Color color);
23+
24+
virtual void make_overlays(VideoOverlaySet& items) const;
25+
bool read(Logger& logger, const ImageViewRGB32& frame);
26+
27+
private:
28+
ImageFloatBox m_box_star;
29+
};
30+
31+
32+
33+
}
34+
}
35+
}
36+
#endif

SerialPrograms/Source/PokemonLGPE/PokemonLGPE_Panels.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
#include "Pokemon/Pokemon_Strings.h"
99
#include "PokemonLGPE_Panels.h"
1010

11-
11+
#include "Programs/PokemonLGPE_AlolanTrade.h"
1212

1313
namespace PokemonAutomation{
1414
namespace NintendoSwitch{
@@ -27,7 +27,7 @@ std::vector<PanelEntry> PanelListFactory::make_panels() const{
2727
//ret.emplace_back("---- General ----");
2828

2929
ret.emplace_back("---- Shiny Hunting ----");
30-
//ret.emplace_back(make_single_switch_program<AudioStarterReset_Descriptor, AudioStarterReset>());
30+
ret.emplace_back(make_single_switch_program<AlolanTrade_Descriptor, AlolanTrade>());
3131

3232
return ret;
3333
}
Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
/* LGPE Alolan Trade
2+
*
3+
* From: https://github.com/PokemonAutomation/Arduino-Source
4+
*
5+
*/
6+
7+
#include "CommonFramework/Exceptions/OperationFailedException.h"
8+
#include "CommonFramework/Notifications/ProgramNotifications.h"
9+
#include "CommonFramework/ProgramStats/StatsTracking.h"
10+
#include "CommonFramework/VideoPipeline/VideoFeed.h"
11+
#include "CommonTools/Async/InferenceRoutines.h"
12+
#include "CommonTools/StartupChecks/VideoResolutionCheck.h"
13+
#include "NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h"
14+
#include "NintendoSwitch/Controllers/NintendoSwitch_Joycon.h"
15+
#include "Pokemon/Pokemon_Strings.h"
16+
#include "CommonTools/VisualDetectors/BlackScreenDetector.h"
17+
#include "PokemonLGPE/Inference/PokemonLGPE_ShinySymbolDetector.h"
18+
#include "PokemonLGPE_AlolanTrade.h"
19+
20+
//#include <iostream>
21+
//using std::cout;
22+
//using std::endl;
23+
24+
namespace PokemonAutomation{
25+
namespace NintendoSwitch{
26+
namespace PokemonLGPE{
27+
28+
AlolanTrade_Descriptor::AlolanTrade_Descriptor()
29+
: SingleSwitchProgramDescriptor(
30+
"PokemonLGPE:AlolanTrade",
31+
Pokemon::STRING_POKEMON + " LGPE", "Alolan Trade",
32+
"",
33+
"Shiny hunt Alolan forms by trading in-game.",
34+
FeedbackType::NONE,
35+
AllowCommandsWhenRunning::DISABLE_COMMANDS,
36+
{ControllerFeature::NintendoSwitch_RightJoycon},
37+
FasterIfTickPrecise::NOT_FASTER
38+
)
39+
{}
40+
41+
struct AlolanTrade_Descriptor::Stats : public StatsTracker{
42+
Stats()
43+
: trades(m_stats["Trades"])
44+
, resets(m_stats["Resets"])
45+
, shinies(m_stats["Shinies"])
46+
{
47+
m_display_order.emplace_back("Trades");
48+
m_display_order.emplace_back("Resets");
49+
m_display_order.emplace_back("Shinies");
50+
}
51+
std::atomic<uint64_t>& trades;
52+
std::atomic<uint64_t>& resets;
53+
std::atomic<uint64_t>& shinies;
54+
};
55+
std::unique_ptr<StatsTracker> AlolanTrade_Descriptor::make_stats() const{
56+
return std::unique_ptr<StatsTracker>(new Stats());
57+
}
58+
59+
AlolanTrade::AlolanTrade()
60+
: NUM_TRADES(
61+
"<b>Number of Pokemon to trade:</b>",
62+
LockMode::UNLOCK_WHILE_RUNNING,
63+
30
64+
)
65+
, GO_HOME_WHEN_DONE(false)
66+
, NOTIFICATION_SHINY(
67+
"Shiny Found",
68+
true, true, ImageAttachmentMode::JPG,
69+
{"Notifs", "Showcase"}
70+
)
71+
, NOTIFICATION_STATUS_UPDATE("Status Update", true, false, std::chrono::seconds(3600))
72+
, NOTIFICATIONS({
73+
&NOTIFICATION_SHINY,
74+
&NOTIFICATION_STATUS_UPDATE,
75+
&NOTIFICATION_PROGRAM_FINISH,
76+
})
77+
{
78+
PA_ADD_OPTION(NUM_TRADES);
79+
//PA_ADD_OPTION(GO_HOME_WHEN_DONE);
80+
PA_ADD_OPTION(NOTIFICATIONS);
81+
}
82+
83+
void AlolanTrade::program(SingleSwitchProgramEnvironment& env, CancellableScope& scope){
84+
JoyconContext context(scope, env.console.controller<JoyconController>());
85+
assert_16_9_720p_min(env.logger(), env.console);
86+
AlolanTrade_Descriptor::Stats& stats = env.current_stats<AlolanTrade_Descriptor::Stats>();
87+
88+
/*
89+
WARNING: JOYCON TEST PROGRAM. Not well tested. Minimum infra to get this running. Bare minimum in general.
90+
Use at your own risk, it won't skip update checks and the like.
91+
FLASH RIGHT JOYCON. YOU NEED RIGHT JOYCON. YOU NEED THE HOME BUTTON. (this means no on-switch screenshots)
92+
Also don't remap any of the buttons in the switch button mapping settings. Yet?
93+
94+
Preconditions:
95+
DO NOT have any Pokemon you want to keep in your boxes. Move them out to Home first.
96+
Favoriting a Pokemon does not prevent it from being traded.
97+
This must not be your first time doing the trade. (I've done all the trades, can't check first time trade behavior.)
98+
99+
Setup:
100+
Catch the Kanto variant of the target.
101+
Stand in front of trade NPC.
102+
Start the program in-game.
103+
104+
Future additions?:
105+
skip favorited pokemon - they have a symbol to indicate fav status (imo still risky to do, just move them out)
106+
detect when all pokemon traded - trading screen will open but text will appear in the center
107+
"you don't have any pokemon your trading partner wants"
108+
detect dialog box, detect yes/no confirm box
109+
actual enter game and start screen detectors
110+
menu detectors, reset game from home, etc.
111+
get rid of all this blindly mashing A in general...so need everything really.
112+
*/
113+
114+
//to check pokemon in menu boxes - not used
115+
//Open menu - always defaults to center (Party)
116+
/* Menu:
117+
Play with Partner
118+
Pokedex - Bag - Party - Communicate - Save
119+
(Press Y for options)
120+
121+
sort boxes by recently caught and press left to get to most recent pokemon
122+
*/
123+
124+
/*
125+
pbf_press_button(context, BUTTON_A, 200ms, 2000ms);
126+
pbf_press_button(context, BUTTON_HOME, 200ms, 2000ms);
127+
pbf_move_joystick(context, 128, 0, 100ms, 100ms);
128+
pbf_move_joystick(context, 128, 0, 100ms, 100ms);
129+
pbf_move_joystick(context, 255, 128, 100ms, 100ms);
130+
pbf_move_joystick(context, 128, 0, 100ms, 100ms);
131+
pbf_press_button(context, BUTTON_X, 200ms, 2000ms);
132+
*/
133+
134+
bool shiny_found = false;
135+
while (!shiny_found) {
136+
//Run trades
137+
for (uint16_t i = 0; i < NUM_TRADES; i++) {
138+
//TODO: This is messy, pull it all out. run_trade()?
139+
140+
//Talk to NPC, say Yes, select Pokemon from box.
141+
BlackScreenOverWatcher trade_started(COLOR_RED);
142+
int ret = run_until<JoyconContext>(
143+
env.console, context,
144+
[](JoyconContext& context){
145+
pbf_mash_button(context, BUTTON_A, 15000ms);
146+
},
147+
{trade_started}
148+
);
149+
context.wait_for_all_requests();
150+
if (ret != 0){
151+
env.log("Failed to start trade.", COLOR_RED);
152+
OperationFailedException::fire(
153+
ErrorReport::SEND_ERROR_REPORT,
154+
"Failed to start trade.",
155+
env.console
156+
);
157+
}
158+
else {
159+
env.log("Trade started.");
160+
}
161+
162+
//Wait for trade to complete.
163+
BlackScreenOverWatcher trade_completed(COLOR_RED);
164+
int ret2 = wait_until(
165+
env.console, context,
166+
std::chrono::seconds(120),
167+
{trade_completed}
168+
);
169+
context.wait_for_all_requests();
170+
if (ret2 != 0){
171+
env.log("Did not detect end of trade.", COLOR_RED);
172+
OperationFailedException::fire(
173+
ErrorReport::SEND_ERROR_REPORT,
174+
"Did not detect end of trade.",
175+
env.console
176+
);
177+
}
178+
else {
179+
env.log("Trade completed.");
180+
}
181+
182+
//After black screen fade is done, a summary will appear.
183+
//pbf_wait(context, 250);
184+
context.wait_for_all_requests();
185+
186+
VideoSnapshot screen = env.console.video().snapshot();
187+
ShinySymbolDetector shiny_checker(COLOR_YELLOW);
188+
shiny_found = shiny_checker.read(env.console.logger(), screen);
189+
190+
if (shiny_found) {
191+
env.log("Shiny detected!");
192+
stats.shinies++;
193+
send_program_status_notification(env, NOTIFICATION_SHINY, "Shiny found!", screen, true);
194+
break;
195+
}
196+
else {
197+
env.log("Not shiny.");
198+
stats.trades++;
199+
}
200+
}
201+
202+
if (!shiny_found) {
203+
//Go to home, reset game
204+
//How to handle sideways joycons vs in-game? What if a set is paired?
205+
//Set as-is for now - I only have one ESP32, don't know how we're handling multiple joycons w/our usual home functions
206+
207+
env.log("Out of Pokemon to trade. Resetting game.");
208+
send_program_status_notification(
209+
env, NOTIFICATION_STATUS_UPDATE,
210+
"Out of Pokemon to trade. Resetting game."
211+
);
212+
}
213+
}
214+
215+
//GO_HOME_WHEN_DONE.run_end_of_program(context);
216+
if (GO_HOME_WHEN_DONE) {
217+
pbf_press_button(context, BUTTON_HOME, 200ms, 3000ms);
218+
}
219+
send_program_finished_notification(env, NOTIFICATION_PROGRAM_FINISH);
220+
}
221+
222+
223+
}
224+
}
225+
}

0 commit comments

Comments
 (0)