making substitution search easier
This commit is contained in:
@@ -249,7 +249,7 @@ If the output is too long, you can limit (the already loaded data) with `xl 180`
|
|||||||
|
|
||||||
`Rune.kind` can be one of `r n s l w` – meaning (r)une, (n)umber, (s)entence, (l)ine, or (w)hitespace. A line is what you see in the source file (which is equivalent to a line in the original jpg page). A sentence is one that ends with a period (`⁘`).
|
`Rune.kind` can be one of `r n s l w` – meaning (r)une, (n)umber, (s)entence, (l)ine, or (w)hitespace. A line is what you see in the source file (which is equivalent to a line in the original jpg page). A sentence is one that ends with a period (`⁘`).
|
||||||
|
|
||||||
`Rune` as well as `RuneText` both support simple arithmetic operations: `Rune(i=2) - 2` will yield a `ᚠ` rune. For example, you can invert a text with `28 - RuneText('test me')`.
|
`Rune` as well as `RuneText` both support simple arithmetic operations: `Rune(i=2) - 2` will yield a `ᚠ` rune. For example, you can invert a text with `28 - RuneText('test me')` or simply `~RuneText('inverted')`.
|
||||||
|
|
||||||
__Note:__ Always initialize a rune with its rune character or its index, never ASCII or its prime value.
|
__Note:__ Always initialize a rune with its rune character or its index, never ASCII or its prime value.
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,12 @@ class RuneSolver(RuneRunner):
|
|||||||
def highlight_interrupt(self):
|
def highlight_interrupt(self):
|
||||||
return self.highlight_rune(self.INTERRUPT, self.INTERRUPT_POS)
|
return self.highlight_rune(self.INTERRUPT, self.INTERRUPT_POS)
|
||||||
|
|
||||||
|
def substitute_get(self, pos, keylen, search_term, found_term):
|
||||||
|
return found_term.zip_sub(search_term).description(count=True)
|
||||||
|
|
||||||
|
def substitute_supports_keylen(self):
|
||||||
|
return False
|
||||||
|
|
||||||
def run(self, data=None):
|
def run(self, data=None):
|
||||||
if data:
|
if data:
|
||||||
self.input.load(data=data)
|
self.input.load(data=data)
|
||||||
@@ -120,7 +126,9 @@ class RunningKeySolver(RuneSolver):
|
|||||||
if self.KEY_INVERT:
|
if self.KEY_INVERT:
|
||||||
r_idx = 28 - r_idx
|
r_idx = 28 - r_idx
|
||||||
pos = self.active_key_pos()
|
pos = self.active_key_pos()
|
||||||
if pos != -1:
|
if pos == -1:
|
||||||
|
self.copy_unmodified(r_idx)
|
||||||
|
else:
|
||||||
i = (pos + self.KEY_SHIFT) % self.k_len
|
i = (pos + self.KEY_SHIFT) % self.k_len
|
||||||
r_idx = (self.decrypt(r_idx, i) - self.KEY_ROTATE) % 29
|
r_idx = (self.decrypt(r_idx, i) - self.KEY_ROTATE) % 29
|
||||||
# rotate_key
|
# rotate_key
|
||||||
@@ -128,8 +136,11 @@ class RunningKeySolver(RuneSolver):
|
|||||||
self.k_current_pos = (self.k_current_pos + 1) % self.k_full_len
|
self.k_current_pos = (self.k_current_pos + 1) % self.k_full_len
|
||||||
return Rune(i=r_idx)
|
return Rune(i=r_idx)
|
||||||
|
|
||||||
def decrypt(self, rune_index, key_index):
|
def decrypt(self, rune_index, key_index): # must subclass
|
||||||
raise NotImplementedError # must subclass
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def copy_unmodified(self, rune_index): # subclass if needed
|
||||||
|
pass
|
||||||
|
|
||||||
def key__str__(self):
|
def key__str__(self):
|
||||||
return self.KEY_DATA # you should override this
|
return self.KEY_DATA # you should override this
|
||||||
@@ -156,6 +167,15 @@ class VigenereSolver(RunningKeySolver):
|
|||||||
def decrypt(self, rune_index, key_index):
|
def decrypt(self, rune_index, key_index):
|
||||||
return rune_index - self.KEY_DATA[key_index]
|
return rune_index - self.KEY_DATA[key_index]
|
||||||
|
|
||||||
|
def substitute_supports_keylen(self):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def substitute_get(self, pos, keylen, search_term, found_term):
|
||||||
|
ret = [Rune(r='⁚')] * keylen
|
||||||
|
for i, r in enumerate(found_term.zip_sub(search_term)):
|
||||||
|
ret[(pos + i) % keylen] = r
|
||||||
|
return RuneText(ret).description(count=True, index=False)
|
||||||
|
|
||||||
def key__str__(self):
|
def key__str__(self):
|
||||||
return self.key__str__basic_runes()
|
return self.key__str__basic_runes()
|
||||||
|
|
||||||
@@ -175,13 +195,35 @@ class AffineSolver(RunningKeySolver):
|
|||||||
|
|
||||||
class AutokeySolver(RunningKeySolver):
|
class AutokeySolver(RunningKeySolver):
|
||||||
def run(self, data=None):
|
def run(self, data=None):
|
||||||
self.running_key = self.KEY_DATA[:]
|
key = self.KEY_DATA[self.KEY_SHIFT:] + self.KEY_DATA[:self.KEY_SHIFT]
|
||||||
|
key = [29] * self.KEY_OFFSET + key + [29] * self.KEY_POST_PAD
|
||||||
|
self.running_key = key
|
||||||
super().run(data=data)
|
super().run(data=data)
|
||||||
|
|
||||||
def decrypt(self, rune_index, key_index):
|
def decrypt(self, rune_index, _):
|
||||||
rune_index = (rune_index - self.running_key.pop(0)) % 29
|
rune_index = (rune_index - self.running_key.pop(0)) % 29
|
||||||
self.running_key.append(rune_index)
|
self.running_key.append(rune_index)
|
||||||
return rune_index
|
return rune_index
|
||||||
|
|
||||||
|
def copy_unmodified(self, rune_index):
|
||||||
|
if self.k_len > 0:
|
||||||
|
self.running_key.pop(0)
|
||||||
|
self.running_key.append(rune_index)
|
||||||
|
|
||||||
|
def substitute_supports_keylen(self):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def substitute_get(self, pos, keylen, search_term, found_term):
|
||||||
|
data = self.input.runes_no_whitespace()
|
||||||
|
ret = [Rune(r='⁚')] * keylen
|
||||||
|
for o in range(len(search_term)):
|
||||||
|
plain = search_term[o]
|
||||||
|
i = pos + o
|
||||||
|
while i >= 0:
|
||||||
|
plain = data[i] - plain
|
||||||
|
i -= keylen
|
||||||
|
ret[i + keylen] = plain
|
||||||
|
return RuneText(ret).description(count=True, index=False)
|
||||||
|
|
||||||
def key__str__(self):
|
def key__str__(self):
|
||||||
return self.key__str__basic_runes()
|
return self.key__str__basic_runes()
|
||||||
|
|||||||
25
RuneText.py
25
RuneText.py
@@ -101,6 +101,9 @@ class Rune(object):
|
|||||||
def __rsub__(self, o):
|
def __rsub__(self, o):
|
||||||
return self if self.index == 29 else Rune(i=(o - self.index) % 29)
|
return self if self.index == 29 else Rune(i=(o - self.index) % 29)
|
||||||
|
|
||||||
|
def __invert__(self):
|
||||||
|
return self if self.index == 29 else Rune(i=28 - self.index)
|
||||||
|
|
||||||
|
|
||||||
#########################################
|
#########################################
|
||||||
# RuneText : Stores multiple Rune objects. Allows arithmetic operations
|
# RuneText : Stores multiple Rune objects. Allows arithmetic operations
|
||||||
@@ -196,6 +199,11 @@ class RuneText(object):
|
|||||||
d['i'] = [x for x in d['i'] if x != 29]
|
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'])
|
return fmt.format(d['r'], len(d['r']), d['t'], len(d['t']), d['i'])
|
||||||
|
|
||||||
|
def zip_sub(self, other):
|
||||||
|
if len(self) != len(other):
|
||||||
|
raise IndexError('RuneText length mismatch')
|
||||||
|
return RuneText([x - y for x, y in zip(self._data, other._data)])
|
||||||
|
|
||||||
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]
|
||||||
@@ -206,20 +214,10 @@ class RuneText(object):
|
|||||||
self._data[key] = value
|
self._data[key] = value
|
||||||
|
|
||||||
def __add__(self, other):
|
def __add__(self, other):
|
||||||
if isinstance(other, RuneText):
|
return RuneText([x + other for x in self._data])
|
||||||
if len(self) != len(other):
|
|
||||||
raise IndexError('RuneText length mismatch')
|
|
||||||
return RuneText([x + y for x, y in zip(self._data, other._data)])
|
|
||||||
else:
|
|
||||||
return RuneText([x + other for x in self._data])
|
|
||||||
|
|
||||||
def __sub__(self, other):
|
def __sub__(self, other):
|
||||||
if isinstance(other, RuneText):
|
return RuneText([x - other for x in self._data])
|
||||||
if len(self) != len(other):
|
|
||||||
raise IndexError('RuneText length mismatch')
|
|
||||||
return RuneText([x - y for x, y in zip(self._data, other._data)])
|
|
||||||
else:
|
|
||||||
return RuneText([x - other for x in self._data])
|
|
||||||
|
|
||||||
def __radd__(self, other):
|
def __radd__(self, other):
|
||||||
return RuneText([other + x for x in self._data])
|
return RuneText([other + x for x in self._data])
|
||||||
@@ -227,5 +225,8 @@ class RuneText(object):
|
|||||||
def __rsub__(self, other):
|
def __rsub__(self, other):
|
||||||
return RuneText([other - x for x in self._data])
|
return RuneText([other - x for x in self._data])
|
||||||
|
|
||||||
|
def __invert__(self):
|
||||||
|
return RuneText([~x for x in self._data])
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f'RuneText<{len(self._data)}>'
|
return f'RuneText<{len(self._data)}>'
|
||||||
|
|||||||
@@ -85,8 +85,8 @@ def command_a(cmd, args): # [a]ll variations
|
|||||||
root = RuneText(args)
|
root = RuneText(args)
|
||||||
inclIndex = 'q' not in cmd
|
inclIndex = 'q' not in cmd
|
||||||
if 'i' in cmd:
|
if 'i' in cmd:
|
||||||
root = 28 - root
|
root = ~root
|
||||||
for i in range(0, 29):
|
for i in range(29):
|
||||||
print('{:02d}: {}'.format(i, (root + i).description(index=inclIndex)))
|
print('{:02d}: {}'.format(i, (root + i).description(index=inclIndex)))
|
||||||
|
|
||||||
|
|
||||||
@@ -110,7 +110,7 @@ def command_d(cmd, args): # [d]ecrypt or single substitution
|
|||||||
print('Error: key length mismatch')
|
print('Error: key length mismatch')
|
||||||
else:
|
else:
|
||||||
print('Substition:')
|
print('Substition:')
|
||||||
print((enc - plain).description())
|
print((enc.zip_sub(plain)).description())
|
||||||
|
|
||||||
|
|
||||||
#########################################
|
#########################################
|
||||||
@@ -134,13 +134,26 @@ def command_f(cmd, args): # (f)ind word
|
|||||||
print()
|
print()
|
||||||
print('Found:')
|
print('Found:')
|
||||||
for _, _, pos, _, w in cur_words:
|
for _, _, pos, _, w in cur_words:
|
||||||
print('{:04}: {}'.format(pos, w.description(count=True)))
|
print(f'{pos:04}: {w.description(count=True)}')
|
||||||
if search_term:
|
if search_term:
|
||||||
print()
|
print()
|
||||||
|
keylen = [len(search_term)]
|
||||||
|
if SOLVER.substitute_supports_keylen():
|
||||||
|
try:
|
||||||
|
inp = input('What is the key length? (num or [a]ll): ').strip()
|
||||||
|
if inp:
|
||||||
|
if inp[0] == 'a':
|
||||||
|
keylen = range(len(search_term), 24)
|
||||||
|
else:
|
||||||
|
keylen = [int(inp)]
|
||||||
|
except ValueError:
|
||||||
|
raise ValueError('not a number.')
|
||||||
|
print()
|
||||||
print('Available substition:')
|
print('Available substition:')
|
||||||
for _, _, pos, _, w in cur_words:
|
for _, _, pos, _, w in cur_words:
|
||||||
word = search_term - w
|
for kl in keylen:
|
||||||
print('{:04}: {}'.format(pos, word.description(count=True)))
|
res = SOLVER.substitute_get(pos, kl, search_term, w)
|
||||||
|
print(f'{pos:04}: {res}')
|
||||||
|
|
||||||
|
|
||||||
#########################################
|
#########################################
|
||||||
@@ -253,17 +266,16 @@ def command_t(cmd, args): # (t)ranslate
|
|||||||
if cmd != 't':
|
if cmd != 't':
|
||||||
return False
|
return False
|
||||||
word = RuneText(args)
|
word = RuneText(args)
|
||||||
rev_word = 28 - word
|
x = word.as_dict()
|
||||||
word = word.as_dict()
|
psum = sum(x['p'])
|
||||||
psum = sum(word['p'])
|
|
||||||
sffx = '*' if LIB.is_prime(psum) else ''
|
sffx = '*' if LIB.is_prime(psum) else ''
|
||||||
if LIB.is_prime(LIB.rev(psum)):
|
if LIB.is_prime(LIB.rev(psum)):
|
||||||
sffx += '√'
|
sffx += '√'
|
||||||
print('runes({}): {}'.format(len(word['r']), word['r']))
|
print('runes({}): {}'.format(len(x['r']), x['r']))
|
||||||
print('plain({}): {}'.format(len(word['t']), word['t']))
|
print('plain({}): {}'.format(len(x['t']), x['t']))
|
||||||
print('reversed: {}'.format(''.join([x.rune for x in rev_word])))
|
print('reversed: {}'.format(''.join([x.rune for x in ~word])))
|
||||||
print('indices: {}'.format(word['i']))
|
print('indices: {}'.format(x['i']))
|
||||||
print('prime({}{}): {}'.format(psum, sffx, word['p']))
|
print('prime({}{}): {}'.format(psum, sffx, x['p']))
|
||||||
|
|
||||||
|
|
||||||
#########################################
|
#########################################
|
||||||
|
|||||||
Reference in New Issue
Block a user