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 '/' (FIG) char 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) # FIG
encoded.append(c*3)
else:
codemode = False
if numericmode:
encoded.append(nchangemode) # end numericmode with the FIG 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
Wren
For consistency with the Julia example, this uses the CT-37w checkerboard with F/L changed to 98 and SUPP to 99.
import "./str" for Str
var row1 = "AEINOT"
var row2 = "BCDFGHJKLM"
var row3 = "PQRSUVWXYZ"
var row4 = " ."
var emap = {}
for (i in 0...row1.count) emap[row1[i]] = i.toString
for (i in 0...row2.count) emap[row2[i]] = (70 + i).toString
for (i in 0...row3.count) emap[row3[i]] = (80 + i).toString
for (i in 0...row4.count) emap[row4[i]] = (90 + i).toString
var ewords = {
"ACK": "92", "REQ": "93", "MSG": "94", "RV": "95",
"GRID": "96", "SEND": "97", "SUPP": "99"
}
var efigs = "0123456789"
var spc = "90"
var dot = "91"
var fsl = "98"
var dmap = {}
var dwords = {}
for (k in emap.keys) dmap[emap[k]] = k
for (k in ewords.keys) dwords[ewords[k]] = k
var drow1 = "012345"
var encode = Fn.new { |s|
s = Str.upper(s)
var res = ""
var words = s.split(" ")
var wc = words.count
for (i in 0...wc) {
var word = words[i]
var add = ""
if (ewords.containsKey(word)) {
add = ewords[word]
} else if (ewords.containsKey(word[0...-1]) && word[-1] == ".") {
add = ewords[word[0...-1]] + dot
} else if (word.startsWith("CODE")) {
add = "6" + word[4..-1]
} else {
var figs = false
for (c in word) {
if (efigs.contains(c)) {
if (figs) {
add = add + c * 3
} else {
figs = true
add = add + fsl + c * 3
}
} else {
var ec = emap[c]
if (!ec) {
Fiber.abort("Message contains unrecognized character '%(c)'.")
}
if (figs) {
add = add + fsl + ec
figs = false
} else {
add = add + ec
}
}
}
if (figs && i < wc - 1) add = add + fsl
}
res = res + add
if (i < wc - 1) res = res + spc
}
return res
}
var decode = Fn.new { |s|
var res = ""
var sc = s.count
var figs = false
var i = 0
while (i < sc) {
var c = s[i]
var ix = -1
if (figs) {
if (s[i..i+1] != fsl) {
res = res + c
i = i + 3
} else {
figs = false
i = i + 2
}
} else if ((ix = drow1.indexOf(c)) >= 0) {
res = res + dmap[drow1[ix]]
i = i + 1
} else if (c == "6") {
res = res + "CODE" + s[i+1..i+3]
i = i + 4
} else if (c == "7" || c == "8") {
var d = s[i+1]
res = res + dmap[c + d]
i = i + 2
} else if (c == "9") {
var d = s[i+1]
if (d == "0") {
res = res + " "
} else if (d == "1") {
res = res + "."
} else if (d == "8") {
figs = !figs
} else {
res = res + dwords[c + d]
}
i = i + 2
}
}
return res
}
var msg = "Admin ACK your MSG. CODE291 SEND further 2000 SUPP to HQ by 1 March"
System.print("Message:\n%(msg)")
var enc = encode.call(msg)
System.print("\nEncoded:\n%(enc)")
var dec = decode.call(enc)
System.print("\nDecoded:\n%(dec)")
- Output:
Message: 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