AusPostCode/chicanery.py

209 lines
4.9 KiB
Python
Executable File

#!/usr/bin/env python3
import math
import string
chars = string.digits + string.ascii_letters
def to_base(num, base):
if base > len(chars):
raise ValueError(f"Base must be less than {len(chars)}")
if abs(num) == 0:
return str(num)
else:
negative = num < 0
num = abs(num)
digits = []
while num:
digits.append(chars[num % base])
num //= base
return ('-' if negative else '') + ''.join(digits[::-1])
test_me = {
"023": "a",
"030": "b",
"031": "c",
"032": "d",
"033": "e",
"103": "f",
"113": "g",
"123": "h",
"130": "i",
"131": "j",
"132": "k",
"133": "l",
"203": "m",
"213": "n",
"223": "o",
"230": "p",
"231": "q",
"232": "r",
"233": "s",
"303": "t",
"313": "u",
"323": "v",
"330": "w",
"331": "x",
"332": "y",
"333": "z"
}
for i, code in enumerate(test_me.keys()):
# the lowercase letter codes use a pattern that repeats every seven letters.
# starting from 030Q (12D), the code increments by one three times, then increments by four four times.
# firstly, increase the output by 16 for every 7 in the input.
# then, if i % 7 is 0, 1, 2, or 3, add that number to the output.
# otherwise, subtract 3 from i % 7, multiply the result by 4, and add that to the output.
# finally, add 11, because that's where the lowercase chars start.
out = i // 7 * 16 + max((i % 7 - 3) * 4, i % 7) + 11
check = int(code, 4)
print(f"In: {i:03}\tOut: {out:03}\tCode: {to_base(out, 4).zfill(3)} {('GOOD' if check == out else 'FAIL')}")
print("\n===================\n")
for i, code in enumerate(test_me.keys()):
# now to do the process in reverse - converting a letter code to an index.
# first we subtract 11 to get the codes to start from zero.
x = int(code, 4) - 11
# first, add 7 for every 16 in the input.
# next, we can use (x + 10) % 16 to get the sequence '10, 11, 12, 13, 14, 2, 6'.
# we can use min() to replace the 2 with a 5, and then mod the whole sequence by 10 to get 0 through 6 repeating.
# now we just need to add the two together.
out = x // 16 * 7 + (max((x + 10) % 16, 5) % 10)
character = string.ascii_lowercase[out] if out < len(string.ascii_lowercase) else '?'
print(f"In: {x:03}\tOut: {out:03}\tGoal: {i:03}\tChar: {character} {('GOOD' if character == test_me[code] else 'FAIL')}")
exit(0)
headings = {
"QUAD": "",
"SEPT": "",
"GOAL7": "",
"DEC": "₁₀",
"GOAL": "₁₀",
"ORIG": "₁₀",
"SXTNS": "₁₀",
"FOURS": "₁₀",
"MOD16": "₁₀",
"MOD7": "",
"MOD8": "",
"MOD4": "",
"MAGIC": ""
}
out = ""
for heading in headings:
out += f"{heading:8}"
print(out)
print("=" * 8 * len(headings))
goal = 2
successes = 0
for key in test_me.keys():
# breaks on i in [31, 46, 47, 51, 61, 62, 63]
#
original_value = int(key, 4)
# the code starts at 023₄, which is 11₁₀, so we subtract 11₁₀ as an initial offset
x = original_value - 1
sixteens = (x // 16)
eights = (x // 8)
mod_sixteen = x % 16
mod_eight = x % 8
fours = (x // 4)
mod_sixteen_mod_seven = mod_sixteen % 7
# magic = abs(-8 + ((mod_eight // mod_sixteen) * (16 * sixteens + 8 + sixteens)))
magic = 8
letter_index = ((7 * sixteens) + (x % 8)) - ((1 - (mod_sixteen // 8)) * (2 + 3 * (fours % 2)))
output = [
key,
to_base(letter_index, 7),
to_base(goal, 7),
letter_index,
goal,
x,
sixteens,
fours,
mod_sixteen,
mod_sixteen_mod_seven,
mod_eight,
x % 4,
magic
]
colour_code = 91
if goal == letter_index:
successes += 1
colour_code = 0
print(f'\033[{colour_code}m', end='')
out = ""
for i, suffix in enumerate(headings.values()):
value = str(output[i]).zfill(3) + suffix
out += f"{value:8}"
print(out)
goal += 1
print(f"Succeeded on {successes} of {goal} attempts ({math.floor(successes / float(goal) * 100)}%)")
exit(0)
outputs = {
"decimals": [],
"indices": [],
"mod_fours": [],
"divided_by_sixteens": [],
"indices_plus_mod_fours": [],
"actual_indices": [],
"correctness_checks": []
}
position = 0
for x, expected_value in test_me.items():
wacky = " [WACKY]" if x[1] != "3" else ""
num = int(x, 4) - 11
index = (num // 4)
mod_four = num % 4
divided_by_sixteen = ((num) // 16)
# actual_index = index + mod_four + (3 * divided_by_sixteen)
actual_index = (num // 4) + (num % 4) + (3 * ((num - 4) // 16)) + 3
# actual_index = (num // 16) * 7 + (num % 4)
correctness_check = "GUD" if actual_index == position else "OOP"
outputs["decimals"].append(f"{num:3d}")
outputs["indices"].append(f"{index:3d}")
outputs["mod_fours"].append(f"{mod_four:3d}")
outputs["divided_by_sixteens"].append(f"{divided_by_sixteen:3d}")
outputs["indices_plus_mod_fours"].append(f"{mod_four + index:3d}")
outputs["actual_indices"].append(f"{actual_index:3d}")
outputs["correctness_checks"].append(correctness_check)
position += 1
# print(f"{i}Q = {num}D{wacky}: {index}")
print(' '.join(test_me.keys()))
print(' ' + ' '.join(test_me.values()))
# ignored_outputs = [""]
for key, numbers in outputs.items():
print(' '.join(numbers) + f" ({key})")