The first shinies that S.H.A.O.O.O.H. found were in Safari Week 2025, making it kind of the bots birthday. Let’s give it an upgrade for its first anniversary.
The main mode of operation of the bot is to sift through encounters or soft resets and then once a shiny is found, to wait for manual input to do the catching. This generally works quite well as catching might require dynamically reacting to moves missing, how many PP are left etc. It’d be preferable to mess that up manually rather than having an algorithm to catch a shiny that fails.
However, that dynamic changes a bit in the Safari Zone. If the bot finds a shiny in the middle of the night and then trying to catch it in the morning results in it fleeing it could be demoralising.
So, let’s add some functionality to allow it to throw Safari Balls. In order to do that there need to be the ability to trigger “Caught” and “Failed” transactions through the state machines.
This was done by adding state modifiers to trigger these transactions which were
previously only possible through the web interface. Unimaginatively these are called
Caught and Failed.
In the Safari Zone there’s three options after throwing a ball:
- Caught - so trigger the Caught transition
- Fled - trigger the Failed transition
- Stayed - loop around again and throw the next ball
After a shiny is detected the state machine is still running so can trigger these transitions, but when these transitions are sent they reset the state machine and start from the initial state again so need some logic to ensure the character is facing in a consistent direction.
This doesn’t account for using bait strategies or running out of safari balls but those would add a lot of complexity.
So far it’s been pretty successful, as long as it’s something in the Nidoran family. It doesn’t always detect if something flees, and identifies a lot of sprites as Venomoth, but it’s trying.
And the full state machine is:
# FRLG Safari Zone
# Try to get an encounter
chk_dir@ State&CheckToggle +up -left
up@ State +entering -down 75..95 {$FRLG_START_ENCOUNTER} :Up
down@ State +entering -up 75..95 {$FRLG_START_ENCOUNTER} :Down
left@ State +entering -right 75..95 {$FRLG_START_ENCOUNTER} :Left
right@ State +entering -left 75..95 {$FRLG_START_ENCOUNTER} :Right
entering@ State&Toggle
enter@ State&StartTimer +wait_ready {$FRLG_IN_ENCOUNTER}
wait_ready@ State&UpdateTimer +press_a {$FRLG_ENCOUNTER_READY}
press_a@ State 3000..4000 :A
# Safari Zone Area 4
detect@ State&ProcDelay=2481 +try_catch -run1 {Sprite:29+84+102+48+30+32+49+128+115:0}
# Try to catch
try_catch@ State 15000..15000 :A
# Did it catch?
# Caught if ready and options not onscreen
was_caught@ State +caught -chk_fled {$FRLG_ENCOUNTER_READY,$FRLG_IN_ENCOUNTER}&!{FRLG_SAFARI_OPTIONS_ONSCREEN}
# If still in encounter try again
chk_fled@ State +stayed -fled {$FRLG_IN_ENCOUNTER,FRLG_SAFARI_OPTIONS_ONSCREEN}
# Caught
caught@ State 500..500 :B
caught2@ State 1000..1000 :B
caught3@ State 1000..1000 :B
caught4@ State 1000..1000 :B
caught5@ State 1000..1000 :B
caught6@ State 1000..1000 :B
caught_chk@ State&CheckToggle +caught_up -caught_end 500..500
caught_up@ State :Up
caught_end@ State&Caught
# Stayed - try again
stayed@ State +try_catch
# Fled
fled@ State&CheckToggle +fled_up -fled_end 500..500
# Move up for next attempt
fled_up@ State :Up
fled_end@ State&Failed
# Run for next encounter
run1@ State 500..1000 :Down
run2@ State 500..1000 :Right
run3@ State 500..1000 :A
run4@ State 3000..6200 :A
Last modified on 2026-07-17