Last active
August 24, 2023 06:01
-
-
Save fractal161/85e8604131ec5e26797d1e6d7685af60 to your computer and use it in GitHub Desktop.
Generates bunch of score uncap codes for NES Tetris
This file contains 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
letter_order = 'APZLGITYEOXUKSVN' | |
gg_order = [ | |
['A', 'E', 'P', 'O', 'Z', 'X', 'L', 'U'], | |
['G', 'K', 'I', 'S', 'T', 'V', 'Y', 'N'], | |
] | |
# returns the two game genie codes associated with the given input | |
def create_code(addr, data, cmp=None): | |
indices = [] | |
if cmp == None: | |
# generate 6 digit gg code | |
indices.append(((data >> 4) & 8) | (data & 7)) | |
indices.append(((addr >> 4) & 8) | ((data >> 4) & 7)) | |
indices.append(((addr >> 4) & 7)) | |
indices.append((addr & 8) | ((addr >> 12) & 7)) | |
indices.append(((addr >> 8) & 8) | (addr & 7)) | |
indices.append((data & 8) | ((addr >> 8) & 7)) | |
code1 = ''.join(letter_order[i] for i in indices) | |
indices[2] ^= 8 | |
code2 = ''.join(letter_order[i] for i in indices) | |
return code1, code2 | |
# generate 8 digit gg code | |
indices.append(((data >> 4) & 8) | (data & 7)) | |
indices.append(((addr >> 4) & 8) | ((data >> 4) & 7)) | |
indices.append(((addr >> 4) & 7)) | |
indices.append((addr & 8) | ((addr >> 12) & 7)) | |
indices.append(((addr >> 8) & 8) | (addr & 7)) | |
indices.append((cmp & 8) | ((addr >> 8) & 7)) | |
indices.append(((cmp >> 4) & 8) | (cmp & 7)) | |
indices.append((data & 8) | ((cmp >> 4) & 7)) | |
code1 = ''.join(letter_order[i] for i in indices) | |
indices[2] ^= 8 | |
code2 = ''.join(letter_order[i] for i in indices) | |
return code1, code2 | |
# computes the number of horizontal movements needed to enter the given code | |
# on an everdrive | |
def ev_metric(code): | |
score = 0 | |
for c in code: | |
index = letter_order.find(c.upper()) | |
if index == -1: | |
raise ValueError('Invalid game genie character') | |
score += min(index + 1, len(letter_order) - index) | |
return score | |
# computes the total distance traveled when entering the given code | |
# on a game genie | |
def gg_metric(code): | |
# returns the location of a letter on the gg grid as (y, x) | |
def gg_pos(c): | |
for i in range(2): | |
if c in gg_order[i]: | |
return (i, gg_order[i].index(c)) | |
raise ValueError('Invalid game genie character') | |
# computes the chebyshev distance between two letters on the gg grid | |
def gg_chebyshev(a, b): | |
ay, ax = gg_pos(a) | |
by, bx = gg_pos(b) | |
return max(abs(ay - by), abs(ax - bx)) | |
score = 0 | |
prev_letter = 'A' | |
for c in code: | |
score += gg_chebyshev(prev_letter, c) | |
prev_letter = c | |
return score | |
if __name__ == '__main__': | |
gg_distances = {} | |
ev_distances = {} | |
num_codes = 0 | |
# helper function for updating maps from codes to distances | |
# bad practice obviously but this script is small so it's fine | |
def update_codes(addr, data, cmp=None): | |
# create 6 digit codes | |
codes = list(create_code(addr, data)) | |
# if optional compare value specified, create 8 digit codes | |
if cmp != None: | |
codes.extend(list(create_code(addr, data, cmp))) | |
for code in codes: | |
gg = gg_metric(code) | |
ev = ev_metric(code) | |
gg_distances[code] = gg | |
ev_distances[code] = ev | |
print(code, ev, gg) | |
# strategy 1: raise the compare threshold | |
for i in range(0xf1, 0x100): | |
update_codes(0x9c89, i, 0xa0) | |
num_codes += 4 | |
# strategy 2: change the compare mask | |
for i in range(0, 0xa0): | |
update_codes(0x9c87, i, 0xf0) | |
num_codes += 4 | |
# strategy 3: change the compare data | |
unused_ram = [ | |
0x03, 0x04, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, | |
0x0f, 0x10, 0x11, 0x12, 0x13, 0x16, 0x1b, 0x1c, 0x1d, 0x1e, | |
0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, | |
0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, | |
0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, | |
0x3f, 0x43, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x63, 0x7b, 0x7c, | |
0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, | |
0x87, 0x88, 0x89, 0x8a ,0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, | |
0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, | |
0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xb4, 0xe5, 0xe8, 0xe9, 0xf0, | |
0xf9, 0xfa, | |
] | |
used_but_small_ram = [ | |
0x19, 0x33, 0x34, 0x40, 0x41, 0x42, 0x46, 0x47, 0x48, | |
0x4A, 0x4F, 0x50, 0x53, 0x54, 0x60, 0x61, 0x62, 0x66, | |
0x67, 0x68, 0x6A, 0x6F, 0x70, 0x73, 0x74, | |
] | |
for i in unused_ram + used_but_small_ram: | |
update_codes(0x9c85, i, 0x55) | |
num_codes += 4 | |
print() | |
print(num_codes, 'codes found') | |
print() | |
# compute best/worst codes for each metric | |
print('Best codes for game genie:') | |
min_gg_distance = min(gg_distances.values()) | |
min_gg_codes = filter(lambda k: gg_distances[k] == min_gg_distance, | |
gg_distances.keys()) | |
for code in min_gg_codes: | |
print(code, ev_metric(code), gg_metric(code)) | |
print('Worst codes for game genie:') | |
max_gg_distance = max(gg_distances.values()) | |
max_gg_codes = filter(lambda k: gg_distances[k] == max_gg_distance, | |
gg_distances.keys()) | |
for code in max_gg_codes: | |
print(code, ev_metric(code), gg_metric(code)) | |
print() | |
print('Best codes for everdrive:') | |
min_ev_distance = min(ev_distances.values()) | |
min_ev_codes = filter(lambda k: ev_distances[k] == min_ev_distance, | |
ev_distances.keys()) | |
for code in min_ev_codes: | |
print(code, ev_metric(code), gg_metric(code)) | |
print('Worst codes for everdrive:') | |
max_ev_distance = max(ev_distances.values()) | |
max_ev_codes = filter(lambda k: ev_distances[k] == max_ev_distance, | |
ev_distances.keys()) | |
for code in max_ev_codes: | |
print(code, ev_metric(code), gg_metric(code)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment