Straddling checkerboard

From Rosetta Code
Straddling checkerboard is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

Implement functions to encrypt and decrypt a message using the straddling checkerboard method. When setting the checkerboard up, it should take a 28 character alphabet (A-Z plus a full stop and an escape character) and two different numbers representing the blanks in the first row. The output will be a series of decimal digits.

When encrypting, numbers should be encrypted by inserting the escape character before each digit, then including the digit unencrypted. This should be reversed for decryption.

ALGOL 68

Translation of: C++

Note: This specimen retains the original C++ coding style.

Works with: ALGOL 68 version Revision 1 - no extensions to language used.
Works with: ALGOL 68G version Any - tested with release 1.18.0-9h.tiny.

<lang algol68>#!/usr/local/bin/a68g --script #

PRIO MIN=5, MAX=5; OP MIN = (INT a, b)INT: (a<b|a|b),

  MAX = (INT a, b)INT: (a>b|a|b);

MODE STRADDLINGCHECKERBOARD=STRUCT(

 [0:9]CHAR first, second, third,
 [ABS ".": ABS "Z"]STRING table,
 INT row u, row v,
 CHAR esc, skip

);

STRUCT(

 PROC (REF STRADDLINGCHECKERBOARD #self#, STRING #alphabet#, INT #u#, INT #v#)VOID init,
 PROC (REF STRADDLINGCHECKERBOARD #self#, STRING #plain#)STRING encode,
 PROC (REF STRADDLINGCHECKERBOARD #self#, STRING #plain#)STRING decode

) sc class = (

  1. PROC init = # (REF STRADDLINGCHECKERBOARD self, STRING in alphabet, INT u, v)VOID:
 (
   STRING alphabet = in alphabet[@0];
   esc OF self := alphabet[UPB alphabet]; # use the last CHAR as the escape #
   skip OF self := alphabet[UPB alphabet-1];
   row u OF self := u MIN v;
   row v OF self := u MAX v;
   OP DIGIT = (INT i)CHAR: REPR(ABS "0" + i );
   INT j := LWB alphabet;
 # (first OF self)[u] := (first OF self)[v] := skip; #
   FOR i FROM LWB first OF self TO UPB first OF self  DO
     IF i NE u AND i NE v THEN
       (first OF self)[i] := alphabet[j];
       (table OF self)[ABS alphabet[j]] := DIGIT i;
       j+:=1
     FI;
     (second OF self)[i] := alphabet[i+8];
     (table OF self)[ABS alphabet[i+8]] := DIGIT (row u OF self) + DIGIT i;
     (third OF self)[i] := alphabet[i+18];
     (table OF self)[ABS alphabet[i+18]] := DIGIT (row v OF self) + DIGIT i
   OD
 ),
  1. PROC encode = # (REF STRADDLINGCHECKERBOARD self, STRING plain)STRING:
 (
   STRING esc = (table OF self)[ABS (esc OF self)];
   INT l2u = ABS "A" - ABS "a";
   STRING out := "";
   FOR i FROM LWB plain TO UPB plain DO
     CHAR c := plain[i];
     IF "a" <= c AND c <= "z" THEN
       c := REPR ( ABS c + l2u) FI;
     IF "A" <= c AND c <= "Z" THEN
       out +:= (table OF self)[ABS c]
     ELIF "0" <= c AND c <= "9" THEN
       out +:= esc + c
     FI
   OD;
   out # EXIT #
 ),
  1. PROC decode = # (REF STRADDLINGCHECKERBOARD self, STRING cipher)STRING:
 (
   CHAR null = REPR 0;
   STRING out;
   INT state := 0;
   FOR i FROM LWB cipher TO UPB cipher DO
     INT n := ABS cipher[i] - ABS "0";
     CHAR next :=
       CASE state IN
         #1:# (second OF self)[n],
         #2:# (third OF self)[n],
         #3:# cipher[i]
       OUT
         IF n = row u OF self THEN
           state := 1; null
         ELIF n = row v OF self THEN
           state := 2; null
         ELSE
           (first OF self)[n]
         FI
       ESAC;
     IF next = "/" THEN
       state := 3
     ELIF next NE null THEN
       state := 0;
       out +:= next
     FI
   OD;
   out # EXIT #
 )

);

main: (

 STRADDLINGCHECKERBOARD sc; (init OF sc class)(sc, "HOLMESRTABCDFGIJKNPQUVWXYZ./", 3, 7);
 STRING original := "One night-it was on the twentieth of March, 1888-I was returning"[@0];
 STRING en := (encode OF sc class)(sc, original);
 STRING de := (decode OF sc class)(sc, en);
 printf(($ggl$,
         "Original: ", original,
         "Encoded:  ", en,
         "Decoded:  ", de
 ))

)</lang> Output:

Original: One night-it was on the twentieth of March, 1888-I was returning
Encoded:  139539363509369743061399059745399365901344308320791798798798367430685972839363935
Decoded:  ONENIGHTITWASONTHETWENTIETHOFMARCH1888IWASRETURNING

C

Library: GLib

<lang c>#include <stdio.h>

  1. include <stdlib.h>
  2. include <string.h>
  3. include <stdbool.h>
  4. include <ctype.h>
  5. include <glib.h>
  1. define ROWS 4
  2. define COLS 10
  3. define NPRX "/"

/* wikipedia table char *table[ROWS][COLS] = {

 { "0", "1", "2",  "3", "4", "5", "6",  "7", "8", "9" },
 { "E", "T", NULL, "A", "O", "N", NULL, "R", "I", "S  },
 { "B", "C", "D",  "F", "G", "H", "J",  "K", "L", "M" },
 { "P", "Q", NPRX, "U", "V", "W", "X",  "Y", "Z", "." }

};

  • /

/* example of extending the table, COLS must be 11 char *table[ROWS][COLS] = {

 { "0", "1", "2", "3",  "4", "5", "6", "7",  "8", "9",  ":"  },
 { "H", "O", "L", NULL, "M", "E", "S", NULL, "R", "T",  ","  },
 { "A", "B", "C", "D",  "F", "G", "I", "J",  "K", "N",  "-"  },
 { "P", "Q", "U", "V",  "W", "X", "Y", "Z",  ".", NPRX, "?"  }

};

  • /

// task table char *table[ROWS][COLS] = {

 { "0", "1", "2", "3",  "4", "5", "6", "7",  "8", "9"  },
 { "H", "O", "L", NULL, "M", "E", "S", NULL, "R", "T"  },
 { "A", "B", "C", "D",  "F", "G", "I", "J",  "K", "N"  },
 { "P", "Q", "U", "V",  "W", "X", "Y", "Z",  ".", NPRX }

};


GHashTable *create_table_from_array(char *table[ROWS][COLS], bool is_encoding) {

 char buf[16];
 GHashTable *r = g_hash_table_new_full(g_str_hash, g_str_equal, free, free);
 size_t i, j, k, m;
 for(i = 0, m = 0; i < COLS; i++)
 {
   if (table[1][i] == NULL) m++;
 }
 const size_t SELNUM = m;
 
 size_t selectors[SELNUM];
 size_t numprefix_row, numprefix_col;
 bool has_numprefix = false;
 // selectors keep the indexes of the symbols to select 2nd and 3rd real row;
 // nulls must be placed into the 2nd row of the table
 for(i = 0, k = 0; i < COLS && k < SELNUM; i++)
 {
   if ( table[1][i] == NULL )
   {
     selectors[k] = i;
     k++;
   }
 }
 // numprefix is the prefix to insert symbols from the 1st row of table (numbers)
 for(j = 1; j < ROWS; j++)
 {
   for(i = 0; i < COLS; i++)
   {
     if (table[j][i] == NULL) continue;
     if ( strcmp(table[j][i], NPRX) == 0 )
     {
       numprefix_col = i;
       numprefix_row = j;
       has_numprefix = true;

break;

     }
   }
 }
 // create the map for each symbol
 for(i = has_numprefix ? 0 : 1; i < ROWS; i++)
 {
   for(j = 0; j < COLS; j++)
   {
     if (table[i][j] == NULL) continue;
     if (strlen(table[i][j]) > 1)
     {

fprintf(stderr, "symbols must be 1 byte long\n"); continue; // we continue just ignoring the issue

     }
     if (has_numprefix && i == (ROWS-1) && j == numprefix_col && i == numprefix_row) continue;
     if (has_numprefix && i == 0)
     {

snprintf(buf, sizeof(buf), "%s%s%s", table[0][selectors[SELNUM-1]], table[0][numprefix_col], table[0][j]);

     }
     else if (i == 1)
     {

snprintf(buf, sizeof(buf), "%s", table[0][j]);

     }
     else
     {

snprintf(buf, sizeof(buf), "%s%s", table[0][selectors[i-2]], table[0][j]);

     }
     if (is_encoding) g_hash_table_insert(r, strdup(table[i][j]), strdup(buf));
     else g_hash_table_insert(r, strdup(buf), strdup(table[i][j]));
   }
 }
 if (is_encoding) g_hash_table_insert(r, strdup("mode"), strdup("encode"));
 else g_hash_table_insert(r, strdup("mode"), strdup("decode"));
 return r;

}

char *decode(GHashTable *et, const char *enctext) {

 char *r = NULL;
 if (et == NULL || enctext == NULL || strlen(enctext) == 0 ||
     g_hash_table_lookup(et, "mode") == NULL ||
     strcmp(g_hash_table_lookup(et, "mode"), "decode") != 0) return NULL;
 GString *res = g_string_new(NULL);
 GString *en = g_string_new(NULL);
 for( ; *enctext != '\0'; enctext++ )
 {
   if (en->len < 3)
   {
     g_string_append_c(en, *enctext);
     r = g_hash_table_lookup(et, en->str);
     if (r == NULL) continue;
     g_string_append(res, r);
     g_string_truncate(en, 0);
   }
   else
   {
     fprintf(stderr, "decoding error\n");
     break;
   }
 }
 
 r = res->str;
 g_string_free(res, FALSE);
 g_string_free(en, TRUE);
 return r;

}

char *encode(GHashTable *et, const char *plaintext, int (*trasf)(int), bool compress_spaces) {

 GString *s;
 char *r = NULL;
 char buf[2] = { 0 };
 if (plaintext == NULL ||
     et == NULL || g_hash_table_lookup(et, "mode") == NULL ||
     strcmp(g_hash_table_lookup(et, "mode"), "encode") != 0) return NULL;
 s = g_string_new(NULL);
 for(buf[0] = trasf ? trasf(*plaintext) : *plaintext; 
     buf[0] != '\0'; 
     buf[0] = trasf ? trasf(*++plaintext) : *++plaintext)
 {
   if ( (r = g_hash_table_lookup(et, buf)) != NULL )
   {
     g_string_append(s, r);
   }
   else if (isspace(buf[0])) 
   {
     if (!compress_spaces) g_string_append(s, buf);
   } 
   else
   {
     fprintf(stderr, "char '%s' is not encodable%s\n",

isprint(buf[0]) ? buf : "?", !compress_spaces ? ", replacing with a space" : "");

     if (!compress_spaces) g_string_append_c(s, ' ');
   }
 }
 r = s->str;
 g_string_free(s, FALSE);
 return r;

}


int main() {

 GHashTable *enctab = create_table_from_array(table, true);  // is encoding? true
 GHashTable *dectab = create_table_from_array(table, false); // is encoding? false (decoding)
 const char *text = "One night-it was on the twentieth of March, 1888-I was returning";
 char *encoded = encode(enctab, text, toupper, true);
 printf("%s\n", encoded);
 char *decoded = decode(dectab, encoded);
 printf("%s\n", decoded);
 free(decoded);
 free(encoded);
 g_hash_table_destroy(enctab);
 g_hash_table_destroy(dectab);
 return 0;

}</lang>

Shorter version

<lang C>#include <stdio.h>

  1. include <stdlib.h>

char * board = "ET AON RIS"

               "BCDFGHJKLM"
               "PQ/UVWXYZ.";

char encode[128] = {0}; char decode[128] = {0}; int row[2] = {0};

void read_table(char *s) {

       int i, code;
       for (i = 0; i < 30; i++) {
               if (s[i] == '\0') {
                       fprintf(stderr, "Table too short\n");
                       exit(1);
               }
               if (s[i] == ' ') {
                       row[  row[0] ? 1 : 0 ] = i;
                       continue;
               }
               code = ((i < 10) ? 0 : i < 20 ? row[0] : row[1])
                               * 10 + (i % 10);
               encode[0 + s[i]] = code; /* guess what 0 + s[i] does, sigh */
               decode[code] = s[i];
       }

}

void encipher(char *in, char *out, int strip) {

  1. define PUTCODE(c) { if (c > 9) {*(out++) = c / 10 + '0'; c %= 10;} *(out++) = c + '0'; }
       int c, code;
       while ((c = *(in++)) != '\0') {
               if (c >= '0' && c <= '9') {
                       code = encode['.'];
                       c -= '0';
                       PUTCODE(code);
                       PUTCODE(c);
                       continue;
               }
               c &= ~0x20;
               if (c >= 'A' && c <= 'Z') code = encode[c];
               else if (strip && !c )    continue;
               else                      code = encode['/'];
               PUTCODE(code);
       }
       *(out++) = '\0';

}

void decipher(char *in, char *out, int strip) {

       int c;
       while ((c = *(in++)) != '\0') {
               c -= '0';
               if (c == row[0] || c == row[1])
                       c = c * 10 + *(in++) - '0';
               c = decode[c];
               if (c == '.') c = *(in++);
               if (c == '/' && !strip) c = ' ';
               *(out++) = c;
       }
       *(out++) = '\0';

}

int main() {

       char *msg = "In the winter 1965/we were hungry/just barely alive";
       char enc[100] = {0}, dec[100] = {0};
       read_table(board);
       printf("message: %s\n", msg);
       encipher(msg, enc, 0); printf("encoded: %s\n", enc);
       decipher(enc, dec, 0); printf("decoded: %s\n", dec);
       printf("\nNo spaces:\n");
       encipher(msg, enc, 1); printf("encoded: %s\n", enc);
       decipher(enc, dec, 1); printf("decoded: %s\n", dec);
       return 0;

}</lang>Output:<lang>message: In the winter 1965/we were hungry/just barely alive encoded: 85621250626585107626916996966956265062650706225635247676226639162203702867623288640 decoded: IN THE WINTER 1965 WE WERE HUNGRY JUST BARELY ALIVE

No spaces: encoded: 851250658510769169969669562650650702563524767622663912037028673288640 decoded: INTHEWINTER1965/WEWEREHUNGRY/JUSTBARELYALIVE</lang>

C++

<lang cpp>#include <iostream>

  1. include <string>
  2. include <map>
  3. include <algorithm> // for min, max

using namespace std;

class StraddlingCheckerboard {

 map<char, string> table;
 char first[10], second[10], third[10];
 int rowU, rowV;

public:

 StraddlingCheckerboard(const string &alphabet, int u, int v)
 {
   rowU = min(u, v);
   rowV = max(u, v);
   for(int i = 0, j = 0; i < 10; ++i)
   {
     if(i != u && i != v)
     {
       first[i] = alphabet[j];
       table[alphabet[j]] = '0' + i;
       ++j;
     }
     second[i] = alphabet[i+8];
     table[alphabet[i+8]] = '0' + rowU;
     table[alphabet[i+8]] += '0' + i;
     third[i] = alphabet[i+18];
     table[alphabet[i+18]] = '0' + rowV;
     table[alphabet[i+18]] += '0' + i;
   }
 }
 string encode(const string &plain)
 {
   string out;
   for(int i = 0; i < plain.size(); ++i)
   {
     char c = plain[i];
     if(c >= 'a' && c <= 'z')
       c += 'A' - 'a';
     if(c >= 'A' && c <= 'Z')
       out += table[c];
     else if(c >= '0' && c <= '9')
     {
       out += table['/'];
       out += c;
     }
   }
   return out;
 }
 string decode(const string &cipher)
 {
   string out;
   int state = 0;
   for(int i = 0; i < cipher.size(); ++i)
   {
     int n = cipher[i] - '0';
     char next = 0;
     if(state == 1)
       next = second[n];
     else if(state == 2)
       next = third[n];
     else if(state == 3)
       next = cipher[i];
     else if(n == rowU)
       state = 1;
     else if(n == rowV)
       state = 2;
     else
       next = first[n];
     if(next == '/')
       state = 3;
     else if(next != 0)
     {
       state = 0;
       out += next;
     }
   }
   return out;
 }

};</lang>

Test program: <lang cpp>int main() {

 StraddlingCheckerboard sc("HOLMESRTABCDFGIJKNPQUVWXYZ./", 3, 7);
 
 string original = "One night-it was on the twentieth of March, 1888-I was returning";
 string en = sc.encode(original);
 string de = sc.decode(en);
 cout << "Original: " << original << endl;
 cout << "Encoded:  " << en << endl;
 cout << "Decoded:  " << de << endl;
 return 0;

}</lang>

Output:

Original: One night-it was on the twentieth of March, 1888-I was returning
Encoded:  139539363509369743061399059745399365901344308320791798798798367430685972839363935
Decoded:  ONENIGHTITWASONTHETWENTIETHOFMARCH1888IWASRETURNING

D

Partially based on the PicoLisp version: <lang d>import std.stdio, std.algorithm, std.string;

auto T = [["79", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"],

         ["",   "H", "O", "L", "",  "M", "E", "S", "",  "R", "T"],
         ["3",  "A", "B", "C", "D", "F", "G", "I", "J", "K", "N"],
         ["7",  "P", "Q", "U", "V", "W", "X", "Y", "Z", ".", "/"]];

string straddle(in string s) {

   string r;
   foreach (c; s.toupper())
       foreach (L; T)
           if (L.canFind("" ~ c))
               r ~= L[0] ~ T[0][L.countUntil("" ~ c)];
   return r;

}

string unStraddle(in string s) {

   string r;
   for (int i = 0; i < s.length; i++) {
       int j = [T[2][0], T[3][0]].countUntil(s[i .. i+1]);
       if (j < 0)
           r ~= T[1][T[0].countUntil(s[i .. i+1])];
       else {
           auto v = T[2 + j][T[0].countUntil(s[++i..i+1])];
           r ~= (v == "/") ? s[++i .. i+1] : v;
       }
   }
   return r;

}

void main() {

   const orig = "One night-it was on the twentieth of March, 1888-I was returning";
   writeln("Original: ", orig);
   writeln("Encoded:  ", straddle(orig));
   writeln("Decoded:  ", unStraddle(straddle(orig)));

}</lang> Output:

Original: One night-it was on the twentieth of March, 1888-I was returning
Encoded:  139539363509369743061399059745399365901344308320791798798798367430685972839363935
Decoded:  ONENIGHTITWASONTHETWENTIETHOFMARCH1888IWASRETURNING

Alternative version

Translation of: C++

Same output: <lang d>import std.stdio, std.algorithm, std.ctype;

/*const*/ struct StraddlingCheckerboard {

 private:
   string[char] table;
   char[10] first, second, third;
   const int rowU, rowV;
 public:
   /*pure nothrow*/ this(in string alphabet, in int u, in int v) {
       rowU = min(u, v);
       rowV = max(u, v);
       int j = 0;
       foreach (i; 0 .. 10) {
           if (i != u && i != v) {
               first[i] = alphabet[j];
               table[alphabet[j]] = "" ~ cast(char)('0' + i);
               j++;
           }
           second[i] = alphabet[i + 8];
           table[alphabet[i + 8]] = "" ~ cast(char)('0' + rowU) ~
                                    cast(char)('0' + i);
           third[i] = alphabet[i + 18];
           table[alphabet[i + 18]] = "" ~ cast(char)('0' + rowV) ~
                                     cast(char)('0' + i);
       }
   }
   /*nothrow*/ pure string encode(in string plain) const {
       string r;
       foreach (c; plain) {
           if (islower(c))      r ~= table[cast(char)toupper(c)];
           else if (isupper(c)) r ~= table[c];
           else if (isdigit(c)) r ~= table['/'] ~ c;
       }
       return r;
   }
   pure nothrow string decode(in string cipher) const {
       string r;
       int state = 0;
       foreach (c; cipher) {
           const int n = c - '0';
           char next = '\0';
           if (state == 1)      next = second[n];
           else if (state == 2) next = third[n];
           else if (state == 3) next = c;
           else if (n == rowU)  state = 1;
           else if (n == rowV)  state = 2;
           else                 next = first[n];
           if (next == '/')
               state = 3;
           else if (next != 0) {
               state = 0;
               r ~= next;
           }
       }
       return r;
   }

}

void main() {

   const orig = "One night-it was on the twentieth of March, 1888-I was returning";
   writeln("Original: ", orig);
   const sc = StraddlingCheckerboard("HOLMESRTABCDFGIJKNPQUVWXYZ./", 3, 7);
   const en = sc.encode(orig);
   writeln("Encoded:  ", en);
   writeln("Decoded:  ", sc.decode(en));

}</lang>

Icon and Unicon

<lang Icon>procedure main() StraddlingCheckerBoard("setup","HOLMESRTABCDFGIJKNPQUVWXYZ./", 3,7)

text := "One night. it was on the twentieth of March, 1888. I was returning" write("text = ",image(text)) write("encode = ",image(en := StraddlingCheckerBoard("encode",text))) write("decode = ",image(StraddlingCheckerBoard("decode",en))) end

procedure StraddlingCheckerBoard(act,text,b1,b2) static SCE,SCD case act of {

  "setup" : {  
     if (b1 < b2 < 10) & (*text = *cset(text) = 28) then {
        SCE := table("")   
        SCD := table()         
        esc := text[-1]                               # escape
        every text[(b1|b2)+1+:0] := " "               # blanks
        uix := ["",b1,b2]                             # 1st position
        every c := text[1 + (i := 0 to *text-1)] do   # build translation
           if c ~== " " then                          # skip blanks
              SCD[SCE[c] := SCE[map(c)] := uix[i/10+1]||(i%10) ] := c         
        every c := !&digits do 
           SCD[SCE[c] := SCE[esc] || c] := c
        delete(SCD,SCE[esc])
        delete(SCE,esc)          
        }
        else stop("Improper setup: ",image(text),", ",b1,", ",b2)
     }
  "encode" : {
     every (s := "") ||:= x := SCE[c := !text]
     return s
     }
  "decode" : {  
     s := ""
     text ? until pos(0) do 
        s ||:= \SCD[k := move(1 to 3)]
     return s       
     }
  }

end</lang>

Output:

text   = "One night. it was on the twentieth of March, 1888. I was returning"
encode = "1395393635097836974306139905974539936590134430832079179879879878367430685972839363935"
decode = "ONENIGHT.ITWASONTHETWENTIETHOFMARCH1888.IWASRETURNING"

J

Solution: <lang j>'Esc Stop'=: '/.' 'Nums Alpha'=: '0123456789';'ABCDEFGHIJKLMNOPQRSTUVWXYZ' Charset=: Nums,Alpha,Stop

escapenum=: (,@:((; Esc&,)&>) Nums) rplc~ ] NB. escape numbers unescapenum=: ((, ; ' '&,@])"1 0 Nums"_) rplc~ ] NB. unescape coded numbers (x is escape code, y is cipher) expandKeyatUV=: 0:`[`(1 #~ 2 + #@])} #inv ] makeChkBrd=: Nums , expandKeyatUV

chkbrd=: conjunction define

 'uv key'=. n
 board=. uv makeChkBrd key
 select. m
 case. 0 do.                                                      NB. encode
   digits=. board 10&#.inv@i. escapenum y
   ' ' -.~ ,(":@{:"1 digits) ,.~ (1 1 0 2{":uv) {~ {."1 digits
 case. 1 do.                                                      NB. decode
   esc=. 0 chkbrd (uv;key) Esc                                    NB. find code for Esc char
   tmp=. esc unescapenum esc,'0',y                                
   tmp=. ((":uv) ((-.@e.~ _1&|.) *. e.~) tmp) <;.1 tmp            NB. box on chars from rows 0 2 3
   idx=. (}. ,~ (1 1 0 2{":uv) ":@i. {.) each tmp                 NB. recreate indices for rows 0 2 3
   idx=. ;(2&{. , [: ((0 1 $~ +:@#) #inv!.'1' ]) 2&}.) each idx   NB. recreate indices for row 1
   }.board {~ _2 (_&".)\ idx    
 end.

)</lang> Example usage: <lang j> preprocess=: (#~ Charset e.~ ])@toupper NB. verb to compress out non-alphanumerics

  chkbrdRC=: chkbrd (3 7;'HOLMESRTABCDFGIJKNPQUVWXYZ./')  NB. define adverb by applying Rosetta Code key to chkbrd conjunction
  0 chkbrdRC preprocess 'One night-it was on the twentieth of March, 1888-I was returning'

139539363509369743061399059745399365901344308320791798798798367430685972839363935

  1 chkbrdRC 0 chkbrdRC preprocess 'One night-it was on the twentieth of March, 1888-I was returning'

ONENIGHTITWASONTHETWENTIETHOFMARCH1888IWASRETURNING</lang> Or using the rules proposed by Util on the discussion page: <lang j> preprocess=: Stop&([`([: I. Charset -.@e.~ ])`]} toupper) NB. replace all non-alphanumerics with Stop

  1 chkbrdRC 0 chkbrdRC preprocess 'One night-it was on the twentieth of March, 1888-I was returning'

ONE.NIGHT.IT.WAS.ON.THE.TWENTIETH.OF.MARCH..1888.I.WAS.RETURNING</lang>


Another method is to create a set of keys which correspond to the encoded letters in the alphabet. This allows for the relatively simple design of the encoding and decoding functions as they are essentially hash table lookups.

Solution: <lang j>NB. stops setcode alphabet NB. creates verbs encode and decode which change between unencoded text and lists of encoded numbers

  setcode=:3 :0

2 6 setcode y

alphabet=:y, ,":"0 i.10 stops=: x alphkeys=. (a:,,&.>x) ([-.~ [:,,&.>/) i.10 esckey=: >(alphabet i. '/'){alphkeys numkeys=. esckey&,&.> i.10 keys=: alphkeys,numkeys

encode=: [:; keys{~ alphabet&i. break=: </.~ i.@# - _1|. ([: >/\.&.|. e.&stops) + _1|.2*esckey&E. decode=: alphabet {~ keys i. break i.0 0 )</lang>

Example usage: <lang j> 3 7 setcode 'HOLMESRTABCDFGIJKNPQUVWXYZ./'

  preprocess=: (#~ alphabet e.~ ])@toupper
  ,":"0 encode preprocess 'One night-it was on the twentieth of March, 1888-I was returning'

139539363509369743061399059745399365901344308320791798798798367430685972839363935

  decode encode preprocess 'One night-it was on the twentieth of March, 1888-I was returning'

ONENIGHTITWASONTHETWENTIETHOFMARCH1888IWASRETURNING</lang>

Perl 6

The .trans method in Perl 6 improves on Perl 5's tr/// by allowing multi-character translation tables.

We build the full table during .new, which simplifies .encode and .decode. <lang perl6>class Straddling_Checkerboard {

   has @!flat_board; # 10x3 stored as 30x1
   has $!plain2code; # Full translation table; invertable
   has @!table;      # Printable layout, like Wikipedia entry

   my $numeric_escape = '/';
   my $exclude = /<-[A..Z0..9.]>/; # Omit the escape character
   method display_table { say ~ .list for @!table };
   method decode ( Str $s --> Str ) {
       $s.trans($!plain2code.invert);
   }
   method encode ( Str $s, :$collapse? --> Str ) {
       my $replace = $collapse ??  !! '.';
       $s.uc.subst( $exclude, $replace, :g ).trans($!plain2code);
   }
   submethod BUILD ( :$alphabet, :$u where 0..9, :$v where 0..9 ) {
       die if $u == $v;
       die if $alphabet.comb.sort.join ne [~] './', 'A'..'Z';
       @!flat_board = $alphabet.uc.comb;
       @!flat_board.splice( $u min $v, 0, Any );
       @!flat_board.splice( $u max $v, 0, Any );
       @!table = [ ' ',             [ 0 ..  9]               ],
                 [ ' ', @!flat_board[ 0 ..  9].map: * // ' ' ],
                 [ $u,  @!flat_board[10 .. 19]               ],
                 [ $v,  @!flat_board[20 .. 29]               ];
       my @order = 0..9; # This may be passed as a param in the future
       my @nums = @order,
                  @order.map({ +"$u$_" }),
                  @order.map({ +"$v$_" });
       my %p2c = @!flat_board Z=> @nums;
       %p2c.delete(Any);
       %p2c{$_} = %p2c{$numeric_escape} ~ $_ for 0..9;
       $!plain2code = [%p2c.keys] => [%p2c.values];
   }

}

sub MAIN ( :$u = 3, :$v = 7, :$alphabet = 'HOLMESRTABCDFGIJKNPQUVWXYZ./' ) {

   my Straddling_Checkerboard $sc .= new: :$u, :$v, :$alphabet;
   $sc.display_table;
   for 0..1 -> $collapse {
       my $original = 'One night-it was on the twentieth of March, 1888-I was returning';
       my $en = $sc.encode($original, :$collapse);
       my $de = $sc.decode($en);
       say;
       say "Original: $original";
       say "Encoded:  $en";
       say "Decoded:  $de";
   }

}</lang>

Output:

  0 1 2 3 4 5 6 7 8 9
  H O L   M E S   R T
3 A B C D F G I J K N
7 P Q U V W X Y Z . /

Original: One night-it was on the twentieth of March, 1888-I was returning
Encoded:  13957839363509783697874306781397890578974539936590781347843083207878791798798798783678743067885972839363935
Decoded:  ONE.NIGHT.IT.WAS.ON.THE.TWENTIETH.OF.MARCH..1888.I.WAS.RETURNING

Original: One night-it was on the twentieth of March, 1888-I was returning
Encoded:  139539363509369743061399059745399365901344308320791798798798367430685972839363935
Decoded:  ONENIGHTITWASONTHETWENTIETHOFMARCH1888IWASRETURNING

PicoLisp

<lang PicoLisp>(de *Straddling

  (NIL  "H"  "O"  "L"  NIL  "M"  "E"  "S"  NIL  "R"  "T")
  ("3"  "A"  "B"  "C"  "D"  "F"  "G"  "I"  "J"  "K"  "N")
  ("7"  "P"  "Q"  "U"  "V"  "W"  "X"  "Y"  "Z"  "."  "/")
  ("79" "0"  "1"  "2"  "3"  "4"  "5"  "6"  "7"  "8"  "9") )

(de straddle (Str)

  (pack
     (mapcar
        '((C)
           (pick
              '((L)
                 (and
                    (index C (cdr L))
                    (cons (car L) (dec @)) ) )
              *Straddling ) )
        (chop (uppc Str)) ) ) )

(de unStraddle (Str)

  (pack
     (make
        (for (L (chop Str)  L)
           (let C (pop 'L)
              (setq C
                 (if (assoc C *Straddling)
                    (get (cdr @) (inc (format (pop 'L))))
                    (get (cdar *Straddling) (inc (format C))) ) )
              (link (if (= "/" C) (pop 'L) C)) ) ) ) ) )</lang>

Output:

: (straddle "One night-it was on the twentieth of March, 1888-I was returning")
-> "139539363509369743061399059745399365901344308320791798798798367430685972839363935"

: (unStraddle @)
-> "ONENIGHTITWASONTHETWENTIETHOFMARCH1888IWASRETURNING"

Python

Partially based on the PicoLisp version: <lang python>T = [["79", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"],

    ["",   "H", "O", "L", "",  "M", "E", "S", "",  "R", "T"],
    ["3",  "A", "B", "C", "D", "F", "G", "I", "J", "K", "N"],
    ["7",  "P", "Q", "U", "V", "W", "X", "Y", "Z", ".", "/"]]

def straddle(s):

   return "".join(L[0]+T[0][L.index(c)] for c in s.upper() for L in T if c in L)

def unstraddle(s):

   s = iter(s)
   for c in s:
       if c in [T[2][0], T[3][0]]:
           i = [T[2][0], T[3][0]].index(c)
           n = T[2 + i][T[0].index(s.next())]
           yield s.next() if n == "/" else n
       else:
           yield T[1][T[0].index(c)]

O = "One night-it was on the twentieth of March, 1888-I was returning" print "Encoded:", straddle(O) print "Decoded:", "".join(unstraddle(straddle(O)))</lang> Output:

Encoded: 139539363509369743061399059745399365901344308320791798798798367430685972839363935
Decoded: ONENIGHTITWASONTHETWENTIETHOFMARCH1888IWASRETURNING