Extended Straddling Checkerboard

From Rosetta Code

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:


Python

<syntaxhighlight lang="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

  1. 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)))

</syntaxhighlihjt>

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