Extended Straddling Checkerboard
An extended Straddling Checkerboard, is like the regular Straddling checkerboard, but allows word dictionaries and arbitrary functional codes like (FIGURE, where you can specify a number literally).
Related tasks:
References:
- Cipher Machines web page on Checkerboards: Checkerboards
Python
""" rosettacode.org/wiki/Extended_Straddling_Checkerboard """
from functools import reduce
WDICT = {
'CODE': 'κ',
'ACK': 'α',
'REQ': 'ρ',
'MSG': 'μ',
'RV': 'ν',
'GRID': 'γ',
'SEND': 'σ',
'SUPP': 'π',
}
SDICT = {v: k for (k, v) in WDICT.items()} # reversed WDICT for reverse lookup on decode
# web site CT37w, but '/' is 98 not 99 to help differentiate from code for 9 of '999'
CT37w = [['', 'A', 'E', 'I', 'N', 'O', 'T', 'κ', '', '', '',],
['7', 'B', 'C', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M',],
['8', 'P', 'Q', 'R', 'S', 'U', 'V', 'W', 'X', 'Y', 'Z',],
['9', ' ', '.', 'α', 'ρ', 'μ', 'ν', 'γ', 'σ', '/', 'π',],]
def xcb_encode(message, nchangemode='98', code='κ', table=CT37w, wdict=WDICT):
"""
Encode with extended straddling checkerboard. Default checkerboard is
CT37w at https://www.ciphermachinesandcryptology.com/en/table.htm
The numeric mode has the numbers as repeated in triplicate
The CODE mode expects a 3-digit numeric code
"""
encoded = []
numericmode, codemode = False, False
codemodecount = 0
# replace terms found in dictionary with a single char symbol that is in the table
s = reduce(lambda x, p: x.replace(
p[0], p[1]), wdict.items(), message.upper())
for c in s:
if c.isnumeric():
if codemode: # codemode symbols are preceded by the CODE digit '6' then as-is
encoded.append(c)
codemodecount += 1
if codemodecount >= 3:
codemode = False
else: # numeric numbers are triplicate encoded so '3' -> '333'
if not numericmode:
numericmode = True
encoded.append(nchangemode)
encoded.append(c*3)
else:
codemode = False
if numericmode:
encoded.append(nchangemode) # end numericmode with the numeric code for '/' (98)
numericmode = False
if c == code:
codemode = True
codemodecount = 0
for row in table:
if c in row:
k = row.index(c)
encoded.append(str(row[0]) + str(k-1))
break
return ''.join(encoded)
def xcb_decode(s, nchangemode='98', code='κ', table=CT37w, sdict=SDICT):
""" Decode extended straddling checkerboard """
numbers = {c*3: c for c in list('0123456789')}
prefixes = sorted([row[0] for row in table], reverse=True)
pos = 0
numericmode = False
codemode = False
decoded = []
while pos < len(s):
if numericmode:
if s[pos:pos+3] in numbers:
decoded.append(numbers[s[pos:pos+3]])
pos += 2
elif s[pos:pos+2] == nchangemode:
numericmode = False
pos += 1
elif codemode:
if (s[pos:pos+3]).isnumeric():
decoded.append(s[pos:pos+3])
pos += 2
codemode = False
elif s[pos:pos+2] == nchangemode:
numericmode = not numericmode
pos += 1
else:
for p in prefixes:
if s[pos:].startswith(p):
n = len(p)
row = next(i for i, r in enumerate(table) if p == r[0])
c = table[row][int(s[pos+n])+1]
decoded.append(c)
if c == code:
codemode = True
pos += n
break
pos += 1
return reduce(lambda x, p: x.replace(p[0], p[1]), sdict.items(), ''.join(decoded))
if __name__ == '__main__':
MESSAGE = 'Admin ACK your MSG. CODE291 SEND further 2000 SUPP to HQ by 1 March'
print(MESSAGE)
print('Encoded: ', xcb_encode(MESSAGE))
print('Decoded: ', xcb_decode(xcb_encode(MESSAGE)))
- Output:
Admin ACK your MSG. CODE291 SEND further 2000 SUPP to HQ by 1 March Encoded: 072792390929088484829094919062919097907384825751829098222000000000989099905490758190708890981119890790827175 Decoded: ADMIN ACK YOUR MSG. CODE291 SEND FURTHER 2000 SUPP TO HQ BY 1 MARCH