Straddling checkerboard
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
Note: This specimen retains the original C++ coding style.
<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 = (
- 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 ),
- 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 # ),
- 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++
<lang cpp>#include <iostream>
- include <string>
- include <map>
- 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
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"
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"