Created
September 28, 2024 22:15
-
-
Save lincoln-lm/c859e030968bdfde34183f7fad7b7544 to your computer and use it in GitHub Desktop.
SWSH rain & galar bird rng scripts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# search config | |
MIN_NPC_COUNT = 25 | |
MAX_NPC_COUNT = 40 | |
# unknown what exactly determines this value but it should be consistent for the area and not very high | |
MIN_FLY_VALUE = 0 | |
MAX_FLY_VALUE = 12 | |
# rng config | |
BRUTEFORCE_RANGE = 5000 | |
class Xoroshiro: | |
def __init__(self, seed_0, seed_1 = 0x82A2B175229D6A5B): | |
self.state = [seed_0, seed_1] | |
@staticmethod | |
def rotl(x, k): | |
return ((x << k) | (x >> (64 - k))) & 0xFFFFFFFFFFFFFFFF | |
@staticmethod | |
def get_mask(x): | |
x -= 1 | |
for i in range(6): | |
x |= x >> (1 << i) | |
return x | |
def next(self): | |
s0, s1 = self.state | |
result = (s0 + s1) & 0xFFFFFFFFFFFFFFFF | |
s1 ^= s0 | |
self.state = [self.rotl(s0, 24) ^ s1 ^ ((s1 << 16) & 0xFFFFFFFFFFFFFFFF), self.rotl(s1, 37)] | |
return result | |
def rand_count(self, maximum = 0xFFFFFFFF): | |
mask = self.get_mask(maximum) | |
res = self.next() & mask | |
count = 1 | |
while res >= maximum: | |
res = self.next() & mask | |
count += 1 | |
return res, count | |
def find_valid_calibration(rng_state: tuple[int, int], possible_results: set[int]) -> list[tuple[int, int]]: | |
calibrations = [] | |
for fly_value in range(MIN_FLY_VALUE, MAX_FLY_VALUE): | |
for npc_count in range(MIN_NPC_COUNT, MAX_NPC_COUNT): | |
test_rng = Xoroshiro(*rng_state) | |
jump = 0 | |
for _ in range(fly_value): | |
_, adv = test_rng.rand_count(100) | |
jump += adv | |
for _ in range(npc_count): | |
_, adv = test_rng.rand_count(91) | |
jump += adv | |
if jump in possible_results: | |
calibrations.append((fly_value, npc_count)) | |
return calibrations | |
def calibrate(): | |
state_0 = int(input("State 0: 0x"), 16) | |
state_1 = int(input("State 1: 0x"), 16) | |
current_advance = int(input("Current Advance: ")) | |
rng = Xoroshiro(state_0, state_1) | |
for _ in range(current_advance): | |
rng.next() | |
next_valid_advance = current_advance | |
temp_rng = Xoroshiro(*rng.state) | |
while not (5 <= (temp_rng.rand_count(100)[0] + 1)): | |
next_valid_advance += 1 | |
rng.next() | |
temp_rng = Xoroshiro(*rng.state) | |
post_menu_state = tuple(temp_rng.state) | |
print(f"Advance to {next_valid_advance} before opening the map") | |
possible_results = set() | |
while True: | |
inp = input("Possible hit advance (enter to confirm list): ") | |
if inp: | |
possible_results.add(int(inp) - next_valid_advance) | |
else: | |
break | |
print(f"{possible_results=}") | |
calibrations = find_valid_calibration(post_menu_state, possible_results) | |
print("Possible Calibrations:") | |
for fly_value, npc_count in calibrations: | |
print(f"{fly_value=} {npc_count=}") | |
def bruteforce_valid_advances(state: tuple[int, int], target_advance: int, fly_value: int, npc_count: int) -> list[int]: | |
valid_advances = [] | |
rng = Xoroshiro(*state) | |
for start in range(target_advance): | |
if start != 0: | |
rng.next() | |
test_rng = Xoroshiro(*rng.state) | |
memory_rand, jump = test_rng.rand_count(100) | |
if not (5 <= (memory_rand + 1)): | |
continue | |
for _ in range(fly_value): | |
_, adv = test_rng.rand_count(100) | |
jump += adv | |
for _ in range(npc_count): | |
_, adv = test_rng.rand_count(91) | |
jump += adv | |
if start + jump == target_advance: | |
valid_advances.append(start) | |
return valid_advances | |
def advance_conversion(): | |
state_0 = int(input("State 0: 0x"), 16) | |
state_1 = int(input("State 1: 0x"), 16) | |
fly_value = int(input("Fly Value: ")) | |
npc_count = int(input("NPC Count: ")) | |
while True: | |
inp = input("Target Advance (enter to exit program): ") | |
if inp: | |
target_advance = int(inp) | |
test_rng = Xoroshiro(state_0, state_1) | |
bruteforce_start = max(0, target_advance - BRUTEFORCE_RANGE) | |
for _ in range(bruteforce_start): | |
test_rng.next() | |
for result in bruteforce_valid_advances(test_rng.state, target_advance - bruteforce_start, fly_value, npc_count): | |
print(f"Enter menu at advance: {result+bruteforce_start}") | |
print("-"*20) | |
else: | |
break | |
if __name__ == "__main__": | |
if int(input("(0) RNG | (1) Calibrate: ")): | |
calibrate() | |
else: | |
advance_conversion() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# search config | |
# find in clear weather based on menu close advances | |
NPC_COUNT = 0 | |
# *roughly* tied to 60x the number of seconds between leaving the menu and the encounter generating | |
MIN_WEATHER_VALUE = 0 | |
MAX_WEATHER_VALUE = 60 | |
# rng config | |
BRUTEFORCE_RANGE = 5000 | |
class Xoroshiro: | |
def __init__(self, seed_0, seed_1 = 0x82A2B175229D6A5B): | |
self.state = [seed_0, seed_1] | |
@staticmethod | |
def rotl(x, k): | |
return ((x << k) | (x >> (64 - k))) & 0xFFFFFFFFFFFFFFFF | |
@staticmethod | |
def get_mask(x): | |
x -= 1 | |
for i in range(6): | |
x |= x >> (1 << i) | |
return x | |
def next(self): | |
s0, s1 = self.state | |
result = (s0 + s1) & 0xFFFFFFFFFFFFFFFF | |
s1 ^= s0 | |
self.state = [self.rotl(s0, 24) ^ s1 ^ ((s1 << 16) & 0xFFFFFFFFFFFFFFFF), self.rotl(s1, 37)] | |
return result | |
def rand_count(self, maximum = 0xFFFFFFFF): | |
mask = self.get_mask(maximum) | |
res = self.next() & mask | |
count = 1 | |
while res >= maximum: | |
res = self.next() & mask | |
count += 1 | |
return res, count | |
def find_valid_calibration(rng_state: tuple[int, int], leaving_pokemon_menu: bool, possible_results: set[int]) -> list[int]: | |
calibrations = [] | |
for weather_value in range(MIN_WEATHER_VALUE, MAX_WEATHER_VALUE, 2): | |
test_rng = Xoroshiro(*rng_state) | |
jump = 0 | |
for _ in range(4 + 6 if leaving_pokemon_menu else 4): | |
_, adv = test_rng.rand_count(20001) | |
jump += adv | |
for _ in range(NPC_COUNT): | |
_, adv = test_rng.rand_count(91) | |
jump += adv | |
for _ in range(weather_value): | |
_, adv = test_rng.rand_count(20001) | |
jump += adv | |
if jump in possible_results: | |
calibrations.append(weather_value) | |
return calibrations | |
def calibrate(): | |
state_0 = int(input("State 0: 0x"), 16) | |
state_1 = int(input("State 1: 0x"), 16) | |
current_advance = int(input("Current Advance: ")) | |
leaving_pokemon_menu = bool(int(input("Leaving pokemon menu? (0/1): "))) | |
rng = Xoroshiro(state_0, state_1) | |
for _ in range(current_advance): | |
rng.next() | |
next_valid_advance = current_advance | |
possible_results = set() | |
while True: | |
inp = input("Possible hit advance (enter to confirm list): ") | |
if inp: | |
possible_results.add(int(inp) - next_valid_advance) | |
else: | |
break | |
print(f"{possible_results=}") | |
calibrations = find_valid_calibration(tuple(rng.state), leaving_pokemon_menu, possible_results) | |
print("Possible Weather Values:") | |
print(calibrations) | |
def bruteforce_valid_advances(state: tuple[int, int], target_advance: int, leaving_pokemon_menu: bool, weather_value: int) -> list[int]: | |
valid_advances = [] | |
rng = Xoroshiro(*state) | |
for start in range(target_advance): | |
if start != 0: | |
rng.next() | |
test_rng = Xoroshiro(*rng.state) | |
jump = 0 | |
for _ in range(4 + 6 if leaving_pokemon_menu else 4): | |
_, adv = test_rng.rand_count(20001) | |
jump += adv | |
for _ in range(NPC_COUNT): | |
_, adv = test_rng.rand_count(91) | |
jump += adv | |
for _ in range(weather_value): | |
_, adv = test_rng.rand_count(20001) | |
jump += adv | |
if start + jump == target_advance: | |
valid_advances.append(start) | |
return valid_advances | |
def advance_conversion(): | |
state_0 = int(input("State 0: 0x"), 16) | |
state_1 = int(input("State 1: 0x"), 16) | |
weather_value = int(input("Weather Value: ")) | |
leaving_pokemon_menu = bool(int(input("Leaving pokemon menu? (0/1): "))) | |
while True: | |
inp = input("Target Advance (enter to exit program): ") | |
if inp: | |
target_advance = int(inp) | |
test_rng = Xoroshiro(state_0, state_1) | |
bruteforce_start = max(0, target_advance - BRUTEFORCE_RANGE) | |
for _ in range(bruteforce_start): | |
test_rng.next() | |
for result in bruteforce_valid_advances(test_rng.state, target_advance - bruteforce_start, leaving_pokemon_menu, weather_value): | |
print(f"Exit menu at advance: {result+bruteforce_start}") | |
print("-"*20) | |
else: | |
break | |
if __name__ == "__main__": | |
if int(input("(0) RNG | (1) Calibrate: ")): | |
calibrate() | |
else: | |
advance_conversion() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment