module refactoring + allow word mistakes for OEIS search
This commit is contained in:
@@ -1,18 +1,11 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: UTF-8 -*-
|
||||
from RuneSolver import VigenereSolver, AffineSolver
|
||||
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
|
||||
import LP
|
||||
|
||||
RUNES = 'ᚠᚢᚦᚩᚱᚳᚷᚹᚻᚾᛁᛄᛇᛈᛉᛋᛏᛒᛖᛗᛚᛝᛟᛞᚪᚫᚣᛡᛠ'
|
||||
INVERT = False
|
||||
KEY_MAX_SCORE = 0.05
|
||||
AFF_MAX_SCORE = 0.04
|
||||
IRP_F_ONLY = False
|
||||
IRP_F_ONLY = True
|
||||
session_files = []
|
||||
|
||||
|
||||
@@ -22,9 +15,9 @@ session_files = []
|
||||
|
||||
def break_cipher(fname, candidates, solver, key_fn):
|
||||
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.input.load(file=filename)
|
||||
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:
|
||||
if IRP_F_ONLY and irp != 0:
|
||||
continue
|
||||
data = load_indices(filename, irp, maxinterrupt=irp_count)
|
||||
data = LP.load_indices(filename, irp, maxinterrupt=irp_count)
|
||||
if INVERT:
|
||||
data = [28 - x for x in data]
|
||||
iguess = SearchInterrupt(data, (28 - irp) if INVERT else irp)
|
||||
print('score: {}, interrupt: {}, count: {}, solver: {}'.format(
|
||||
score, RUNES[irp], len(iguess.stops), key_fn.__name__))
|
||||
iguess = LP.SearchInterrupt(data, (28 - irp) if INVERT else irp)
|
||||
print('IoC: {}, interrupt: {}, count: {}, solver: {}'.format(
|
||||
score, LP.RUNES[irp], len(iguess.stops), key_fn.__name__))
|
||||
testcase = iguess.join(iguess.from_occurrence_index(skips))
|
||||
|
||||
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:
|
||||
txtname += '.'
|
||||
session_files.append(txtname)
|
||||
outfile = f'out/{txtname}.txt'
|
||||
outfile = LP.path.tmp(txtname)
|
||||
with open(outfile, 'w') as f:
|
||||
f.write(
|
||||
f'{irp}, {kl}, {score:.4f}, {key_score:.4f}, {key}, {skips}\n')
|
||||
slvr.output.file_output = outfile
|
||||
slvr.INTERRUPT = RUNES[irp]
|
||||
slvr.INTERRUPT = LP.RUNES[irp]
|
||||
slvr.INTERRUPT_POS = skips
|
||||
slvr.KEY_DATA = key
|
||||
slvr.run()
|
||||
|
||||
|
||||
def pattern_solver(fname, irp=0):
|
||||
with open(f'pages/{fname}.txt', 'r') as f:
|
||||
orig = RuneText(f.read())
|
||||
# orig = RuneText('ᛄᚹᚻᛗᛋᚪ-ᛋᛁᚫᛇ-ᛋᛠᚾᛞ-ᛇᛞ-ᛞᚾᚣᚹᛗ.ᛞᛈ-ᛝᛚᚳᚾᛗᚾᚣ-ᛖᛝᛖᚦᚣᚢ-ᚱᚻᛁᚠ-ᛟᛝ-ᛚᛖᚫᛋᛚᚳᛋᛇ.ᚣᚾᚻᛄᚾᚳᛡ-ᚷᚳᛝ-ᛈᛝ-ᛡᚷᚦᚷᛖ.ᚻᛠᚣ-ᛄᛞᚹᛒ-ᛇᛄᛝᚩᛟ.ᛗᛠᚣᛋᛖᛚᚠ-ᚾᚫᛁ-ᛄᚹᚻᚻᛚᛈᚹᚠ-ᚫᚩᛡᚫᛟ-ᚷᛠ-ᚪᛡᚠᛄᚱᛏᚢᛈ.ᛏᛈ-ᛇᛞ-ᛟᛗᛇᛒᛄᚳᛈ.ᛉᛟ-ᛒᚻᚱᛄᚣ-ᚾᚱ-ᚾᛡᛈᛈ-ᛚᛉᛗᛞ-ᛟᛝ-ᚷᛁᚱᚩᚹᛗ-ᚠᛇᚣ-ᚣᛝᛒ-ᛁ-ᚠᚾᚹᚢ-ᛠᚾᛈᚠᚻ.ᚫᛋᛄᚪᚻ-ᛒᛖᛋᚻᛠ-ᛄᛗ-ᛟᛡᚹᚪᛡ-ᛄᛋᛖᚢᛗ-ᛏᛖᛉᚪ-ᛞᛟᛉᚾᚠ-ᚱᛡᛒᛚᚩᛈᛝ-ᛋᛄᛚᛗ-ᛞᚱᛗᛗ-ᛒᛈ-ᛁᛉᚱᛄᛝ.ᛋᛇᚪ-ᛗᚠᚻᚣᚹᛉᛞ-ᛡᛁᚷᚪ-ᚩᚱ-ᚪᚾᚹᛇᛋᛞᛄᚷ-ᛡ-ᛖᚫᛄ-ᛞᛟᛁᚻᚹᛝ-ᛠᛈᛏ-ᚪᛗᛗᛚᛚᚪᛞ.ᛁᛠᛈᚷᛞ-ᛗᚣᛄᚳᚹᛚ-ᚻᛋᛟᛗ-ᚣᚫᛝᛚ-ᛠᛁᛝᛝᚪ-ᚳᛗ-ᚢᚫᛋ-ᛉᛠᚱ-ᛇᛡᛄᚻᛗᚾ-ᚻᛗᛝᛚ-ᛇᛞ-ᛟᚢᚣᚪᚷᚱ-ᛡᚷ-ᚷᛠ-ᛚᚻᛒ.ᛡᛒ-ᚩᛁᛄ-ᛗᛟᛉᚩᚣ-ᛞᚩ-ᚳᛗ-ᚾᛗᚩ-ᚷᛠ-ᛚᚱᚠᚷ-ᛁᚫᛗᛉ-ᛁᛠᚹᛚ-ᛖᛝᚾᛟᛗᚾ-ᛄᚾ-ᚾᚳᛚᛝ-ᛡ-ᚷᛞᛗᚱᚻᚩ-ᛗᛞᛠᚫᛞ-ᛞᚱᛗᛗ-ᚣᚪ-ᛗᛉᚢᛞᛇᚹ-ᛟᚱᛏᚱᛟᚢᛉᛗᛚᛈᛉᛝ.ᛏᛖ-ᛗᛋᚣ-ᚹᛁ-ᚹᛝ-ᛋᛇᛄᚳᛁᛋᛝ.ᛄᛚᚹ-ᚷᚠᛝ-ᚫᚷᛚᛡᛁᛡ.ᛖᚠᚣ-ᛉᛝᚻᛄᚾᛈᚠ-ᛉᚣ-ᛚᛄᛞᛝᛞᚪ-ᚩᛈ-ᚻᛟ-ᛖᚻᚱᚹ-ᛚᚷᚳ-ᛒᛈᛏᚻ-ᚠᛋᛠᚣᛋᚠ-ᛏᚷᛈᚪᛒ.')
|
||||
# orig = RuneText('ᛇᚦ-ᛒᛏᚣᚳ-ᛇᛚᛉ-ᛄᛚᚦᚪ-ᛋᚱᛉᚦ-ᚦᛄᚻ-ᛉᛗ-ᛏᛞᛋ-ᚣᚾ-ᚣᛟᛇᛈᛟᛚ-ᛈᚹᛚᚪᛗ-ᚪᛉᛁᛇᛝᚢᚱᛉ.ᛞᛄ-ᚻᛠᚪᛚ.ᚠᛚ-ᚩᛋᚾ-ᚫᛞᛋᛁᛞᚹᚾᚪᚪ-ᚱᛟᚻ-ᛚᚠᛚᚳᛟᚱ-ᚣᛏ-ᚹᛏᛝᚣ-ᚳᚩ-ᛄᚷᛟ-ᛖ-ᚫᚻᚦᛠ-ᛒᛠᛁ-ᛁᚩᛡ-ᛗᛉᚠᚷᛁ-ᚣᚣᛋᛇᛗᛠᚹ.ᛇᚪ-ᛇᛉᛡ-ᛄᚾᛇᛁᛇᚫ-ᛋᚱᚹ-ᛝᚣᚦ-ᛠᛁᛄᛚᚢᛄ-ᚻᛇᛚᛟ-ᛒᛠᛒᛚ-ᚩᛈᛈ-ᚢᚻᛚ-ᛡᚾᛚ-ᛒᚦᚱᚠᚦᚫ-ᛞᚳ-ᛄᚳᚷ-ᚹᚫ-ᚱᛉᚣᛖᚱ.ᛒᛝᚹ-ᛟᚳᚫᚹᛈᚢ-ᚱᛋᛒ-ᚷᚦᚳᛏᛏᛠᚹ-ᚱᚣᛞ-ᚣᛠᛄ-ᛋ-qᚪᛚᚾᛖᛄᚪ-ᛇᚻᛖ-ᛏᛠᛈ-ᛝᛉᚾᚳ-ᛋᚾᚹᚦᚾ-ᚣᛞᛝᚣ-ᛠᛠᛡ-ᛉᛁᛚᚢᚩ.ᛗᛉᚦ-ᛒᛝᛇᛠᛟ-ᛁᛟᛏ-ᛠᛏᛄ-ᚫᚳᛉᛝᛖᚠ-ᛇᚠ.ᛄᛄᛝᛟᛡᛟ-ᛠᛖᚫ-ᚦᛏᛠᛗ-ᛁᛏᚩᛒᛡ-ᛝᛟ-ᛉᚠᛇᚷᛗᛠ-ᚠᛖ-ᚳᛖᛖᚾᛠᛁᚪᛟ-ᛉᚣ-ᚢᛁ.ᛒᛏ.ᛒᛠ-ᛠᛁᚢᛗ-ᛞᛟᛋᛠᚷᚠᛇᚫ-ᛏᚪ-ᛇᚦ-ᛒᚪᛟᚩᛗ.ᛟᚳᛇ-ᛞᛞ-ᛋᚱᛁᛋᚦ-ᛇᛒ-ᚳᛒᛟ-ᚳᛟᚳᚷᛇ.ᛗᛉᚦ-ᛞᚦᛉᛈᛚᛈᛚᛁᚢ-ᚳᛞᛡᛝᚻᚷ-ᛞᚪ-ᚳᛟᚳᛁᛟᛞ-')
|
||||
with open(LP.path.page(fname), 'r') as f:
|
||||
orig = LP.RuneText(f.read())
|
||||
# orig = LP.RuneText('ᛄᚹᚻᛗᛋᚪ-ᛋᛁᚫᛇ-ᛋᛠᚾᛞ-ᛇᛞ-ᛞᚾᚣᚹᛗ.ᛞᛈ-ᛝᛚᚳᚾᛗᚾᚣ-ᛖᛝᛖᚦᚣᚢ-ᚱᚻᛁᚠ-ᛟᛝ-ᛚᛖᚫᛋᛚᚳᛋᛇ.ᚣᚾᚻᛄᚾᚳᛡ-ᚷᚳᛝ-ᛈᛝ-ᛡᚷᚦᚷᛖ.ᚻᛠᚣ-ᛄᛞᚹᛒ-ᛇᛄᛝᚩᛟ.ᛗᛠᚣᛋᛖᛚᚠ-ᚾᚫᛁ-ᛄᚹᚻᚻᛚᛈᚹᚠ-ᚫᚩᛡᚫᛟ-ᚷᛠ-ᚪᛡᚠᛄᚱᛏᚢᛈ.ᛏᛈ-ᛇᛞ-ᛟᛗᛇᛒᛄᚳᛈ.ᛉᛟ-ᛒᚻᚱᛄᚣ-ᚾᚱ-ᚾᛡᛈᛈ-ᛚᛉᛗᛞ-ᛟᛝ-ᚷᛁᚱᚩᚹᛗ-ᚠᛇᚣ-ᚣᛝᛒ-ᛁ-ᚠᚾᚹᚢ-ᛠᚾᛈᚠᚻ.ᚫᛋᛄᚪᚻ-ᛒᛖᛋᚻᛠ-ᛄᛗ-ᛟᛡᚹᚪᛡ-ᛄᛋᛖᚢᛗ-ᛏᛖᛉᚪ-ᛞᛟᛉᚾᚠ-ᚱᛡᛒᛚᚩᛈᛝ-ᛋᛄᛚᛗ-ᛞᚱᛗᛗ-ᛒᛈ-ᛁᛉᚱᛄᛝ.ᛋᛇᚪ-ᛗᚠᚻᚣᚹᛉᛞ-ᛡᛁᚷᚪ-ᚩᚱ-ᚪᚾᚹᛇᛋᛞᛄᚷ-ᛡ-ᛖᚫᛄ-ᛞᛟᛁᚻᚹᛝ-ᛠᛈᛏ-ᚪᛗᛗᛚᛚᚪᛞ.ᛁᛠᛈᚷᛞ-ᛗᚣᛄᚳᚹᛚ-ᚻᛋᛟᛗ-ᚣᚫᛝᛚ-ᛠᛁᛝᛝᚪ-ᚳᛗ-ᚢᚫᛋ-ᛉᛠᚱ-ᛇᛡᛄᚻᛗᚾ-ᚻᛗᛝᛚ-ᛇᛞ-ᛟᚢᚣᚪᚷᚱ-ᛡᚷ-ᚷᛠ-ᛚᚻᛒ.ᛡᛒ-ᚩᛁᛄ-ᛗᛟᛉᚩᚣ-ᛞᚩ-ᚳᛗ-ᚾᛗᚩ-ᚷᛠ-ᛚᚱᚠᚷ-ᛁᚫᛗᛉ-ᛁᛠᚹᛚ-ᛖᛝᚾᛟᛗᚾ-ᛄᚾ-ᚾᚳᛚᛝ-ᛡ-ᚷᛞᛗᚱᚻᚩ-ᛗᛞᛠᚫᛞ-ᛞᚱᛗᛗ-ᚣᚪ-ᛗᛉᚢᛞᛇᚹ-ᛟᚱᛏᚱᛟᚢᛉᛗᛚᛈᛉᛝ.ᛏᛖ-ᛗᛋᚣ-ᚹᛁ-ᚹᛝ-ᛋᛇᛄᚳᛁᛋᛝ.ᛄᛚᚹ-ᚷᚠᛝ-ᚫᚷᛚᛡᛁᛡ.ᛖᚠᚣ-ᛉᛝᚻᛄᚾᛈᚠ-ᛉᚣ-ᛚᛄᛞᛝᛞᚪ-ᚩᛈ-ᚻᛟ-ᛖᚻᚱᚹ-ᛚᚷᚳ-ᛒᛈᛏᚻ-ᚠᛋᛠᚣᛋᚠ-ᛏᚷᛈᚪᛒ.')
|
||||
# orig = LP.RuneText('ᛇᚦ-ᛒᛏᚣᚳ-ᛇᛚᛉ-ᛄᛚᚦᚪ-ᛋᚱᛉᚦ-ᚦᛄᚻ-ᛉᛗ-ᛏᛞᛋ-ᚣᚾ-ᚣᛟᛇᛈᛟᛚ-ᛈᚹᛚᚪᛗ-ᚪᛉᛁᛇᛝᚢᚱᛉ.ᛞᛄ-ᚻᛠᚪᛚ.ᚠᛚ-ᚩᛋᚾ-ᚫᛞᛋᛁᛞᚹᚾᚪᚪ-ᚱᛟᚻ-ᛚᚠᛚᚳᛟᚱ-ᚣᛏ-ᚹᛏᛝᚣ-ᚳᚩ-ᛄᚷᛟ-ᛖ-ᚫᚻᚦᛠ-ᛒᛠᛁ-ᛁᚩᛡ-ᛗᛉᚠᚷᛁ-ᚣᚣᛋᛇᛗᛠᚹ.ᛇᚪ-ᛇᛉᛡ-ᛄᚾᛇᛁᛇᚫ-ᛋᚱᚹ-ᛝᚣᚦ-ᛠᛁᛄᛚᚢᛄ-ᚻᛇᛚᛟ-ᛒᛠᛒᛚ-ᚩᛈᛈ-ᚢᚻᛚ-ᛡᚾᛚ-ᛒᚦᚱᚠᚦᚫ-ᛞᚳ-ᛄᚳᚷ-ᚹᚫ-ᚱᛉᚣᛖᚱ.ᛒᛝᚹ-ᛟᚳᚫᚹᛈᚢ-ᚱᛋᛒ-ᚷᚦᚳᛏᛏᛠᚹ-ᚱᚣᛞ-ᚣᛠᛄ-ᛋ-qᚪᛚᚾᛖᛄᚪ-ᛇᚻᛖ-ᛏᛠᛈ-ᛝᛉᚾᚳ-ᛋᚾᚹᚦᚾ-ᚣᛞᛝᚣ-ᛠᛠᛡ-ᛉᛁᛚᚢᚩ.ᛗᛉᚦ-ᛒᛝᛇᛠᛟ-ᛁᛟᛏ-ᛠᛏᛄ-ᚫᚳᛉᛝᛖᚠ-ᛇᚠ.ᛄᛄᛝᛟᛡᛟ-ᛠᛖᚫ-ᚦᛏᛠᛗ-ᛁᛏᚩᛒᛡ-ᛝᛟ-ᛉᚠᛇᚷᛗᛠ-ᚠᛖ-ᚳᛖᛖᚾᛠᛁᚪᛟ-ᛉᚣ-ᚢᛁ.ᛒᛏ.ᛒᛠ-ᛠᛁᚢᛗ-ᛞᛟᛋᛠᚷᚠᛇᚫ-ᛏᚪ-ᛇᚦ-ᛒᚪᛟᚩᛗ.ᛟᚳᛇ-ᛞᛞ-ᛋᚱᛁᛋᚦ-ᛇᛒ-ᚳᛒᛟ-ᚳᛟᚳᚷᛇ.ᛗᛉᚦ-ᛞᚦᛉᛈᛚᛈᛚᛁᚢ-ᚳᛞᛡᛝᚻᚷ-ᛞᚪ-ᚳᛟᚳᛁᛟᛞ-')
|
||||
data = orig.index
|
||||
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]
|
||||
else: # from the beginning
|
||||
data = data[:170]
|
||||
@@ -83,7 +76,7 @@ def pattern_solver(fname, irp=0):
|
||||
data = [x for x in data if x != 29]
|
||||
|
||||
def fn_similarity(x):
|
||||
return Probability(x).similarity()
|
||||
return LP.Probability(x).similarity()
|
||||
|
||||
def fn_pattern_mirror(x, kl):
|
||||
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]
|
||||
|
||||
print(fname)
|
||||
gr = GuessPattern(data)
|
||||
gr = LP.GuessPattern(data)
|
||||
for kl in range(3, 19):
|
||||
# for pattern_shift in range(1):
|
||||
# fn_pattern = fn_pattern_mirror
|
||||
@@ -106,29 +99,29 @@ def pattern_solver(fname, irp=0):
|
||||
# Find proper pattern
|
||||
res = []
|
||||
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)
|
||||
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:
|
||||
res.append((score, parts, offset))
|
||||
|
||||
# Find best matching key for pattern
|
||||
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:
|
||||
fmt = 'kl: {}, pattern-n: {}, IoC: {:.3f}, dist: {:.4f}, offset: {}, key: {}'
|
||||
print(fmt.format(kl, pattern_shift, score, sc, off,
|
||||
RuneText(solution).text))
|
||||
LP.RuneText(solution).text))
|
||||
solved = gr.zip(fn_pattern(solution, kl), off)
|
||||
for i in data_i:
|
||||
solved.insert(i, 29)
|
||||
print(' ', RuneText(solved).text)
|
||||
print(' ', LP.RuneText(solved).text)
|
||||
|
||||
|
||||
#########################################
|
||||
# main
|
||||
#########################################
|
||||
db = InterruptDB.load('db_norm')
|
||||
db = LP.InterruptDB.load('db_norm')
|
||||
# IOC_MIN_SCORE = 1.4 # for db_high
|
||||
IOC_MIN_SCORE = 0.55 # for db_norm
|
||||
|
||||
@@ -166,5 +159,5 @@ for fname in [
|
||||
maxscore = max(x[1] for x in db[fname])
|
||||
print('No candidates. Highest score is only', maxscore)
|
||||
continue
|
||||
break_cipher(fname, candidates, AffineSolver, GuessAffine)
|
||||
break_cipher(fname, candidates, VigenereSolver, GuessVigenere)
|
||||
break_cipher(fname, candidates, LP.AffineSolver, LP.GuessAffine)
|
||||
break_cipher(fname, candidates, LP.VigenereSolver, LP.GuessVigenere)
|
||||
|
||||
Reference in New Issue
Block a user