Extended Straddling Checkerboard: Difference between revisions
Attempted to make this into a draft task now a way forward seems reasonably clear. |
added transcription of ct37w |
||
Line 3: | Line 3: | ||
;Task |
;Task |
||
Implement encoding and decoding of a message using the extended straddling checkerboard, CT-37w, described in the reference below. |
Implement encoding and decoding of a message using the extended straddling checkerboard, CT-37w, as described in the reference below. |
||
You may switch the codes for F/L (99) and SUPP (98) to help differentiate the code for '9' from that of '999' |
You may switch the codes for F/L (99) and SUPP (98) to help differentiate the code for '9' from that of '999', so we would then have F/L (98) and SUPP (99) as follows: |
||
<pre> |
|||
A E I N O T CODE |
|||
0 1 2 3 4 5 6 |
|||
B C D F G H J K L M |
|||
70 71 72 73 74 75 76 77 78 79 |
|||
P Q R S U V W X Y Z |
|||
80 81 82 83 84 85 86 87 88 89 |
|||
SPC (.) ACK REQ MSG RV GRD SND F/L SUP |
|||
90 91 92 93 94 95 96 97 98 99 |
|||
0 1 2 3 4 5 6 7 8 9 |
|||
000 111 222 333 444 555 666 777 888 999 |
|||
</pre> |
|||
There is no need to create a word dictionary for CODE (6). It suffices to just include CODE followed by some 3 digit number in the message to be encoded. |
There is no need to create a word dictionary for CODE (6). It suffices to just include CODE followed by some 3 digit number in the message to be encoded. |
Revision as of 16:08, 25 March 2024
An extended Straddling Checkerboard, is like the regular Straddling checkerboard, but allows word dictionaries and arbitrary functional codes such as FIGURE, where you can specify a number literally.
- Task
Implement encoding and decoding of a message using the extended straddling checkerboard, CT-37w, as described in the reference below.
You may switch the codes for F/L (99) and SUPP (98) to help differentiate the code for '9' from that of '999', so we would then have F/L (98) and SUPP (99) as follows:
A E I N O T CODE 0 1 2 3 4 5 6 B C D F G H J K L M 70 71 72 73 74 75 76 77 78 79 P Q R S U V W X Y Z 80 81 82 83 84 85 86 87 88 89 SPC (.) ACK REQ MSG RV GRD SND F/L SUP 90 91 92 93 94 95 96 97 98 99 0 1 2 3 4 5 6 7 8 9 000 111 222 333 444 555 666 777 888 999
There is no need to create a word dictionary for CODE (6). It suffices to just include CODE followed by some 3 digit number in the message to be encoded.
Test your solution by encoding and decoding the message:
'Admin ACK your MSG. CODE291 SEND further 2000 SUPP to HQ by 1 March'
- Related task
- Reference
-
- Cipher Machines web page on Checkerboards: Checkerboards
Julia
const row1, row2, row3, row4 = "AEINOT", "BCDFGHJKLM", "PQRSUVWXYZ", " ."
const emap = Dict{String,String}()
for (row, k) in [(row1, -1), (row2, 69), (row3, 79), (row4, 89)]
for i in eachindex(row)
emap[string(row[i])] = string(i + k)
end
end
const dmap = Dict{String,String}(v => k for (k, v) in emap)
const ewords = Dict{String,String}(
"ACK" => "92",
"REQ" => "93",
"MSG" => "94",
"RV" => "95",
"GRID" => "96",
"SEND" => "97",
"SUPP" => "99",
)
const dwords = Dict{String,String}(v => k for (k, v) in ewords)
const efigs, spc, dot, fsl, drow1 = "0123456789", "90", "91", "98", "012345"
function encode(s)
s, res = uppercase(s), ""
words = split(s, r"\s")
wc = length(words)
for i = 1:wc
word, add = words[i], ""
if haskey(ewords, word)
add = ewords[word]
elseif haskey(ewords, word[begin:end-1]) && word[end] == "."
add = ewords[word[begin:end-1]] * dot
elseif startswith(word, "CODE")
add = "6" * word[begin+4:end]
else
figs = false
for c in word
if contains(efigs, c)
if figs
add *= c^3
else
figs = true
add *= fsl * c^3
end
else
ec = get(emap, string(c), "")
isempty(ec) && error("Message contains unrecognized character $c.")
if figs
add *= fsl * ec
figs = false
else
add *= ec
end
end
end
if figs && i <= wc - 1
add *= fsl
end
end
res *= add
if i <= wc - 1
res *= spc
end
end
return res
end
function decode(s)
res, sc, figs, i = "", length(s), false, 1
while i <= sc
ch = s[i]
c = string(ch)
if figs
if s[i:i+1] != fsl
res *= c
i += 3
else
figs = false
i += 2
end
elseif !((ix = findfirst(==(ch), drow1)) isa Nothing)
res *= dmap[string(drow1[ix])]
i += 1
elseif c == "6"
res *= "CODE" * s[i+1:i+3]
i += 4
elseif c == "7" || c == "8"
d = string(s[i+1])
res *= dmap[c*d]
i += 2
elseif c == "9"
d = string(s[i+1])
if d == "0"
res *= " "
elseif d == "1"
res *= "."
elseif d == "8"
figs = !figs
else
res *= dwords[c*d]
end
i += 2
end
end
return res
end
const msg = "Admin ACK your MSG. CODE291 SEND further 2000 SUPP to HQ by 1 March"
println("Message:\n$msg")
enc = encode(msg)
println("\nEncoded:\n$enc")
dec = decode(enc)
println("\nDecoded:\n$dec")
- Output:
Message: Admin ACK your MSG. CODE291 SEND further 2000 SUPP to HQ by 1 March Encoded: 0727923909290884848290798374919062919097907384825751829098222000000000989099905490758190708890981119890790827175 Decoded: ADMIN ACK YOUR MSG. CODE291 SEND FURTHER 2000 SUPP TO HQ BY 1 MARCH
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 Python 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