Files
LiberPrayground/playground.py
2021-01-14 00:28:11 +01:00

294 lines
9.2 KiB
Python
Executable File

#!/usr/bin/env python3
from RuneSolver import VigenereSolver
from RuneText import alphabet, RuneText
import lib as LIB
SOLVER = VigenereSolver()
SOLVER.input.load(file='_input.txt')
def main():
help_str = '''
Welcome pilgrim,
Available commands are:
a : Generate all 29 rotations of a given rune-word (a) or inverted rune (ai)
d : Get decryption key (substitution) for a single phrase
f : Find words with a given length (f 4, or f word)
g : Print Gematria Primus (gp) or reversed Gematria (gpr)
h : Highlight occurrences of interrupt jumps (hj) or reset (h)
k : Re/set decryption key (k), invert key (ki),
': change key shift (ks), rotation (kr), offset (ko), or after padding (kp)
': set key jumps (kj) e.g., [1,2] (first appearence of ᚠ is index 1)
l : Toggle log level: normal (ln), quiet (lq) verbose (lv)
p : Prime number and emirp check
t : Translate between runes, text, and indices (0-28)
x : Execute decryption. Also: load data into memory
': set manually (x DATA) or load from file (xf p0-2) (default: _input.txt)
': limit/trim loaded data up to nth character (xl 300)
? : Show currently set variables
q : Quit
'''
print(help_str)
while True:
try:
inpt = input('$>: ').strip()
if not inpt:
continue
cmd_p = inpt.split(' ', 1)
cmd = cmd_p[0].strip().lower()
args = cmd_p[1].strip() if len(cmd_p) > 1 else ''
if cmd[0] != 'l': # only log mode allowed after find operation
SOLVER.reset_highlight()
if cmd == 'help':
print(help_str)
elif cmd == 'q' or cmd == 'exit' or cmd == 'quit':
exit()
elif cmd == '?':
print(SOLVER)
else:
cmdX = {'a': command_a, 'd': command_d, 'f': command_f,
'g': command_g, 'h': command_h, 'k': command_k,
'l': command_l, 'p': command_p, 't': command_t,
'x': command_x}
res = cmdX[cmd[0]](cmd, args) if cmd[0] in cmdX else False
if res is False:
print('Command not found.')
except Exception as e:
print(e)
# this allow you to write `ks3` instead of `ks 3`
def get_cmd_int(cmd, args, desc=None, start=2):
val = int(args) if args else 0
if not val and cmd[start:]:
val = int(cmd[start:])
if desc:
print(f'set {desc}: {val}')
return val
#########################################
# A
#########################################
def command_a(cmd, args): # [a]ll variations
if cmd.strip('aliq'):
return False
if not args:
raise Warning('No input provided.')
root = RuneText(args)
inclIndex = 'q' not in cmd
if 'i' in cmd:
root = 28 - root
for i in range(0, 29):
print('{:02d}: {}'.format(i, (root + i).description(index=inclIndex)))
#########################################
# D
#########################################
def command_d(cmd, args): # [d]ecrypt or single substitution
if not (cmd in 'decrypt'):
return False
if not args:
trythis = input('What is the encrypted text?: ').strip()
else:
trythis = args
enc = RuneText(trythis)
print('encrypted:', enc.description())
target = input('What should the decrypted clear text be?: ').strip()
plain = RuneText(target)
print('plaintext:', plain.description())
if len(enc) != len(plain):
print('Error: key length mismatch')
else:
print('Substition:')
print((enc - plain).description())
#########################################
# F
#########################################
def command_f(cmd, args): # (f)ind word
if not (cmd in 'find'):
return False
if not args:
raise Warning('No input provided.')
try:
search_term = None
s_len = int(args.strip()) # search words with n-length
except ValueError:
search_term = RuneText(args)
s_len = len(search_term)
cur_words = SOLVER.highlight_words_with_len(s_len)
SOLVER.run()
print()
print('Found:')
for _, _, pos, _, w in cur_words:
print('{:04}: {}'.format(pos, w.description(count=True)))
if search_term:
print()
print('Available substition:')
for _, _, pos, _, w in cur_words:
word = search_term - w
print('{:04}: {}'.format(pos, word.description(count=True)))
#########################################
# G
#########################################
def command_g(cmd, args): # (g)ematria primus
if cmd not in 'gp gpr gpi':
return False
def pp(i):
p, r, t = alphabet[28 - i if rev else i]
return '{:2d} {} {:3d} {}'.format(i, r, p, '/'.join(t))
rev = cmd[-1] in 'ir' or len(args) > 0 and args[0] in 'ir'
print('Gematria Primus (reversed)' if rev else 'Gematria Primus')
half = (len(alphabet) >> 1) + (len(alphabet) & 1) # ceil
for i in range(half):
if i < len(alphabet) // 2:
print('{:22} {}'.format(pp(i), pp(i + half)))
else:
print(pp(i))
#########################################
# H
#########################################
def command_h(cmd, args): # (h)ighlight
if cmd == 'h':
SOLVER.reset_highlight()
SOLVER.run()
elif cmd in 'hj hi':
res = SOLVER.highlight_interrupt()
SOLVER.run()
txt = ''
bits = ''
# first appearance of ᚠ is l_pos == 1; r_pos is the index on runes only
for l_pos, r_pos, _, is_set in res:
txt += '{}.{}.{} '.format(l_pos, 'T' if is_set else 'F', r_pos)
bits += '1' if is_set else '0'
print(f'\nInterrupts: {bits}\n{txt}')
else:
return False
#########################################
# K
#########################################
def command_k(cmd, args): # (k)ey manipulation
if cmd == 'k' or cmd == 'key':
SOLVER.KEY_DATA = [x.index for x in RuneText(args)]
print('set key: {}'.format(SOLVER.KEY_DATA))
elif cmd[1] == 's':
SOLVER.KEY_SHIFT = get_cmd_int(cmd, args, 'shift')
elif cmd[1] == 'r':
SOLVER.KEY_ROTATE = get_cmd_int(cmd, args, 'rotation')
elif cmd[1] == 'o':
SOLVER.KEY_OFFSET = get_cmd_int(cmd, args, 'offset')
elif cmd[1] == 'p':
SOLVER.KEY_POST_PAD = get_cmd_int(cmd, args, 'post padding')
elif cmd[1] == 'i':
SOLVER.KEY_INVERT = not SOLVER.KEY_INVERT
print('set key invert: {}'.format(SOLVER.KEY_INVERT))
elif cmd[1] == 'j':
args = args.strip('[]')
pos = [int(x) for x in args.split(',')] if args else []
SOLVER.INTERRUPT_POS = pos
print('set interrupt jumps: {}'.format(pos))
else:
return False # command not found
SOLVER.run()
#########################################
# L
#########################################
def command_l(cmd, args): # (l)og level
if cmd == 'lv' or args == 'v' or args == 'verbose':
SOLVER.output.VERBOSE = not SOLVER.output.VERBOSE
elif cmd == 'lq' or args == 'q' or args == 'quiet':
SOLVER.output.QUIET = not SOLVER.output.QUIET
elif cmd == 'ln' or args == 'n' or args == 'normal':
SOLVER.output.VERBOSE = False
SOLVER.output.QUIET = False
else:
return False
SOLVER.run()
#########################################
# P
#########################################
def command_p(cmd, args): # (p)rime test
if args and cmd not in 'prime':
return False
p = get_cmd_int(cmd, args, start=1)
print(p, ':', LIB.is_prime(p))
print(LIB.rev(p), ':', LIB.is_prime(LIB.rev(p)))
#########################################
# T
#########################################
def command_t(cmd, args): # (t)ranslate
if cmd != 't':
return False
word = RuneText(args)
rev_word = 28 - word
word = word.as_dict()
psum = sum(word['p'])
sffx = '*' if LIB.is_prime(psum) else ''
if LIB.is_prime(LIB.rev(psum)):
sffx += ''
print('runes({}): {}'.format(len(word['r']), word['r']))
print('plain({}): {}'.format(len(word['t']), word['t']))
print('reversed: {}'.format(''.join([x.rune for x in rev_word])))
print('indices: {}'.format(word['i']))
print('prime({}{}): {}'.format(psum, sffx, word['p']))
#########################################
# X
#########################################
def command_x(cmd, args): # e(x)ecute decryption
if cmd == 'x':
pass # just run the solver
elif cmd == 'xf': # reload from file
file = f'pages/{args}.txt' if args else '_input.txt'
print('loading file:', file)
SOLVER.input.load(file=file)
args = None # so run() won't override data
elif len(cmd) > 0 and cmd[1] == 'l': # limit content
limit = get_cmd_int(cmd, args, 'read limit')
last_file = SOLVER.input.loaded_file
if last_file:
SOLVER.input.load(file=last_file)
if limit > 0:
SOLVER.input.data.trim(limit)
args = None
else:
return False
SOLVER.run(args if args else None)
if __name__ == '__main__':
main()