module refactoring + allow word mistakes for OEIS search
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -4,4 +4,4 @@ data/oeis.txt
|
|||||||
other/ec-*.png
|
other/ec-*.png
|
||||||
other/ec-*.txt
|
other/ec-*.txt
|
||||||
other/list-onions.txt
|
other/list-onions.txt
|
||||||
out/
|
tmp/
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import re
|
|
||||||
from RuneText import RuneText
|
|
||||||
from NGrams import NGrams
|
from NGrams import NGrams
|
||||||
|
from RuneText import RUNES
|
||||||
|
|
||||||
|
|
||||||
def normalized_probability(int_prob):
|
def normalized_probability(int_prob):
|
||||||
@@ -9,8 +8,6 @@ def normalized_probability(int_prob):
|
|||||||
return [x / total for x in int_prob] # math.log(x / total, 10)
|
return [x / total for x in int_prob] # math.log(x / total, 10)
|
||||||
|
|
||||||
|
|
||||||
RUNES = 'ᚠᚢᚦᚩᚱᚳᚷᚹᚻᚾᛁᛄᛇᛈᛉᛋᛏᛒᛖᛗᛚᛝᛟᛞᚪᚫᚣᛡᛠ'
|
|
||||||
re_norune = re.compile('[^' + RUNES + ']')
|
|
||||||
PROB_INT = [0] * 29
|
PROB_INT = [0] * 29
|
||||||
for k, v in NGrams.load(1, '').items(): # '-no-e', '-solved'
|
for k, v in NGrams.load(1, '').items(): # '-no-e', '-solved'
|
||||||
PROB_INT[RUNES.index(k)] = v
|
PROB_INT[RUNES.index(k)] = v
|
||||||
@@ -53,24 +50,3 @@ class Probability(object):
|
|||||||
val = sum(abs(Probability(nums[x::keylen]).IC() - target_ioc)
|
val = sum(abs(Probability(nums[x::keylen]).IC() - target_ioc)
|
||||||
for x in range(keylen))
|
for x in range(keylen))
|
||||||
return 1 - (val / keylen)
|
return 1 - (val / keylen)
|
||||||
|
|
||||||
|
|
||||||
#########################################
|
|
||||||
# load page and convert to indices for faster access
|
|
||||||
#########################################
|
|
||||||
|
|
||||||
def load_indices(fname, interrupt, maxinterrupt=None, minlen=None, limit=None):
|
|
||||||
with open(fname, 'r') as f:
|
|
||||||
data = RuneText(re_norune.sub('', f.read())).index[:limit]
|
|
||||||
if maxinterrupt is not None:
|
|
||||||
# incl. everything up to but not including next interrupt
|
|
||||||
# e.g., maxinterrupt = 0 will return text until first interrupt
|
|
||||||
for i, x in enumerate(data):
|
|
||||||
if x != interrupt:
|
|
||||||
continue
|
|
||||||
if maxinterrupt == 0:
|
|
||||||
if minlen and i < minlen:
|
|
||||||
continue
|
|
||||||
return data[:i]
|
|
||||||
maxinterrupt -= 1
|
|
||||||
return data
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import itertools # product, compress, combinations
|
import itertools # product, compress, combinations
|
||||||
import bisect # bisect_left, insort
|
import bisect # bisect_left, insort
|
||||||
import lib as LIB
|
from lib import affine_decrypt
|
||||||
|
|
||||||
|
|
||||||
#########################################
|
#########################################
|
||||||
@@ -45,7 +45,7 @@ class GuessAffine(object):
|
|||||||
best = 9999999
|
best = 9999999
|
||||||
for s in range(29):
|
for s in range(29):
|
||||||
for t in range(29):
|
for t in range(29):
|
||||||
shifted = [LIB.affine_decrypt(x, (s, t))
|
shifted = [affine_decrypt(x, (s, t))
|
||||||
for x in self.nums[offset::keylength]]
|
for x in self.nums[offset::keylength]]
|
||||||
score = score_fn(shifted)
|
score = score_fn(shifted)
|
||||||
if score < best:
|
if score < best:
|
||||||
@@ -1,15 +1,9 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import os
|
import os
|
||||||
from HeuristicSearch import SearchInterrupt
|
from HeuristicSearch import SearchInterrupt
|
||||||
from HeuristicLib import load_indices, Probability
|
from HeuristicLib import Probability
|
||||||
|
from RuneText import RUNES, load_indices
|
||||||
RUNES = 'ᚠᚢᚦᚩᚱᚳᚷᚹᚻᚾᛁᛄᛇᛈᛉᛋᛏᛒᛖᛗᛚᛝᛟᛞᚪᚫᚣᛡᛠ' # used in InterruptToWeb
|
from LPath import FILES_ALL, FILES_UNSOLVED, LPath
|
||||||
FILES_SOLVED = ['0_warning', '0_welcome', '0_wisdom', '0_koan_1',
|
|
||||||
'0_loss_of_divinity', 'jpg107-167', 'jpg229',
|
|
||||||
'p56_an_end', 'p57_parable']
|
|
||||||
FILES_UNSOLVED = ['p0-2', 'p3-7', 'p8-14', 'p15-22', 'p23-26',
|
|
||||||
'p27-32', 'p33-39', 'p40-53', 'p54-55']
|
|
||||||
FILES_ALL = FILES_UNSOLVED + FILES_SOLVED
|
|
||||||
|
|
||||||
|
|
||||||
#########################################
|
#########################################
|
||||||
@@ -85,10 +79,10 @@ class InterruptDB(object):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def load(dbname):
|
def load(dbname):
|
||||||
if not os.path.isfile(f'InterruptDB/{dbname}.txt'):
|
if not os.path.isfile(LPath.InterruptDB(dbname)):
|
||||||
return {}
|
return {}
|
||||||
ret = {}
|
ret = {}
|
||||||
with open(f'InterruptDB/{dbname}.txt', 'r') as f:
|
with open(LPath.InterruptDB(dbname), 'r') as f:
|
||||||
for line in f.readlines():
|
for line in f.readlines():
|
||||||
if line.startswith('#'):
|
if line.startswith('#'):
|
||||||
continue
|
continue
|
||||||
@@ -104,7 +98,7 @@ class InterruptDB(object):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def write(name, score, irp, irpmax, keylen, nums, dbname='db_main'):
|
def write(name, score, irp, irpmax, keylen, nums, dbname='db_main'):
|
||||||
with open(f'InterruptDB/{dbname}.txt', 'a') as f:
|
with open(LPath.InterruptDB(dbname), 'a') as f:
|
||||||
nums = ','.join(map(str, nums))
|
nums = ','.join(map(str, nums))
|
||||||
f.write(f'{name}|{irpmax}|{score:.5f}|{irp}|{keylen}|{nums}\n')
|
f.write(f'{name}|{irpmax}|{score:.5f}|{irp}|{keylen}|{nums}\n')
|
||||||
|
|
||||||
@@ -134,7 +128,7 @@ class InterruptIndices(object):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def write(dbname='db_indices'):
|
def write(dbname='db_indices'):
|
||||||
with open(f'InterruptDB/{dbname}.txt', 'w') as f:
|
with open(LPath.InterruptDB(dbname), 'w') as f:
|
||||||
f.write('# file | total runes in file | interrupt | indices\n')
|
f.write('# file | total runes in file | interrupt | indices\n')
|
||||||
for name in FILES_ALL:
|
for name in FILES_ALL:
|
||||||
fname = f'pages/{name}.txt'
|
fname = f'pages/{name}.txt'
|
||||||
@@ -149,7 +143,7 @@ class InterruptIndices(object):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def read(dbname='db_indices'):
|
def read(dbname='db_indices'):
|
||||||
with open(f'InterruptDB/{dbname}.txt', 'r') as f:
|
with open(LPath.InterruptDB(dbname), 'r') as f:
|
||||||
ret = {}
|
ret = {}
|
||||||
for line in f.readlines():
|
for line in f.readlines():
|
||||||
if line.startswith('#'):
|
if line.startswith('#'):
|
||||||
@@ -169,8 +163,9 @@ class InterruptIndices(object):
|
|||||||
#########################################
|
#########################################
|
||||||
|
|
||||||
class InterruptToWeb(object):
|
class InterruptToWeb(object):
|
||||||
def __init__(self, dbname, template='InterruptDB/template.html'):
|
def __init__(self, dbname, template='template.html'):
|
||||||
self.template = template
|
with open(LPath.results(template), 'r') as f:
|
||||||
|
self.template = f.read()
|
||||||
self.indices = InterruptIndices()
|
self.indices = InterruptIndices()
|
||||||
self.scores = {}
|
self.scores = {}
|
||||||
db = InterruptDB.load(dbname)
|
db = InterruptDB.load(dbname)
|
||||||
@@ -252,8 +247,6 @@ class InterruptToWeb(object):
|
|||||||
return f'<table>{trh}{"".join(trd[1:])}{trbest}</table>'
|
return f'<table>{trh}{"".join(trd[1:])}{trbest}</table>'
|
||||||
|
|
||||||
def make(self, outfile, pmin=1.25, pmax=1.65):
|
def make(self, outfile, pmin=1.25, pmax=1.65):
|
||||||
with open(self.template, 'r') as f:
|
|
||||||
html = f.read()
|
|
||||||
nav = ''
|
nav = ''
|
||||||
txt = ''
|
txt = ''
|
||||||
for i in range(29):
|
for i in range(29):
|
||||||
@@ -263,10 +256,10 @@ class InterruptToWeb(object):
|
|||||||
nav += f'<a href="#tb-i{i}">{RUNES[i]}</a>\n'
|
nav += f'<a href="#tb-i{i}">{RUNES[i]}</a>\n'
|
||||||
txt += f'<h3 id="tb-i{i}">Interrupt {i}: <b>{RUNES[i]}</b></h3>'
|
txt += f'<h3 id="tb-i{i}">Interrupt {i}: <b>{RUNES[i]}</b></h3>'
|
||||||
txt += self.table_interrupt(i, pmin, pmax)
|
txt += self.table_interrupt(i, pmin, pmax)
|
||||||
html = html.replace('__NAVIGATION__', nav)
|
html = self.template.replace('__NAVIGATION__', nav)
|
||||||
html = html.replace('__TAB_RELIABLE__', self.table_reliable())
|
html = html.replace('__TAB_RELIABLE__', self.table_reliable())
|
||||||
html = html.replace('__INTERRUPT_TABLES__', txt)
|
html = html.replace('__INTERRUPT_TABLES__', txt)
|
||||||
with open(outfile, 'w') as f:
|
with open(LPath.results(outfile), 'w') as f:
|
||||||
f.write(html)
|
f.write(html)
|
||||||
|
|
||||||
|
|
||||||
@@ -279,12 +272,10 @@ def create_initial_db(dbname, minkl=1, maxkl=32, max_irp=20, irpset=range(29)):
|
|||||||
oldValues = {k: set((a, b, c) for a, _, b, c, _ in v)
|
oldValues = {k: set((a, b, c) for a, _, b, c, _ in v)
|
||||||
for k, v in oldDB.items()}
|
for k, v in oldDB.items()}
|
||||||
for irp in irpset: # interrupt rune index
|
for irp in irpset: # interrupt rune index
|
||||||
# for name in FILES_UNSOLVED:
|
|
||||||
for name in FILES_ALL:
|
for name in FILES_ALL:
|
||||||
fname = f'pages/{name}.txt'
|
data = load_indices(LPath.page(name), irp, maxinterrupt=max_irp)
|
||||||
data = load_indices(fname, irp, maxinterrupt=max_irp)
|
|
||||||
db = InterruptDB(data, irp)
|
db = InterruptDB(data, irp)
|
||||||
print('load:', fname, 'interrupt:', irp, 'count:', db.irp_count)
|
print('load:', name, 'interrupt:', irp, 'count:', db.irp_count)
|
||||||
for keylen in range(minkl, maxkl + 1): # key length
|
for keylen in range(minkl, maxkl + 1): # key length
|
||||||
if (db.irp_count, irp, keylen) in oldValues.get(name, []):
|
if (db.irp_count, irp, keylen) in oldValues.get(name, []):
|
||||||
print(f'{keylen}: skipped.')
|
print(f'{keylen}: skipped.')
|
||||||
@@ -305,9 +296,8 @@ def find_secondary_solutions(db_in, db_out, threshold=0.75, max_irp=20):
|
|||||||
search_set.add((name, irp, kl))
|
search_set.add((name, irp, kl))
|
||||||
print('searching through', len(search_set), 'files.')
|
print('searching through', len(search_set), 'files.')
|
||||||
for name, irp, kl in search_set:
|
for name, irp, kl in search_set:
|
||||||
fname = f'pages/{name}.txt'
|
print('load:', name, 'interrupt:', irp, 'keylen:', kl)
|
||||||
print('load:', fname, 'interrupt:', irp, 'keylen:', kl)
|
data = load_indices(LPath.page(name), irp, maxinterrupt=max_irp)
|
||||||
data = load_indices(fname, irp, maxinterrupt=max_irp)
|
|
||||||
db = InterruptDB(data, irp)
|
db = InterruptDB(data, irp)
|
||||||
c = db.make_secondary(db_out, name, kl, threshold)
|
c = db.make_secondary(db_out, name, kl, threshold)
|
||||||
print('found', c, 'additional solutions')
|
print('found', c, 'additional solutions')
|
||||||
@@ -317,6 +307,5 @@ if __name__ == '__main__':
|
|||||||
# find_secondary_solutions('db_high', 'db_high_secondary', threshold=1.4)
|
# find_secondary_solutions('db_high', 'db_high_secondary', threshold=1.4)
|
||||||
# find_secondary_solutions('db_norm', 'db_norm_secondary', threshold=0.55)
|
# find_secondary_solutions('db_norm', 'db_norm_secondary', threshold=0.55)
|
||||||
# create_initial_db('db_norm', minkl=1, maxkl=32, max_irp=20)
|
# create_initial_db('db_norm', minkl=1, maxkl=32, max_irp=20)
|
||||||
# InterruptToWeb('db_high').make('InterruptDB/index_high.html')
|
# InterruptToWeb('db_high').make('index_high.html')
|
||||||
InterruptToWeb('db_norm').make(
|
InterruptToWeb('db_norm').make('index_norm.html', pmin=0.40, pmax=0.98)
|
||||||
'InterruptDB/index_norm.html', pmin=0.40, pmax=0.98)
|
|
||||||
38
LP/LPath.py
Executable file
38
LP/LPath.py
Executable file
@@ -0,0 +1,38 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import os.path
|
||||||
|
|
||||||
|
FILES_SOLVED = ['0_warning', '0_welcome', '0_wisdom', '0_koan_1',
|
||||||
|
'0_loss_of_divinity', 'jpg107-167', 'jpg229',
|
||||||
|
'p56_an_end', 'p57_parable']
|
||||||
|
FILES_UNSOLVED = ['p0-2', 'p3-7', 'p8-14', 'p15-22', 'p23-26',
|
||||||
|
'p27-32', 'p33-39', 'p40-53', 'p54-55']
|
||||||
|
FILES_ALL = FILES_UNSOLVED + FILES_SOLVED
|
||||||
|
|
||||||
|
LP_MODULE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
LP_ROOT_DIR = os.path.relpath(os.path.dirname(LP_MODULE_DIR))
|
||||||
|
|
||||||
|
|
||||||
|
class LPath(object):
|
||||||
|
@staticmethod
|
||||||
|
def root(fname):
|
||||||
|
return os.path.join(LP_ROOT_DIR, fname)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def page(fname):
|
||||||
|
return os.path.join(LP_ROOT_DIR, 'pages', fname + '.txt')
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def data(fname, ext='txt'):
|
||||||
|
return os.path.join(LP_ROOT_DIR, 'data', f'{fname}.{ext}')
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def tmp(fname, ext='txt'):
|
||||||
|
return os.path.join(LP_ROOT_DIR, 'tmp', f'{fname}.{ext}')
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def InterruptDB(fname):
|
||||||
|
return os.path.join(LP_ROOT_DIR, 'InterruptDB', fname + '.txt')
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def results(fname):
|
||||||
|
return os.path.join(LP_ROOT_DIR, 'InterruptDB', fname)
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import re
|
import re
|
||||||
from RuneText import alphabet, RuneText
|
from RuneText import RUNES, re_norune, RuneText
|
||||||
|
from LPath import LPath
|
||||||
|
|
||||||
|
|
||||||
#########################################
|
#########################################
|
||||||
@@ -28,11 +29,10 @@ class NGrams(object):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def make(gramsize, infile, outfile):
|
def make(gramsize, infile, outfile):
|
||||||
allowed_chr = [x[1] for x in alphabet]
|
|
||||||
with open(infile, 'r') as f:
|
with open(infile, 'r') as f:
|
||||||
data = re.sub('[^{}]'.format(''.join(allowed_chr)), '', f.read())
|
data = re_norune.sub('', f.read())
|
||||||
|
|
||||||
res = {x: 0 for x in allowed_chr} if gramsize == 1 else {}
|
res = {x: 0 for x in RUNES} if gramsize == 1 else {}
|
||||||
for i in range(len(data) - gramsize + 1):
|
for i in range(len(data) - gramsize + 1):
|
||||||
ngram = data[i:i + gramsize]
|
ngram = data[i:i + gramsize]
|
||||||
try:
|
try:
|
||||||
@@ -47,19 +47,28 @@ class NGrams(object):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def load(ngram=1, prefix=''):
|
def load(ngram=1, prefix=''):
|
||||||
ret = {}
|
ret = {}
|
||||||
with open(f'data/p{prefix}-{ngram}gram.txt', 'r') as f:
|
with open(LPath.data(f'p{prefix}-{ngram}gram'), 'r') as f:
|
||||||
for line in f.readlines():
|
for line in f.readlines():
|
||||||
r, v = line.split()
|
r, v = line.split()
|
||||||
ret[r] = int(v)
|
ret[r] = int(v)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
# NGrams.translate('data/baseline-text.txt', 'data/baseline-rune.txt', False)
|
def make_translation(stream=False): # if true, ignore spaces / word bounds
|
||||||
# for i in range(1, 6):
|
NGrams.translate(LPath.data('baseline-text'),
|
||||||
# print(f'generate {i}-gram file')
|
LPath.data('baseline-rune'), stream)
|
||||||
# NGrams.make(i, infile='data/baseline-rune-words.txt',
|
|
||||||
# outfile=f'data/p-{i}gram.txt')
|
|
||||||
# NGrams.make(i, infile='_solved.txt',
|
def make_ngrams(max_ngram=1):
|
||||||
# outfile=f'data/p-solved-{i}gram.txt')
|
for i in range(1, max_ngram + 1):
|
||||||
# NGrams.make(i, infile='data/baseline-rune-no-e.txt',
|
print(f'generate {i}-gram file')
|
||||||
# outfile=f'data/p-no-e-{i}gram.txt')
|
NGrams.make(i, infile=LPath.data('baseline-rune-words'),
|
||||||
|
outfile=LPath.data(f'p-{i}gram'))
|
||||||
|
NGrams.make(i, infile=LPath.root('_solved.txt'),
|
||||||
|
outfile=LPath.data(f'p-solved-{i}gram'))
|
||||||
|
NGrams.make(i, infile=LPath.data('baseline-rune-no-e'),
|
||||||
|
outfile=LPath.data(f'p-no-e-{i}gram'))
|
||||||
|
|
||||||
|
|
||||||
|
# make_translation(stream=False)
|
||||||
|
# make_ngrams(5)
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
from RuneText import RuneText
|
|
||||||
import lib as LIB
|
|
||||||
import sys
|
import sys
|
||||||
|
from RuneText import RuneText
|
||||||
|
import lib as utils
|
||||||
|
|
||||||
|
|
||||||
#########################################
|
#########################################
|
||||||
@@ -86,11 +86,11 @@ class RuneReader(object):
|
|||||||
self.loaded_file = None
|
self.loaded_file = None
|
||||||
self.words = {x: [] for x in range(20)} # increase for longer words
|
self.words = {x: [] for x in range(20)} # increase for longer words
|
||||||
|
|
||||||
def load(self, data=None, file=None):
|
def load(self, data=None, file=None, limit=None):
|
||||||
self.loaded_file = None
|
self.loaded_file = None
|
||||||
if not data:
|
if not data:
|
||||||
with open(file, 'r') as f:
|
with open(file, 'r') as f:
|
||||||
data = f.read()
|
data = f.read()[:limit]
|
||||||
self.loaded_file = file
|
self.loaded_file = file
|
||||||
self.data = data if isinstance(data, RuneText) else RuneText(data)
|
self.data = data if isinstance(data, RuneText) else RuneText(data)
|
||||||
self.generate_word_list()
|
self.generate_word_list()
|
||||||
@@ -219,7 +219,7 @@ class RuneRunner(object):
|
|||||||
if self.output.VERBOSE:
|
if self.output.VERBOSE:
|
||||||
self.output.write(n1=('' if is_first else '+') + str(num))
|
self.output.write(n1=('' if is_first else '+') + str(num))
|
||||||
return
|
return
|
||||||
prm = LIB.is_prime(num)
|
prm = utils.is_prime(num)
|
||||||
if typ == 'w': # word
|
if typ == 'w': # word
|
||||||
tt = ('' if is_first else ' + ') + str(num) + ('*' if prm else '')
|
tt = ('' if is_first else ' + ') + str(num) + ('*' if prm else '')
|
||||||
self.output.write(n2=tt)
|
self.output.write(n2=tt)
|
||||||
@@ -229,7 +229,7 @@ class RuneRunner(object):
|
|||||||
self.output.mark = False
|
self.output.mark = False
|
||||||
# if not is_first:
|
# if not is_first:
|
||||||
sffx = ' = {}'.format(num) + ('*' if prm else '')
|
sffx = ' = {}'.format(num) + ('*' if prm else '')
|
||||||
if LIB.is_prime(LIB.rev(num)):
|
if utils.is_emirp(num):
|
||||||
sffx += '√'
|
sffx += '√'
|
||||||
if self.output.VERBOSE:
|
if self.output.VERBOSE:
|
||||||
self.output.write(n2=sffx)
|
self.output.write(n2=sffx)
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
from RuneRunner import RuneRunner
|
from RuneRunner import RuneRunner
|
||||||
from RuneText import Rune, RuneText
|
from RuneText import Rune, RuneText
|
||||||
import lib as LIB
|
from lib import affine_decrypt
|
||||||
|
|
||||||
|
|
||||||
#########################################
|
#########################################
|
||||||
@@ -186,7 +186,7 @@ class VigenereSolver(RunningKeySolver):
|
|||||||
|
|
||||||
class AffineSolver(RunningKeySolver):
|
class AffineSolver(RunningKeySolver):
|
||||||
def decrypt(self, rune_index, key_index):
|
def decrypt(self, rune_index, key_index):
|
||||||
return LIB.affine_decrypt(rune_index, self.KEY_DATA[key_index])
|
return affine_decrypt(rune_index, self.KEY_DATA[key_index])
|
||||||
|
|
||||||
|
|
||||||
#########################################
|
#########################################
|
||||||
@@ -1,4 +1,7 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: UTF-8 -*-
|
||||||
|
import re # load_indices
|
||||||
|
|
||||||
white_rune = {'•': ' ', '⁘': '.', '⁚': ',', '⁖': ';', '⁜': '#'}
|
white_rune = {'•': ' ', '⁘': '.', '⁚': ',', '⁖': ';', '⁜': '#'}
|
||||||
white_text = {v: k for k, v in white_rune.items()}
|
white_text = {v: k for k, v in white_rune.items()}
|
||||||
alphabet = [ # Using last value for display. Custom added: V
|
alphabet = [ # Using last value for display. Custom added: V
|
||||||
@@ -14,8 +17,9 @@ alphabet = [ # Using last value for display. Custom added: V
|
|||||||
]
|
]
|
||||||
text_map = {t: r for _, r, ta in alphabet for t in ta}
|
text_map = {t: r for _, r, ta in alphabet for t in ta}
|
||||||
rune_map = {r: t for _, r, ta in alphabet for t in ta}
|
rune_map = {r: t for _, r, ta in alphabet for t in ta}
|
||||||
index_map = [r for _, r, _ in alphabet] # array already sorted
|
|
||||||
primes_map = {r: p for p, r, _ in alphabet}
|
primes_map = {r: p for p, r, _ in alphabet}
|
||||||
|
RUNES = [r for _, r, _ in alphabet] # array already sorted
|
||||||
|
re_norune = re.compile('[^' + ''.join(RUNES) + ']')
|
||||||
# del alphabet # used in playground for GP display
|
# del alphabet # used in playground for GP display
|
||||||
|
|
||||||
|
|
||||||
@@ -37,7 +41,7 @@ class Rune(object):
|
|||||||
@property
|
@property
|
||||||
def rune(self):
|
def rune(self):
|
||||||
if self._rune is None:
|
if self._rune is None:
|
||||||
self._rune = index_map[self._index] if self._index < 29 else '•'
|
self._rune = RUNES[self._index] if self._index < 29 else '•'
|
||||||
return self._rune
|
return self._rune
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -54,7 +58,7 @@ class Rune(object):
|
|||||||
def index(self):
|
def index(self):
|
||||||
if self._index is None:
|
if self._index is None:
|
||||||
r = self._rune
|
r = self._rune
|
||||||
self._index = index_map.index(r) if r in index_map else 29
|
self._index = RUNES.index(r) if r in RUNES else 29
|
||||||
return self._index
|
return self._index
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -182,37 +186,18 @@ class RuneText(object):
|
|||||||
res.append(Rune(r=rune, t=char))
|
res.append(Rune(r=rune, t=char))
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def as_dict(self):
|
|
||||||
ret = {'r': '', 't': '', 'i': [], 'p': []}
|
|
||||||
for x in self._data:
|
|
||||||
ret['r'] += x.rune
|
|
||||||
ret['t'] += x.text
|
|
||||||
ret['i'].append(x.index)
|
|
||||||
ret['p'].append(x.prime)
|
|
||||||
return ret
|
|
||||||
|
|
||||||
def description(self, count=False, index=True, indexWhitespace=False):
|
def description(self, count=False, index=True, indexWhitespace=False):
|
||||||
if len(self) == 0:
|
return None if len(self) == 0 else \
|
||||||
return None
|
self.rune + (f' ({len(self)})' if count else '') + ' - ' + \
|
||||||
fmt = '{0} ({1}) – {2} ({3})' if count else '{0} – {2}'
|
self.text + (f' ({len(self.text)})' if count else '') + \
|
||||||
d = self.as_dict()
|
(f' - {self.index if indexWhitespace else self.index_rune_only}'
|
||||||
if index:
|
if index else '')
|
||||||
fmt += ' – {4}'
|
|
||||||
if not indexWhitespace:
|
|
||||||
d['i'] = [x for x in d['i'] if x != 29]
|
|
||||||
return fmt.format(d['r'], len(d['r']), d['t'], len(d['t']), d['i'])
|
|
||||||
|
|
||||||
def zip_sub(self, other):
|
def zip_sub(self, other):
|
||||||
if len(self) != len(other):
|
if len(self) != len(other):
|
||||||
raise IndexError('RuneText length mismatch')
|
raise IndexError('RuneText length mismatch')
|
||||||
return RuneText([x - y for x, y in zip(self._data, other._data)])
|
return RuneText([x - y for x, y in zip(self._data, other._data)])
|
||||||
|
|
||||||
@property
|
|
||||||
def rune_sum(self):
|
|
||||||
if self._rune_sum is None:
|
|
||||||
self._rune_sum = sum(x.prime for x in self._data)
|
|
||||||
return self._rune_sum
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def text(self):
|
def text(self):
|
||||||
return ''.join(x.text for x in self._data)
|
return ''.join(x.text for x in self._data)
|
||||||
@@ -226,9 +211,19 @@ class RuneText(object):
|
|||||||
return [x.index for x in self._data if x.kind != 'l']
|
return [x.index for x in self._data if x.kind != 'l']
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def index_no_whitespace(self):
|
def index_rune_only(self):
|
||||||
return [x.index for x in self._data if x.index != 29]
|
return [x.index for x in self._data if x.index != 29]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def prime(self):
|
||||||
|
return [x.prime for x in self._data]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def prime_sum(self):
|
||||||
|
if self._rune_sum is None:
|
||||||
|
self._rune_sum = sum(self.prime)
|
||||||
|
return self._rune_sum
|
||||||
|
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
if isinstance(key, str):
|
if isinstance(key, str):
|
||||||
return [getattr(x, key) for x in self._data]
|
return [getattr(x, key) for x in self._data]
|
||||||
@@ -255,3 +250,24 @@ class RuneText(object):
|
|||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f'RuneText<{len(self._data)}>'
|
return f'RuneText<{len(self._data)}>'
|
||||||
|
|
||||||
|
|
||||||
|
#########################################
|
||||||
|
# load page and convert to indices for faster access
|
||||||
|
#########################################
|
||||||
|
|
||||||
|
def load_indices(fname, interrupt, maxinterrupt=None, minlen=None, limit=None):
|
||||||
|
with open(fname, 'r') as f:
|
||||||
|
data = RuneText(re_norune.sub('', f.read())).index_rune_only[:limit]
|
||||||
|
if maxinterrupt is not None:
|
||||||
|
# incl. everything up to but not including next interrupt
|
||||||
|
# e.g., maxinterrupt = 0 will return text until first interrupt
|
||||||
|
for i, x in enumerate(data):
|
||||||
|
if x != interrupt:
|
||||||
|
continue
|
||||||
|
if maxinterrupt == 0:
|
||||||
|
if minlen and i < minlen:
|
||||||
|
continue
|
||||||
|
return data[:i]
|
||||||
|
maxinterrupt -= 1
|
||||||
|
return data
|
||||||
17
LP/__init__.py
Normal file
17
LP/__init__.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import sys
|
||||||
|
if True:
|
||||||
|
sys.path.append(__path__[0])
|
||||||
|
|
||||||
|
import lib as utils
|
||||||
|
from LPath import FILES_ALL, FILES_UNSOLVED, FILES_SOLVED
|
||||||
|
from LPath import LPath as path
|
||||||
|
from RuneSolver import VigenereSolver, AffineSolver, AutokeySolver, SequenceSolver
|
||||||
|
from RuneText import Rune, RuneText
|
||||||
|
from RuneText import RUNES, alphabet, load_indices
|
||||||
|
|
||||||
|
from HeuristicSearch import GuessVigenere, GuessAffine, GuessPattern
|
||||||
|
from HeuristicSearch import SearchInterrupt
|
||||||
|
from HeuristicLib import Probability
|
||||||
|
from InterruptDB import InterruptDB
|
||||||
|
|
||||||
|
from FailedAttempts import NGramShifter
|
||||||
@@ -1,11 +1,7 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import math
|
import math
|
||||||
|
|
||||||
AFFINE_INV = None
|
|
||||||
|
|
||||||
|
|
||||||
# yes it will report 2,3,5 as non-prime
|
|
||||||
# though why add a check if it will never be tested anyway
|
|
||||||
def is_prime(num):
|
def is_prime(num):
|
||||||
if isinstance(num, str):
|
if isinstance(num, str):
|
||||||
num = int(num)
|
num = int(num)
|
||||||
@@ -30,6 +26,10 @@ def rev(num): # or int(str(num)[::-1])
|
|||||||
return revs
|
return revs
|
||||||
|
|
||||||
|
|
||||||
|
def is_emirp(num):
|
||||||
|
return is_prime(rev(num))
|
||||||
|
|
||||||
|
|
||||||
def power(x, y, p):
|
def power(x, y, p):
|
||||||
res = 1
|
res = 1
|
||||||
x %= p
|
x %= p
|
||||||
@@ -74,6 +74,9 @@ def elliptic_curve(x, a, b, r):
|
|||||||
return y, -y % r
|
return y, -y % r
|
||||||
|
|
||||||
|
|
||||||
|
AFFINE_INV = None
|
||||||
|
|
||||||
|
|
||||||
def affine_inverse(s, n=29):
|
def affine_inverse(s, n=29):
|
||||||
def fn(s, n):
|
def fn(s, n):
|
||||||
g = [n, s]
|
g = [n, s]
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
8, 1.9043, [10, 16, 10, 9, 10, 16, 26, 23], [1, 4, 5, 6, 7, 11, 12, 14, 19, 20, 21, 23, 25]
|
|
||||||
LEOJGTHFE#
|
|
||||||
WELMOME,PILGEIM,TO THE GEEAT JOURNOY TOWARD TE END OF ALC THNGS.
|
|
||||||
IT IS DOT AN EASY URIP,BUT FOE THOSE WHO FAND THEIR WNY HERE IT AS A NECESFARY ONE.
|
|
||||||
ACONG THE WAY JOU WILL FIDD AN END TB ALL STRULGLE AND SSMFMOS,TFUR ANNOCENCO,YOUR ILLSSIANS,YOUE CERTAINUY,AND YOUE REALITY.
|
|
||||||
UCTIMATELJ,YOU WILL HISCOUER NN END TO SOLF.
|
|
||||||
IT IS THRBUGH THIS PALGRIMAGO THAT WE SHNPE OURSECUES AND OSR REALITIOS.
|
|
||||||
JOURNEJ DEEP WITHAN AND YOU NGILL ARRISE OUTSIDO.
|
|
||||||
LICE THE IDSTAR,IT IF ONLY THROSGH GONG WITIN THAT WE RAY EMERGO#
|
|
||||||
WIDSOM#
|
|
||||||
YBU ARE A BEG UNTO YOUESELP.
|
|
||||||
TFU AEE A LAW UNUO YOURSECF.
|
|
||||||
EACH INTECLIGENCE AS HOLY.
|
|
||||||
FOR NLL THAT LISES IS HOLJ#
|
|
||||||
#
|
|
||||||
AN INSTRSCTIAN#
|
|
||||||
COFIEHA OPIAL DUH TAUF#
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
13, 2.1477, [21, 10, 4, 0, 1, 19, 0, 18, 4, 18, 9, 0, 18], [2, 3]
|
|
||||||
O COAN#
|
|
||||||
DURNG A LESDON,THE MASTER EXNGLAINED THE I.
|
|
||||||
"THE I ES THE UOICE OF THE CERCUMFERENCE,"HE DAID.
|
|
||||||
WHEN ASCED AEY A STUDENT TO EOEPLAIN WHAT THAT IAEANT;THE MASTER SOID"IT IS A UOICE ENSIDE YOUR HEAD".
|
|
||||||
"E DON'T HAUE A UOIPE IN MY HEAD," THOUGTT THE STUDENT,ANTH HE RAISED HIS HOND TO TELL THE MADTER.
|
|
||||||
THE MASTER SAOPPED THE STUDEBT,AND SAID"THE UOECE THAT JUST SAITH YOU HAUE NO UOIPE IN YOUR HEAD;IS IE I.
|
|
||||||
"AND THE STUDEBTS WERE ENLIGHAENED#
|
|
||||||
@@ -1,12 +1,8 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
from RuneSolver import VigenereSolver, AffineSolver, AutokeySolver
|
import LP
|
||||||
from RuneText import alphabet, RuneText
|
|
||||||
import lib as LIB
|
|
||||||
|
|
||||||
SOLVER = VigenereSolver()
|
SOLVER = LP.VigenereSolver() # VigenereSolver, AffineSolver, AutokeySolver
|
||||||
# SOLVER = AffineSolver()
|
SOLVER.input.load(file=LP.path.root('_input.txt'))
|
||||||
# SOLVER = AutokeySolver()
|
|
||||||
SOLVER.input.load(file='_input.txt')
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
@@ -82,7 +78,7 @@ def command_a(cmd, args): # [a]ll variations
|
|||||||
return False
|
return False
|
||||||
if not args:
|
if not args:
|
||||||
raise Warning('No input provided.')
|
raise Warning('No input provided.')
|
||||||
root = RuneText(args)
|
root = LP.RuneText(args)
|
||||||
inclIndex = 'q' not in cmd
|
inclIndex = 'q' not in cmd
|
||||||
if 'i' in cmd:
|
if 'i' in cmd:
|
||||||
root = ~root
|
root = ~root
|
||||||
@@ -101,10 +97,10 @@ def command_d(cmd, args): # [d]ecrypt or single substitution
|
|||||||
trythis = input('What is the encrypted text?: ').strip()
|
trythis = input('What is the encrypted text?: ').strip()
|
||||||
else:
|
else:
|
||||||
trythis = args
|
trythis = args
|
||||||
enc = RuneText(trythis)
|
enc = LP.RuneText(trythis)
|
||||||
print('encrypted:', enc.description())
|
print('encrypted:', enc.description())
|
||||||
target = input('What should the decrypted clear text be?: ').strip()
|
target = input('What should the decrypted clear text be?: ').strip()
|
||||||
plain = RuneText(target)
|
plain = LP.RuneText(target)
|
||||||
print('plaintext:', plain.description())
|
print('plaintext:', plain.description())
|
||||||
if len(enc) != len(plain):
|
if len(enc) != len(plain):
|
||||||
print('Error: key length mismatch')
|
print('Error: key length mismatch')
|
||||||
@@ -126,7 +122,7 @@ def command_f(cmd, args): # (f)ind word
|
|||||||
search_term = None
|
search_term = None
|
||||||
s_len = int(args.strip()) # search words with n-length
|
s_len = int(args.strip()) # search words with n-length
|
||||||
except ValueError:
|
except ValueError:
|
||||||
search_term = RuneText(args)
|
search_term = LP.RuneText(args)
|
||||||
s_len = len(search_term)
|
s_len = len(search_term)
|
||||||
|
|
||||||
cur_words = SOLVER.highlight_words_with_len(s_len)
|
cur_words = SOLVER.highlight_words_with_len(s_len)
|
||||||
@@ -165,14 +161,14 @@ def command_g(cmd, args): # (g)ematria primus
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def pp(i):
|
def pp(i):
|
||||||
p, r, t = alphabet[28 - i if rev else i]
|
p, r, t = LP.alphabet[28 - i if rev else i]
|
||||||
return '{:2d} {} {:3d} {}'.format(i, r, p, '/'.join(t))
|
return '{:2d} {} {:3d} {}'.format(i, r, p, '/'.join(t))
|
||||||
|
|
||||||
rev = cmd[-1] in 'ir' or len(args) > 0 and args[0] in 'ir'
|
rev = cmd[-1] in 'ir' or len(args) > 0 and args[0] in 'ir'
|
||||||
print('Gematria Primus (reversed)' if rev else 'Gematria Primus')
|
print('Gematria Primus (reversed)' if rev else 'Gematria Primus')
|
||||||
half = (len(alphabet) >> 1) + (len(alphabet) & 1) # ceil
|
half = (len(LP.alphabet) >> 1) + (len(LP.alphabet) & 1) # ceil
|
||||||
for i in range(half):
|
for i in range(half):
|
||||||
if i < len(alphabet) // 2:
|
if i < len(LP.alphabet) // 2:
|
||||||
print('{:22} {}'.format(pp(i), pp(i + half)))
|
print('{:22} {}'.format(pp(i), pp(i + half)))
|
||||||
else:
|
else:
|
||||||
print(pp(i))
|
print(pp(i))
|
||||||
@@ -206,8 +202,8 @@ def command_h(cmd, args): # (h)ighlight
|
|||||||
|
|
||||||
def command_k(cmd, args): # (k)ey manipulation
|
def command_k(cmd, args): # (k)ey manipulation
|
||||||
if cmd == 'k' or cmd == 'key':
|
if cmd == 'k' or cmd == 'key':
|
||||||
SOLVER.KEY_DATA = [x.index for x in RuneText(args)]
|
SOLVER.KEY_DATA = LP.RuneText(args).index
|
||||||
print('set key: {}'.format(SOLVER.KEY_DATA))
|
print(f'set key: {SOLVER.KEY_DATA}')
|
||||||
elif cmd[1] == 's':
|
elif cmd[1] == 's':
|
||||||
SOLVER.KEY_SHIFT = get_cmd_int(cmd, args, 'shift')
|
SOLVER.KEY_SHIFT = get_cmd_int(cmd, args, 'shift')
|
||||||
elif cmd[1] == 'r':
|
elif cmd[1] == 'r':
|
||||||
@@ -218,12 +214,12 @@ def command_k(cmd, args): # (k)ey manipulation
|
|||||||
SOLVER.KEY_POST_PAD = get_cmd_int(cmd, args, 'post padding')
|
SOLVER.KEY_POST_PAD = get_cmd_int(cmd, args, 'post padding')
|
||||||
elif cmd[1] == 'i':
|
elif cmd[1] == 'i':
|
||||||
SOLVER.KEY_INVERT = not SOLVER.KEY_INVERT
|
SOLVER.KEY_INVERT = not SOLVER.KEY_INVERT
|
||||||
print('set key invert: {}'.format(SOLVER.KEY_INVERT))
|
print(f'set key invert: {SOLVER.KEY_INVERT}')
|
||||||
elif cmd == 'kj':
|
elif cmd == 'kj':
|
||||||
args = args.strip('[]')
|
args = args.strip('[]')
|
||||||
pos = [int(x) for x in args.split(',')] if args else []
|
pos = [int(x) for x in args.split(',')] if args else []
|
||||||
SOLVER.INTERRUPT_POS = pos
|
SOLVER.INTERRUPT_POS = pos
|
||||||
print('set interrupt jumps: {}'.format(pos))
|
print(f'set interrupt jumps: {SOLVER.INTERRUPT_POS}')
|
||||||
else:
|
else:
|
||||||
return False # command not found
|
return False # command not found
|
||||||
SOLVER.run()
|
SOLVER.run()
|
||||||
@@ -254,8 +250,8 @@ def command_p(cmd, args): # (p)rime test
|
|||||||
if args and cmd not in 'prime':
|
if args and cmd not in 'prime':
|
||||||
return False
|
return False
|
||||||
p = get_cmd_int(cmd, args, start=1)
|
p = get_cmd_int(cmd, args, start=1)
|
||||||
print(p, ':', LIB.is_prime(p))
|
print(p, ':', LP.utils.is_prime(p))
|
||||||
print(LIB.rev(p), ':', LIB.is_prime(LIB.rev(p)))
|
print(LP.utils.rev(p), ':', LP.utils.is_emirp(p))
|
||||||
|
|
||||||
|
|
||||||
#########################################
|
#########################################
|
||||||
@@ -265,17 +261,14 @@ def command_p(cmd, args): # (p)rime test
|
|||||||
def command_t(cmd, args): # (t)ranslate
|
def command_t(cmd, args): # (t)ranslate
|
||||||
if cmd != 't':
|
if cmd != 't':
|
||||||
return False
|
return False
|
||||||
word = RuneText(args)
|
word = LP.RuneText(args)
|
||||||
x = word.as_dict()
|
sffx = ''.join(['*' if LP.utils.is_prime(word.prime_sum) else '',
|
||||||
psum = sum(x['p'])
|
'√' if LP.utils.is_emirp(word.prime_sum) else ''])
|
||||||
sffx = '*' if LIB.is_prime(psum) else ''
|
print('runes({}): {}'.format(len(word), word.rune))
|
||||||
if LIB.is_prime(LIB.rev(psum)):
|
print('plain({}): {}'.format(len(word.text), word.text))
|
||||||
sffx += '√'
|
print('reversed: {}'.format((~word).rune))
|
||||||
print('runes({}): {}'.format(len(x['r']), x['r']))
|
print('indices: {}'.format(word.index))
|
||||||
print('plain({}): {}'.format(len(x['t']), x['t']))
|
print('prime({}{}): {}'.format(word.prime_sum, sffx, word.prime))
|
||||||
print('reversed: {}'.format(''.join([x.rune for x in ~word])))
|
|
||||||
print('indices: {}'.format(x['i']))
|
|
||||||
print('prime({}{}): {}'.format(psum, sffx, x['p']))
|
|
||||||
|
|
||||||
|
|
||||||
#########################################
|
#########################################
|
||||||
@@ -286,7 +279,7 @@ def command_x(cmd, args): # e(x)ecute decryption
|
|||||||
if cmd == 'x':
|
if cmd == 'x':
|
||||||
pass # just run the solver
|
pass # just run the solver
|
||||||
elif cmd == 'xf': # reload from file
|
elif cmd == 'xf': # reload from file
|
||||||
file = f'pages/{args}.txt' if args else '_input.txt'
|
file = LP.path.page(args) if args else LP.path.root('_input.txt')
|
||||||
print('loading file:', file)
|
print('loading file:', file)
|
||||||
SOLVER.input.load(file=file)
|
SOLVER.input.load(file=file)
|
||||||
args = None # so run() won't override data
|
args = None # so run() won't override data
|
||||||
|
|||||||
@@ -1,18 +1,11 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# -*- coding: UTF-8 -*-
|
# -*- coding: UTF-8 -*-
|
||||||
from RuneSolver import VigenereSolver, AffineSolver
|
import LP
|
||||||
from HeuristicSearch import GuessVigenere, GuessAffine, GuessPattern
|
|
||||||
from HeuristicSearch import SearchInterrupt
|
|
||||||
from HeuristicLib import load_indices, Probability
|
|
||||||
from InterruptDB import InterruptDB
|
|
||||||
from RuneText import RuneText
|
|
||||||
# from FailedAttempts import NGramShifter
|
|
||||||
|
|
||||||
RUNES = 'ᚠᚢᚦᚩᚱᚳᚷᚹᚻᚾᛁᛄᛇᛈᛉᛋᛏᛒᛖᛗᛚᛝᛟᛞᚪᚫᚣᛡᛠ'
|
|
||||||
INVERT = False
|
INVERT = False
|
||||||
KEY_MAX_SCORE = 0.05
|
KEY_MAX_SCORE = 0.05
|
||||||
AFF_MAX_SCORE = 0.04
|
AFF_MAX_SCORE = 0.04
|
||||||
IRP_F_ONLY = False
|
IRP_F_ONLY = True
|
||||||
session_files = []
|
session_files = []
|
||||||
|
|
||||||
|
|
||||||
@@ -22,9 +15,9 @@ session_files = []
|
|||||||
|
|
||||||
def break_cipher(fname, candidates, solver, key_fn):
|
def break_cipher(fname, candidates, solver, key_fn):
|
||||||
def fn_similarity(x):
|
def fn_similarity(x):
|
||||||
return Probability(x).similarity()
|
return LP.Probability(x).similarity()
|
||||||
|
|
||||||
filename = f'pages/{fname}.txt'
|
filename = LP.path.page(fname)
|
||||||
slvr = solver()
|
slvr = solver()
|
||||||
slvr.input.load(file=filename)
|
slvr.input.load(file=filename)
|
||||||
slvr.output.QUIET = True
|
slvr.output.QUIET = True
|
||||||
@@ -36,12 +29,12 @@ def break_cipher(fname, candidates, solver, key_fn):
|
|||||||
for irp_count, score, irp, kl, skips in candidates:
|
for irp_count, score, irp, kl, skips in candidates:
|
||||||
if IRP_F_ONLY and irp != 0:
|
if IRP_F_ONLY and irp != 0:
|
||||||
continue
|
continue
|
||||||
data = load_indices(filename, irp, maxinterrupt=irp_count)
|
data = LP.load_indices(filename, irp, maxinterrupt=irp_count)
|
||||||
if INVERT:
|
if INVERT:
|
||||||
data = [28 - x for x in data]
|
data = [28 - x for x in data]
|
||||||
iguess = SearchInterrupt(data, (28 - irp) if INVERT else irp)
|
iguess = LP.SearchInterrupt(data, (28 - irp) if INVERT else irp)
|
||||||
print('score: {}, interrupt: {}, count: {}, solver: {}'.format(
|
print('IoC: {}, interrupt: {}, count: {}, solver: {}'.format(
|
||||||
score, RUNES[irp], len(iguess.stops), key_fn.__name__))
|
score, LP.RUNES[irp], len(iguess.stops), key_fn.__name__))
|
||||||
testcase = iguess.join(iguess.from_occurrence_index(skips))
|
testcase = iguess.join(iguess.from_occurrence_index(skips))
|
||||||
|
|
||||||
key_score, key = key_fn(testcase).guess(kl, fn_similarity)
|
key_score, key = key_fn(testcase).guess(kl, fn_similarity)
|
||||||
@@ -56,25 +49,25 @@ def break_cipher(fname, candidates, solver, key_fn):
|
|||||||
while txtname in session_files:
|
while txtname in session_files:
|
||||||
txtname += '.'
|
txtname += '.'
|
||||||
session_files.append(txtname)
|
session_files.append(txtname)
|
||||||
outfile = f'out/{txtname}.txt'
|
outfile = LP.path.tmp(txtname)
|
||||||
with open(outfile, 'w') as f:
|
with open(outfile, 'w') as f:
|
||||||
f.write(
|
f.write(
|
||||||
f'{irp}, {kl}, {score:.4f}, {key_score:.4f}, {key}, {skips}\n')
|
f'{irp}, {kl}, {score:.4f}, {key_score:.4f}, {key}, {skips}\n')
|
||||||
slvr.output.file_output = outfile
|
slvr.output.file_output = outfile
|
||||||
slvr.INTERRUPT = RUNES[irp]
|
slvr.INTERRUPT = LP.RUNES[irp]
|
||||||
slvr.INTERRUPT_POS = skips
|
slvr.INTERRUPT_POS = skips
|
||||||
slvr.KEY_DATA = key
|
slvr.KEY_DATA = key
|
||||||
slvr.run()
|
slvr.run()
|
||||||
|
|
||||||
|
|
||||||
def pattern_solver(fname, irp=0):
|
def pattern_solver(fname, irp=0):
|
||||||
with open(f'pages/{fname}.txt', 'r') as f:
|
with open(LP.path.page(fname), 'r') as f:
|
||||||
orig = RuneText(f.read())
|
orig = LP.RuneText(f.read())
|
||||||
# orig = RuneText('ᛄᚹᚻᛗᛋᚪ-ᛋᛁᚫᛇ-ᛋᛠᚾᛞ-ᛇᛞ-ᛞᚾᚣᚹᛗ.ᛞᛈ-ᛝᛚᚳᚾᛗᚾᚣ-ᛖᛝᛖᚦᚣᚢ-ᚱᚻᛁᚠ-ᛟᛝ-ᛚᛖᚫᛋᛚᚳᛋᛇ.ᚣᚾᚻᛄᚾᚳᛡ-ᚷᚳᛝ-ᛈᛝ-ᛡᚷᚦᚷᛖ.ᚻᛠᚣ-ᛄᛞᚹᛒ-ᛇᛄᛝᚩᛟ.ᛗᛠᚣᛋᛖᛚᚠ-ᚾᚫᛁ-ᛄᚹᚻᚻᛚᛈᚹᚠ-ᚫᚩᛡᚫᛟ-ᚷᛠ-ᚪᛡᚠᛄᚱᛏᚢᛈ.ᛏᛈ-ᛇᛞ-ᛟᛗᛇᛒᛄᚳᛈ.ᛉᛟ-ᛒᚻᚱᛄᚣ-ᚾᚱ-ᚾᛡᛈᛈ-ᛚᛉᛗᛞ-ᛟᛝ-ᚷᛁᚱᚩᚹᛗ-ᚠᛇᚣ-ᚣᛝᛒ-ᛁ-ᚠᚾᚹᚢ-ᛠᚾᛈᚠᚻ.ᚫᛋᛄᚪᚻ-ᛒᛖᛋᚻᛠ-ᛄᛗ-ᛟᛡᚹᚪᛡ-ᛄᛋᛖᚢᛗ-ᛏᛖᛉᚪ-ᛞᛟᛉᚾᚠ-ᚱᛡᛒᛚᚩᛈᛝ-ᛋᛄᛚᛗ-ᛞᚱᛗᛗ-ᛒᛈ-ᛁᛉᚱᛄᛝ.ᛋᛇᚪ-ᛗᚠᚻᚣᚹᛉᛞ-ᛡᛁᚷᚪ-ᚩᚱ-ᚪᚾᚹᛇᛋᛞᛄᚷ-ᛡ-ᛖᚫᛄ-ᛞᛟᛁᚻᚹᛝ-ᛠᛈᛏ-ᚪᛗᛗᛚᛚᚪᛞ.ᛁᛠᛈᚷᛞ-ᛗᚣᛄᚳᚹᛚ-ᚻᛋᛟᛗ-ᚣᚫᛝᛚ-ᛠᛁᛝᛝᚪ-ᚳᛗ-ᚢᚫᛋ-ᛉᛠᚱ-ᛇᛡᛄᚻᛗᚾ-ᚻᛗᛝᛚ-ᛇᛞ-ᛟᚢᚣᚪᚷᚱ-ᛡᚷ-ᚷᛠ-ᛚᚻᛒ.ᛡᛒ-ᚩᛁᛄ-ᛗᛟᛉᚩᚣ-ᛞᚩ-ᚳᛗ-ᚾᛗᚩ-ᚷᛠ-ᛚᚱᚠᚷ-ᛁᚫᛗᛉ-ᛁᛠᚹᛚ-ᛖᛝᚾᛟᛗᚾ-ᛄᚾ-ᚾᚳᛚᛝ-ᛡ-ᚷᛞᛗᚱᚻᚩ-ᛗᛞᛠᚫᛞ-ᛞᚱᛗᛗ-ᚣᚪ-ᛗᛉᚢᛞᛇᚹ-ᛟᚱᛏᚱᛟᚢᛉᛗᛚᛈᛉᛝ.ᛏᛖ-ᛗᛋᚣ-ᚹᛁ-ᚹᛝ-ᛋᛇᛄᚳᛁᛋᛝ.ᛄᛚᚹ-ᚷᚠᛝ-ᚫᚷᛚᛡᛁᛡ.ᛖᚠᚣ-ᛉᛝᚻᛄᚾᛈᚠ-ᛉᚣ-ᛚᛄᛞᛝᛞᚪ-ᚩᛈ-ᚻᛟ-ᛖᚻᚱᚹ-ᛚᚷᚳ-ᛒᛈᛏᚻ-ᚠᛋᛠᚣᛋᚠ-ᛏᚷᛈᚪᛒ.')
|
# orig = LP.RuneText('ᛄᚹᚻᛗᛋᚪ-ᛋᛁᚫᛇ-ᛋᛠᚾᛞ-ᛇᛞ-ᛞᚾᚣᚹᛗ.ᛞᛈ-ᛝᛚᚳᚾᛗᚾᚣ-ᛖᛝᛖᚦᚣᚢ-ᚱᚻᛁᚠ-ᛟᛝ-ᛚᛖᚫᛋᛚᚳᛋᛇ.ᚣᚾᚻᛄᚾᚳᛡ-ᚷᚳᛝ-ᛈᛝ-ᛡᚷᚦᚷᛖ.ᚻᛠᚣ-ᛄᛞᚹᛒ-ᛇᛄᛝᚩᛟ.ᛗᛠᚣᛋᛖᛚᚠ-ᚾᚫᛁ-ᛄᚹᚻᚻᛚᛈᚹᚠ-ᚫᚩᛡᚫᛟ-ᚷᛠ-ᚪᛡᚠᛄᚱᛏᚢᛈ.ᛏᛈ-ᛇᛞ-ᛟᛗᛇᛒᛄᚳᛈ.ᛉᛟ-ᛒᚻᚱᛄᚣ-ᚾᚱ-ᚾᛡᛈᛈ-ᛚᛉᛗᛞ-ᛟᛝ-ᚷᛁᚱᚩᚹᛗ-ᚠᛇᚣ-ᚣᛝᛒ-ᛁ-ᚠᚾᚹᚢ-ᛠᚾᛈᚠᚻ.ᚫᛋᛄᚪᚻ-ᛒᛖᛋᚻᛠ-ᛄᛗ-ᛟᛡᚹᚪᛡ-ᛄᛋᛖᚢᛗ-ᛏᛖᛉᚪ-ᛞᛟᛉᚾᚠ-ᚱᛡᛒᛚᚩᛈᛝ-ᛋᛄᛚᛗ-ᛞᚱᛗᛗ-ᛒᛈ-ᛁᛉᚱᛄᛝ.ᛋᛇᚪ-ᛗᚠᚻᚣᚹᛉᛞ-ᛡᛁᚷᚪ-ᚩᚱ-ᚪᚾᚹᛇᛋᛞᛄᚷ-ᛡ-ᛖᚫᛄ-ᛞᛟᛁᚻᚹᛝ-ᛠᛈᛏ-ᚪᛗᛗᛚᛚᚪᛞ.ᛁᛠᛈᚷᛞ-ᛗᚣᛄᚳᚹᛚ-ᚻᛋᛟᛗ-ᚣᚫᛝᛚ-ᛠᛁᛝᛝᚪ-ᚳᛗ-ᚢᚫᛋ-ᛉᛠᚱ-ᛇᛡᛄᚻᛗᚾ-ᚻᛗᛝᛚ-ᛇᛞ-ᛟᚢᚣᚪᚷᚱ-ᛡᚷ-ᚷᛠ-ᛚᚻᛒ.ᛡᛒ-ᚩᛁᛄ-ᛗᛟᛉᚩᚣ-ᛞᚩ-ᚳᛗ-ᚾᛗᚩ-ᚷᛠ-ᛚᚱᚠᚷ-ᛁᚫᛗᛉ-ᛁᛠᚹᛚ-ᛖᛝᚾᛟᛗᚾ-ᛄᚾ-ᚾᚳᛚᛝ-ᛡ-ᚷᛞᛗᚱᚻᚩ-ᛗᛞᛠᚫᛞ-ᛞᚱᛗᛗ-ᚣᚪ-ᛗᛉᚢᛞᛇᚹ-ᛟᚱᛏᚱᛟᚢᛉᛗᛚᛈᛉᛝ.ᛏᛖ-ᛗᛋᚣ-ᚹᛁ-ᚹᛝ-ᛋᛇᛄᚳᛁᛋᛝ.ᛄᛚᚹ-ᚷᚠᛝ-ᚫᚷᛚᛡᛁᛡ.ᛖᚠᚣ-ᛉᛝᚻᛄᚾᛈᚠ-ᛉᚣ-ᛚᛄᛞᛝᛞᚪ-ᚩᛈ-ᚻᛟ-ᛖᚻᚱᚹ-ᛚᚷᚳ-ᛒᛈᛏᚻ-ᚠᛋᛠᚣᛋᚠ-ᛏᚷᛈᚪᛒ.')
|
||||||
# orig = RuneText('ᛇᚦ-ᛒᛏᚣᚳ-ᛇᛚᛉ-ᛄᛚᚦᚪ-ᛋᚱᛉᚦ-ᚦᛄᚻ-ᛉᛗ-ᛏᛞᛋ-ᚣᚾ-ᚣᛟᛇᛈᛟᛚ-ᛈᚹᛚᚪᛗ-ᚪᛉᛁᛇᛝᚢᚱᛉ.ᛞᛄ-ᚻᛠᚪᛚ.ᚠᛚ-ᚩᛋᚾ-ᚫᛞᛋᛁᛞᚹᚾᚪᚪ-ᚱᛟᚻ-ᛚᚠᛚᚳᛟᚱ-ᚣᛏ-ᚹᛏᛝᚣ-ᚳᚩ-ᛄᚷᛟ-ᛖ-ᚫᚻᚦᛠ-ᛒᛠᛁ-ᛁᚩᛡ-ᛗᛉᚠᚷᛁ-ᚣᚣᛋᛇᛗᛠᚹ.ᛇᚪ-ᛇᛉᛡ-ᛄᚾᛇᛁᛇᚫ-ᛋᚱᚹ-ᛝᚣᚦ-ᛠᛁᛄᛚᚢᛄ-ᚻᛇᛚᛟ-ᛒᛠᛒᛚ-ᚩᛈᛈ-ᚢᚻᛚ-ᛡᚾᛚ-ᛒᚦᚱᚠᚦᚫ-ᛞᚳ-ᛄᚳᚷ-ᚹᚫ-ᚱᛉᚣᛖᚱ.ᛒᛝᚹ-ᛟᚳᚫᚹᛈᚢ-ᚱᛋᛒ-ᚷᚦᚳᛏᛏᛠᚹ-ᚱᚣᛞ-ᚣᛠᛄ-ᛋ-qᚪᛚᚾᛖᛄᚪ-ᛇᚻᛖ-ᛏᛠᛈ-ᛝᛉᚾᚳ-ᛋᚾᚹᚦᚾ-ᚣᛞᛝᚣ-ᛠᛠᛡ-ᛉᛁᛚᚢᚩ.ᛗᛉᚦ-ᛒᛝᛇᛠᛟ-ᛁᛟᛏ-ᛠᛏᛄ-ᚫᚳᛉᛝᛖᚠ-ᛇᚠ.ᛄᛄᛝᛟᛡᛟ-ᛠᛖᚫ-ᚦᛏᛠᛗ-ᛁᛏᚩᛒᛡ-ᛝᛟ-ᛉᚠᛇᚷᛗᛠ-ᚠᛖ-ᚳᛖᛖᚾᛠᛁᚪᛟ-ᛉᚣ-ᚢᛁ.ᛒᛏ.ᛒᛠ-ᛠᛁᚢᛗ-ᛞᛟᛋᛠᚷᚠᛇᚫ-ᛏᚪ-ᛇᚦ-ᛒᚪᛟᚩᛗ.ᛟᚳᛇ-ᛞᛞ-ᛋᚱᛁᛋᚦ-ᛇᛒ-ᚳᛒᛟ-ᚳᛟᚳᚷᛇ.ᛗᛉᚦ-ᛞᚦᛉᛈᛚᛈᛚᛁᚢ-ᚳᛞᛡᛝᚻᚷ-ᛞᚪ-ᚳᛟᚳᛁᛟᛞ-')
|
# orig = LP.RuneText('ᛇᚦ-ᛒᛏᚣᚳ-ᛇᛚᛉ-ᛄᛚᚦᚪ-ᛋᚱᛉᚦ-ᚦᛄᚻ-ᛉᛗ-ᛏᛞᛋ-ᚣᚾ-ᚣᛟᛇᛈᛟᛚ-ᛈᚹᛚᚪᛗ-ᚪᛉᛁᛇᛝᚢᚱᛉ.ᛞᛄ-ᚻᛠᚪᛚ.ᚠᛚ-ᚩᛋᚾ-ᚫᛞᛋᛁᛞᚹᚾᚪᚪ-ᚱᛟᚻ-ᛚᚠᛚᚳᛟᚱ-ᚣᛏ-ᚹᛏᛝᚣ-ᚳᚩ-ᛄᚷᛟ-ᛖ-ᚫᚻᚦᛠ-ᛒᛠᛁ-ᛁᚩᛡ-ᛗᛉᚠᚷᛁ-ᚣᚣᛋᛇᛗᛠᚹ.ᛇᚪ-ᛇᛉᛡ-ᛄᚾᛇᛁᛇᚫ-ᛋᚱᚹ-ᛝᚣᚦ-ᛠᛁᛄᛚᚢᛄ-ᚻᛇᛚᛟ-ᛒᛠᛒᛚ-ᚩᛈᛈ-ᚢᚻᛚ-ᛡᚾᛚ-ᛒᚦᚱᚠᚦᚫ-ᛞᚳ-ᛄᚳᚷ-ᚹᚫ-ᚱᛉᚣᛖᚱ.ᛒᛝᚹ-ᛟᚳᚫᚹᛈᚢ-ᚱᛋᛒ-ᚷᚦᚳᛏᛏᛠᚹ-ᚱᚣᛞ-ᚣᛠᛄ-ᛋ-qᚪᛚᚾᛖᛄᚪ-ᛇᚻᛖ-ᛏᛠᛈ-ᛝᛉᚾᚳ-ᛋᚾᚹᚦᚾ-ᚣᛞᛝᚣ-ᛠᛠᛡ-ᛉᛁᛚᚢᚩ.ᛗᛉᚦ-ᛒᛝᛇᛠᛟ-ᛁᛟᛏ-ᛠᛏᛄ-ᚫᚳᛉᛝᛖᚠ-ᛇᚠ.ᛄᛄᛝᛟᛡᛟ-ᛠᛖᚫ-ᚦᛏᛠᛗ-ᛁᛏᚩᛒᛡ-ᛝᛟ-ᛉᚠᛇᚷᛗᛠ-ᚠᛖ-ᚳᛖᛖᚾᛠᛁᚪᛟ-ᛉᚣ-ᚢᛁ.ᛒᛏ.ᛒᛠ-ᛠᛁᚢᛗ-ᛞᛟᛋᛠᚷᚠᛇᚫ-ᛏᚪ-ᛇᚦ-ᛒᚪᛟᚩᛗ.ᛟᚳᛇ-ᛞᛞ-ᛋᚱᛁᛋᚦ-ᛇᛒ-ᚳᛒᛟ-ᚳᛟᚳᚷᛇ.ᛗᛉᚦ-ᛞᚦᛉᛈᛚᛈᛚᛁᚢ-ᚳᛞᛡᛝᚻᚷ-ᛞᚪ-ᚳᛟᚳᛁᛟᛞ-')
|
||||||
data = orig.index
|
data = orig.index
|
||||||
if False: # longest uninterrupted text
|
if False: # longest uninterrupted text
|
||||||
pos, lg = InterruptDB.longest_no_interrupt(data, interrupt=0, irpmax=0)
|
pos, lg = LP.InterruptDB.longest_no_interrupt(data, interrupt=0, irpmax=0)
|
||||||
data = data[pos:pos + lg]
|
data = data[pos:pos + lg]
|
||||||
else: # from the beginning
|
else: # from the beginning
|
||||||
data = data[:170]
|
data = data[:170]
|
||||||
@@ -83,7 +76,7 @@ def pattern_solver(fname, irp=0):
|
|||||||
data = [x for x in data if x != 29]
|
data = [x for x in data if x != 29]
|
||||||
|
|
||||||
def fn_similarity(x):
|
def fn_similarity(x):
|
||||||
return Probability(x).similarity()
|
return LP.Probability(x).similarity()
|
||||||
|
|
||||||
def fn_pattern_mirror(x, kl):
|
def fn_pattern_mirror(x, kl):
|
||||||
for i in range(10000): # mirrored, 012210012210 or 012101210
|
for i in range(10000): # mirrored, 012210012210 or 012101210
|
||||||
@@ -92,7 +85,7 @@ def pattern_solver(fname, irp=0):
|
|||||||
yield from x[::-1][1:-1]
|
yield from x[::-1][1:-1]
|
||||||
|
|
||||||
print(fname)
|
print(fname)
|
||||||
gr = GuessPattern(data)
|
gr = LP.GuessPattern(data)
|
||||||
for kl in range(3, 19):
|
for kl in range(3, 19):
|
||||||
# for pattern_shift in range(1):
|
# for pattern_shift in range(1):
|
||||||
# fn_pattern = fn_pattern_mirror
|
# fn_pattern = fn_pattern_mirror
|
||||||
@@ -106,29 +99,29 @@ def pattern_solver(fname, irp=0):
|
|||||||
# Find proper pattern
|
# Find proper pattern
|
||||||
res = []
|
res = []
|
||||||
for offset in range(kl): # up to keylen offset
|
for offset in range(kl): # up to keylen offset
|
||||||
mask = GuessPattern.pattern(kl, fn_pattern)
|
mask = LP.GuessPattern.pattern(kl, fn_pattern)
|
||||||
parts = gr.split(kl, mask, offset)
|
parts = gr.split(kl, mask, offset)
|
||||||
score = sum(Probability(x).IC() for x in parts) / kl
|
score = sum(LP.Probability(x).IC() for x in parts) / kl
|
||||||
if score > 1.6 and score < 2.1:
|
if score > 1.6 and score < 2.1:
|
||||||
res.append((score, parts, offset))
|
res.append((score, parts, offset))
|
||||||
|
|
||||||
# Find best matching key for pattern
|
# Find best matching key for pattern
|
||||||
for score, parts, off in res:
|
for score, parts, off in res:
|
||||||
sc, solution = GuessPattern.guess(parts, fn_similarity)
|
sc, solution = LP.GuessPattern.guess(parts, fn_similarity)
|
||||||
if sc < 0.1:
|
if sc < 0.1:
|
||||||
fmt = 'kl: {}, pattern-n: {}, IoC: {:.3f}, dist: {:.4f}, offset: {}, key: {}'
|
fmt = 'kl: {}, pattern-n: {}, IoC: {:.3f}, dist: {:.4f}, offset: {}, key: {}'
|
||||||
print(fmt.format(kl, pattern_shift, score, sc, off,
|
print(fmt.format(kl, pattern_shift, score, sc, off,
|
||||||
RuneText(solution).text))
|
LP.RuneText(solution).text))
|
||||||
solved = gr.zip(fn_pattern(solution, kl), off)
|
solved = gr.zip(fn_pattern(solution, kl), off)
|
||||||
for i in data_i:
|
for i in data_i:
|
||||||
solved.insert(i, 29)
|
solved.insert(i, 29)
|
||||||
print(' ', RuneText(solved).text)
|
print(' ', LP.RuneText(solved).text)
|
||||||
|
|
||||||
|
|
||||||
#########################################
|
#########################################
|
||||||
# main
|
# main
|
||||||
#########################################
|
#########################################
|
||||||
db = InterruptDB.load('db_norm')
|
db = LP.InterruptDB.load('db_norm')
|
||||||
# IOC_MIN_SCORE = 1.4 # for db_high
|
# IOC_MIN_SCORE = 1.4 # for db_high
|
||||||
IOC_MIN_SCORE = 0.55 # for db_norm
|
IOC_MIN_SCORE = 0.55 # for db_norm
|
||||||
|
|
||||||
@@ -166,5 +159,5 @@ for fname in [
|
|||||||
maxscore = max(x[1] for x in db[fname])
|
maxscore = max(x[1] for x in db[fname])
|
||||||
print('No candidates. Highest score is only', maxscore)
|
print('No candidates. Highest score is only', maxscore)
|
||||||
continue
|
continue
|
||||||
break_cipher(fname, candidates, AffineSolver, GuessAffine)
|
break_cipher(fname, candidates, LP.AffineSolver, LP.GuessAffine)
|
||||||
break_cipher(fname, candidates, VigenereSolver, GuessVigenere)
|
break_cipher(fname, candidates, LP.VigenereSolver, LP.GuessVigenere)
|
||||||
|
|||||||
157
solver.py
157
solver.py
@@ -1,22 +1,20 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
from RuneSolver import VigenereSolver, SequenceSolver
|
import LP
|
||||||
from RuneText import Rune, RuneText
|
|
||||||
from lib import elliptic_curve
|
|
||||||
import sys
|
import sys
|
||||||
import itertools
|
import itertools
|
||||||
|
|
||||||
|
|
||||||
def load_sequence_file(fname):
|
def load_sequence_file(fname):
|
||||||
with open(fname, 'r') as f:
|
with open(LP.path.data(fname), 'r') as f:
|
||||||
return [int(x) for x in f.readlines()]
|
return [int(x) for x in f.readlines()]
|
||||||
|
|
||||||
|
|
||||||
PRIMES = load_sequence_file('data/seq_primes_1000.txt')
|
PRIMES = load_sequence_file('seq_primes_1000')
|
||||||
PRIMES_3301 = load_sequence_file('data/seq_primes_3301.txt')
|
PRIMES_3301 = load_sequence_file('seq_primes_3301')
|
||||||
NOT_PRIMES = load_sequence_file('data/seq_not_primes.txt')
|
NOT_PRIMES = load_sequence_file('seq_not_primes')
|
||||||
FIBONACCI = load_sequence_file('data/seq_fibonacci.txt')
|
FIBONACCI = load_sequence_file('seq_fibonacci')
|
||||||
LUCAS = load_sequence_file('data/seq_lucas_numbers.txt')
|
LUCAS = load_sequence_file('seq_lucas_numbers')
|
||||||
MOEBIUS = load_sequence_file('data/seq_moebius.txt')
|
MOEBIUS = load_sequence_file('seq_moebius')
|
||||||
|
|
||||||
|
|
||||||
def print_all_solved():
|
def print_all_solved():
|
||||||
@@ -46,13 +44,13 @@ def print_all_solved():
|
|||||||
slvr.INTERRUPT = 'ᚠ'
|
slvr.INTERRUPT = 'ᚠ'
|
||||||
slvr.INTERRUPT_POS = [4]
|
slvr.INTERRUPT_POS = [4]
|
||||||
|
|
||||||
def solve(path, fn_solution, solver=VigenereSolver):
|
def solve(fname, fn_solution, solver=LP.VigenereSolver):
|
||||||
slvr = solver()
|
slvr = solver()
|
||||||
slvr.output.COLORS = False
|
slvr.output.COLORS = False
|
||||||
slvr.output.QUIET = True # or use -v/-q while calling
|
slvr.output.QUIET = True # or use -v/-q while calling
|
||||||
slvr.input.load(file=f'pages/{path}.txt')
|
slvr.input.load(file=LP.path.page(fname))
|
||||||
fn_solution(slvr)
|
fn_solution(slvr)
|
||||||
print(f'pages/{path}.txt')
|
print(f'pages/{fname}.txt')
|
||||||
print()
|
print()
|
||||||
slvr.run()
|
slvr.run()
|
||||||
print()
|
print()
|
||||||
@@ -64,78 +62,62 @@ def print_all_solved():
|
|||||||
solve('0_loss_of_divinity', plain)
|
solve('0_loss_of_divinity', plain)
|
||||||
solve('jpg107-167', solution_jpg107_167)
|
solve('jpg107-167', solution_jpg107_167)
|
||||||
solve('jpg229', plain)
|
solve('jpg229', plain)
|
||||||
solve('p56_an_end', solution_p56_end, solver=SequenceSolver)
|
solve('p56_an_end', solution_p56_end, solver=LP.SequenceSolver)
|
||||||
solve('p57_parable', plain)
|
solve('p57_parable', plain)
|
||||||
|
|
||||||
|
|
||||||
def play_around():
|
def play_around():
|
||||||
slvr = VigenereSolver()
|
slvr = LP.VigenereSolver()
|
||||||
slvr.output.COLORS = False
|
slvr.output.COLORS = False
|
||||||
slvr.output.QUIET = True
|
slvr.output.QUIET = True
|
||||||
slvr.KEY_DATA = []
|
slvr.KEY_DATA = []
|
||||||
vowels = 'ᚢᚩᛁᛇᛖᛟᚪᚫᛡᛠ'
|
vowels = 'ᚢᚩᛁᛇᛖᛟᚪᚫᛡᛠ'
|
||||||
for uuu in ['0-2', '3-7', '8-14', '15-22', '23-26', '27-32', '33-39', '40-53', '54-55']:
|
for uuu in LP.FILES_UNSOLVED:
|
||||||
slvr.input.load(file=f'pages/p{uuu}.txt')
|
slvr.input.load(file=LP.path.page(uuu))
|
||||||
print(uuu)
|
print(uuu)
|
||||||
print('word count:', sum(len(x) for x in slvr.input.words.values()))
|
print('word count:', sum(len(x) for x in slvr.input.words.values()))
|
||||||
a = [1 if x.rune in vowels else 0 for x in slvr.input.runes_no_whitespace()]
|
a = [1 if x.rune in vowels else 0 for x in slvr.input.runes_no_whitespace()]
|
||||||
b = [a[i:i + 5] for i in range(0, len(a), 5)]
|
b = [a[i:i + 5] for i in range(0, len(a), 5)]
|
||||||
c = [int(''.join(str(y) for y in x), 2) for x in b]
|
c = [int(''.join(str(y) for y in x), 2) for x in b]
|
||||||
# print('-'.join(str(x) for x in c))
|
# print('-'.join(str(x) for x in c))
|
||||||
# print(''.join(Rune(i=x).text for x in c))
|
# print(LP.RuneText(c).text)
|
||||||
# print(''.join('ABCDEFGHIJKLMNOPQRSTUVWXYZ___...'[x] for x in c))
|
# print(''.join('ABCDEFGHIJKLMNOPQRSTUVWXYZ___...'[x] for x in c))
|
||||||
# slvr.run()
|
# slvr.run()
|
||||||
|
|
||||||
|
|
||||||
def try_totient_on_unsolved():
|
def try_totient_on_unsolved():
|
||||||
slvr = SequenceSolver()
|
slvr = LP.SequenceSolver()
|
||||||
slvr.output.QUIET = True
|
slvr.output.QUIET = True
|
||||||
slvr.output.BREAK_MODE = '' # disable line breaks
|
slvr.output.BREAK_MODE = '' # disable line breaks
|
||||||
# slvr.INTERRUPT = 'ᛝ'
|
# slvr.INTERRUPT = 'ᛝ'
|
||||||
# slvr.INTERRUPT_POS = [1]
|
# slvr.INTERRUPT_POS = [1]
|
||||||
# for uuu in ['15-22']:
|
# for uuu in ['15-22']:
|
||||||
for uuu in ['0-2', '3-7', '8-14', '15-22', '23-26', '27-32', '33-39', '40-53', '54-55']:
|
for uuu in LP.FILES_UNSOLVED:
|
||||||
print()
|
print()
|
||||||
print(uuu)
|
print(uuu)
|
||||||
with open(f'pages/p{uuu}.txt', 'r') as f:
|
slvr.input.load(file=LP.path.page(uuu), limit=25)
|
||||||
slvr.input.load(RuneText(f.read()[:15]))
|
# alldata = slvr.input.runes_no_whitespace() + [LP.Rune(i=29)]
|
||||||
# alldata = slvr.input.runes_no_whitespace() + [Rune(i=29)]
|
|
||||||
|
|
||||||
def b60(x):
|
|
||||||
v = x % 60
|
|
||||||
return v if v < 29 else 60 - v
|
|
||||||
|
|
||||||
def ec(r, i):
|
def ec(r, i):
|
||||||
p1, p2 = elliptic_curve(i, 149, 263, 3299)
|
p1, p2 = LP.utils.elliptic_curve(i, 149, 263, 3299)
|
||||||
if p1 is None:
|
if p1 is None:
|
||||||
return r.index
|
return r.index
|
||||||
return r.index + p1 % 29
|
return r.index + p1 % 29
|
||||||
# for p in PRIMES[:500]:
|
|
||||||
# print(p)
|
|
||||||
# for z in range(29):
|
|
||||||
# def fn(i, x):
|
|
||||||
# return (x + alldata[i - 1].index + z) % 29
|
|
||||||
# if fn(0, alldata[0].index) not in [10, 24]:
|
|
||||||
# continue
|
|
||||||
# slvr.FN = lambda i, r: Rune(i=fn(i, r.index))
|
|
||||||
# slvr.run()
|
|
||||||
for z in range(29):
|
for z in range(29):
|
||||||
# slvr.FN = lambda i, r: r - z # ((i + z) // 3)
|
|
||||||
# slvr.FN = lambda i, r: r - PRIMES[i] + z
|
# slvr.FN = lambda i, r: r - PRIMES[i] + z
|
||||||
# slvr.FN = lambda i, r: Rune(i=b60(r.prime) + z % 29)
|
# slvr.FN = lambda i, r: LP.Rune(i=((r.prime + alldata[i + 1].prime) + z) % 60 // 2)
|
||||||
# slvr.FN = lambda i, r: Rune(i=((r.prime + alldata[i + 1].prime) + z) % 60 // 2)
|
# slvr.FN = lambda i, r: LP.Rune(i=(r.prime - PRIMES[FIBONACCI[i]] + z) % 29)
|
||||||
# slvr.FN = lambda i, r: Rune(i=(3301 * r.index + z) % 29)
|
# slvr.FN = lambda i, r: LP.Rune(i=(r.prime ** i + z) % 29)
|
||||||
slvr.FN = lambda i, r: Rune(i=(ec(r, i) + z) % 29)
|
slvr.FN = lambda i, r: LP.Rune(i=(ec(r, i) + z) % 29)
|
||||||
# slvr.FN = lambda i, r: Rune(i=(r.prime - PRIMES[FIBONACCI[i]] + z) % 29)
|
|
||||||
# slvr.FN = lambda i, r: Rune(i=(r.prime ** i + z) % 29)
|
|
||||||
slvr.run()
|
slvr.run()
|
||||||
|
|
||||||
|
|
||||||
def find_oeis(irp=0, invert=False, offset=0):
|
def find_oeis(irp=0, invert=False, offset=0, allow_fails=1, min_match=2):
|
||||||
def trim_orig_oeis(minlen=15, trim=40):
|
def trim_orig_oeis(minlen=15, trim=40):
|
||||||
# download and unzip: https://oeis.org/stripped.gz
|
# download and unzip: https://oeis.org/stripped.gz
|
||||||
with open('data/oeis_orig.txt', 'r') as f_in:
|
with open(LP.path.data('oeis_orig'), 'r') as f_in:
|
||||||
with open('data/oeis.txt', 'w') as f_out:
|
with open(LP.path.data('oeis'), 'w') as f_out:
|
||||||
for line in f_in.readlines():
|
for line in f_in.readlines():
|
||||||
if line[0] == '#':
|
if line[0] == '#':
|
||||||
continue
|
continue
|
||||||
@@ -146,64 +128,79 @@ def find_oeis(irp=0, invert=False, offset=0):
|
|||||||
f_out.write(name + ',' + ','.join(vals) + '\n')
|
f_out.write(name + ',' + ','.join(vals) + '\n')
|
||||||
|
|
||||||
# trim_orig_oeis() # create db if not present already
|
# trim_orig_oeis() # create db if not present already
|
||||||
with open('data/oeis.txt', 'r') as f:
|
with open(LP.path.data('oeis'), 'r') as f:
|
||||||
seqs = []
|
seqs = []
|
||||||
for line in f.readlines():
|
for line in f.readlines():
|
||||||
name, *vals = line.split(',')
|
vals = line.split(',')
|
||||||
vals = [int(x) for x in vals]
|
seqs.append((vals[0], list(map(int, vals[1:]))))
|
||||||
seqs.append([name] + vals)
|
|
||||||
|
|
||||||
RUNES = 'ᚠᚢᚦᚩᚱᚳᚷᚹᚻᚾᛁᛄᛇᛈᛉᛋᛏᛒᛖᛗᛚᛝᛟᛞᚪᚫᚣᛡᛠ'
|
|
||||||
words = [set()] * 13
|
words = [set()] * 13
|
||||||
words[1] = set(x for x in RUNES)
|
words[1] = set(x for x in LP.RUNES)
|
||||||
for i in range(2, 13): # since 12 is the longest word
|
for i in range(2, 13): # since 12 is the longest word
|
||||||
with open(f'data/dictionary_{i}.txt', 'r') as f:
|
with open(LP.path.data(f'dictionary_{i}'), 'r') as f:
|
||||||
words[i] = set(x.strip() for x in f.readlines())
|
words[i] = set(x.strip() for x in f.readlines())
|
||||||
|
|
||||||
for uuu, wlen in {
|
for uuu, wlen in {
|
||||||
'0-2': [8, 5, 4, 3, 3, 11, 5, 4, 3, 3],
|
'p0-2': [8, 5, 4, 3, 3, 11, 5, 4, 3, 3],
|
||||||
'3-7': [2, 11, 3, 4, 7, 7, 7, 4, 6],
|
'p3-7': [2, 11, 3, 4, 7, 7, 7, 4, 6],
|
||||||
'8-14': [4, 8, 3, 2, 3, 9, 4, 3, 4, 2, 2],
|
'p8-14': [4, 8, 3, 2, 3, 9, 4, 3, 4, 2, 2],
|
||||||
'15-22': [4, 5, 4, 2, 5, 4, 5, 6, 5, 6, 3, 3],
|
'p15-22': [4, 5, 4, 2, 5, 4, 5, 6, 5, 6, 3, 3],
|
||||||
'23-26': [2, 6, 3, 4, 8, 3, 3, 7, 5, 5],
|
'p23-26': [2, 6, 3, 4, 8, 3, 3, 7, 5, 5],
|
||||||
'27-32': [3, 12, 4, 7, 2, 3, 3, 2, 1, 3, 4],
|
'p27-32': [3, 12, 4, 7, 2, 3, 3, 2, 1, 3, 4],
|
||||||
'33-39': [2, 8, 2, 9, 6, 3, 3, 5, 3, 2],
|
'p33-39': [2, 8, 2, 9, 6, 3, 3, 5, 3, 2],
|
||||||
'40-53': [3, 5, 5, 4, 3, 5, 4, 2, 12, 3, 3, 2],
|
'p40-53': [3, 5, 5, 4, 3, 5, 4, 2, 12, 3, 3, 2],
|
||||||
'54-55': [1, 8, 8, 3, 6, 2, 5, 3, 2, 3, 5, 7],
|
'p54-55': [1, 8, 8, 3, 6, 2, 5, 3, 2, 3, 5, 7],
|
||||||
# '56_an_end': [2, 3, 5, 2, 4, 3, 4, 6, 1, 4, 3, 6, 2],
|
# 'p56_an_end': [2, 3, 5, 2, 4, 3, 4, 6, 1, 4, 3, 6, 2],
|
||||||
}.items():
|
}.items():
|
||||||
minwords = sum(wlen[:2]) # must match at least n words
|
|
||||||
splits = [(0, 0, 0)]
|
splits = [(0, 0, 0)]
|
||||||
for x in wlen:
|
for x in wlen:
|
||||||
splits.append((splits[-1][1], splits[-1][1] + x))
|
splits.append((splits[-1][1], splits[-1][1] + x))
|
||||||
splits = splits[1:]
|
splits = splits[1:]
|
||||||
print()
|
print()
|
||||||
print(uuu)
|
print(uuu)
|
||||||
with open(f'pages/p{uuu}.txt', 'r') as f:
|
with open(LP.path.page(uuu), 'r') as f:
|
||||||
data = RuneText(f.read()[:120]).index_no_whitespace
|
data = LP.RuneText(f.read()[:120]).index_rune_only
|
||||||
irps = [i for i, x in enumerate(data[:splits[-1][1]]) if x == irp]
|
irps = [i for i, x in enumerate(data[:splits[-1][1]]) if x == irp]
|
||||||
irps.reverse()
|
irps.reverse() # insert -1 starting with the last
|
||||||
if invert:
|
if invert:
|
||||||
data = [28 - x for x in data]
|
data = [28 - x for x in data]
|
||||||
|
|
||||||
for oeis, *v in seqs: # 390k
|
min_len = sum(wlen[:2]) # must match at least n words
|
||||||
v = v[offset:]
|
data_len = len(data)
|
||||||
if len(v) < minwords:
|
for oeis, vals in seqs: # 390k
|
||||||
|
vals = vals[offset:]
|
||||||
|
if len(vals) < min_len:
|
||||||
continue
|
continue
|
||||||
cases = [x for x in irps if x < len(v)]
|
cases = [x for x in irps if x < len(vals)]
|
||||||
for i in range(len(cases) + 1):
|
for i in range(len(cases) + 1):
|
||||||
for comb in itertools.combinations(cases, i): # 2^3
|
for comb in itertools.combinations(cases, i): # 2^3
|
||||||
res = v[:]
|
res = vals[:]
|
||||||
for z in comb:
|
for z in comb:
|
||||||
res.insert(z, -1) # insert interrupts
|
res.insert(z, -1) # insert interrupts
|
||||||
|
shortest = min(data_len, len(res))
|
||||||
|
|
||||||
for s in range(29):
|
for s in range(29):
|
||||||
who = ''.join(RUNES[x if y == -1 else (x - y - s) % 29]
|
failed = 0
|
||||||
for x, y in zip(data, res))
|
full = []
|
||||||
active = [x for x in splits if x[1] <= len(who)]
|
clen = 0
|
||||||
bag = [who[x:y] for x, y in active]
|
for a, b in splits:
|
||||||
if all(w in words[len(w)] for w in bag):
|
if b > shortest:
|
||||||
|
break
|
||||||
|
nums = [x if y == -1 else (x - y - s) % 29
|
||||||
|
for x, y in zip(data[a:b], res[a:b])]
|
||||||
|
word = ''.join(LP.RUNES[x] for x in nums)
|
||||||
|
if word in words[len(nums)]:
|
||||||
|
clen += len(nums)
|
||||||
|
else:
|
||||||
|
failed += 1
|
||||||
|
if failed > allow_fails:
|
||||||
|
break
|
||||||
|
full.append(LP.RuneText(nums).text)
|
||||||
|
|
||||||
|
if failed > allow_fails or clen < min_match:
|
||||||
|
continue # too many failed
|
||||||
print(oeis.split()[0], 'shift:', s, 'irps:', comb)
|
print(oeis.split()[0], 'shift:', s, 'irps:', comb)
|
||||||
print(' ', ' '.join(RuneText(w).text for w in bag))
|
print(' ', ' '.join(full))
|
||||||
|
|
||||||
|
|
||||||
if '-s' in sys.argv: # print [s]olved
|
if '-s' in sys.argv: # print [s]olved
|
||||||
@@ -213,4 +210,4 @@ else:
|
|||||||
# try_totient_on_unsolved()
|
# try_totient_on_unsolved()
|
||||||
# for i in range(0, 4):
|
# for i in range(0, 4):
|
||||||
# print('offset:', i)
|
# print('offset:', i)
|
||||||
# find_oeis(irp=0, invert=False, offset=i)
|
# find_oeis(irp=0, invert=False, offset=i, allow_fails=1, min_match=10)
|
||||||
|
|||||||
17
tmp/0_welcome_0.8844.GuessVigenere.0_8.txt
Normal file
17
tmp/0_welcome_0.8844.GuessVigenere.0_8.txt
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
0, 8, 0.9018, 0.0193, [23, 10, 1, 10, 9, 10, 16, 26], [4, 5, 6, 7, 9, 12, 14, 18, 20]
|
||||||
|
WELCOME#
|
||||||
|
WELCOME,PILGRIM,TO THE GREAT JOURNEY TOWARD THE END OF ALL THNGS.
|
||||||
|
IT IS NOT AN EASY TRIP,BUT FOR THOSE WHO FIND THEIR WAY HERE IT IS A NECESSARY ONE.
|
||||||
|
ALONG THE WAY YOU WILL FIND AN END TO ALL STRUGGFM UML THNGEAMMOS,TFUR INNOCENCE,YOUR ILLUSIANS,YOUR CERTAINTY,AND YOUR REALITY.
|
||||||
|
ULTIMATELY,YOU WILL DISCOUER AN END TO SELF.
|
||||||
|
IT IS THROUGH THIS PILGRIMAGE THAT WE SHAPE OURSELUES AND OUR REALITIES.
|
||||||
|
JOURNEY DEEP WITHIN AND YOU WILL ARRIUE OUTSIDE.
|
||||||
|
LICE THE INSTAR,IT IS ONLY THROUGH GONG WITHIN THAT WE MAY EMERGE#
|
||||||
|
WIDSOM#
|
||||||
|
YOU ARE A BENG UNTO YOURSELF.
|
||||||
|
YOU ARE A LAW UNTO YOURSELF.
|
||||||
|
EACH INTELLIGENCE IS HOLY.
|
||||||
|
GTP SNGM AEXM RMNGMX RC JTFB#
|
||||||
|
#
|
||||||
|
AEH REAEFPNGGSNGEA#
|
||||||
|
HTEAIAEHB TGXP DHH NHDM#
|
||||||
8
tmp/jpg107-167_0.7102.GuessVigenere.0_13.txt
Normal file
8
tmp/jpg107-167_0.7102.GuessVigenere.0_13.txt
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
0, 13, 0.7420, 0.0429, [21, 10, 4, 0, 1, 19, 21, 18, 4, 18, 24, 0, 18], [2, 4]
|
||||||
|
O COAN#
|
||||||
|
DNRNG A CESDON,THE MOSTEE EXNGLAINETH THE I.
|
||||||
|
"TE I ES THE UOECE OF TE CERCUMITSOAEAIA,"MS CUXOE.
|
||||||
|
EGFD INXFL WO EA XIAEACOAI AEX SRMADNGW EOEII JGP NCPS;PT UNUIIAS EOXTIA"NIA FD A UOACE ENSIDE COUR OEEAD".
|
||||||
|
"E DON'T HOUE A SOIPE IN MY TEAD," THBUGTT THE STNDENU,ANTH HE RAIDED HAS HOND TO TYLL THO MADTER.
|
||||||
|
THE IAASTOR SAOPPED IE STSDEBT,AND SOID"THO UOECE THAT MUST FAITH YOU HANE NO SOIPE IN YONR HEAH;IS IE I.
|
||||||
|
"AND IE STSDEBTS WERY ENLAGHAENED#
|
||||||
Reference in New Issue
Block a user