diff options
| -rw-r--r-- | challenge-162/wlmb/blog.txt | 1 | ||||
| -rwxr-xr-x | challenge-162/wlmb/perl/ch-1.pl | 18 | ||||
| -rwxr-xr-x | challenge-162/wlmb/perl/ch-2.pl | 92 |
3 files changed, 111 insertions, 0 deletions
diff --git a/challenge-162/wlmb/blog.txt b/challenge-162/wlmb/blog.txt new file mode 100644 index 0000000000..33ee66ff2f --- /dev/null +++ b/challenge-162/wlmb/blog.txt @@ -0,0 +1 @@ +https://wlmb.github.io/2022/04/25/PWC162/ diff --git a/challenge-162/wlmb/perl/ch-1.pl b/challenge-162/wlmb/perl/ch-1.pl new file mode 100755 index 0000000000..3cca0a6512 --- /dev/null +++ b/challenge-162/wlmb/perl/ch-1.pl @@ -0,0 +1,18 @@ +#!/usr/bin/env perl +# Perl weekly challenge 162 +# Task 1: ISBN-13 +# +# See https://wlmb.github.io/2022/04/25/PWC162/#task-1-isbn-13 +use v5.12; +use warnings; +use List::Util qw(sum); +die "Usage: ./ch-1.pl ISBN1 [ISBN2] ...\n", + "to calculate the check digit of the ISBN-13 codes ISBNi\n", + "The first 12 digits should be provided. Extra digits and punctuation are discarded" + unless @ARGV; +my @factors=(1,3)x6; +foreach(@ARGV){ + my @digits=grep{/\d/}split "", $_; # remove non-digits + say "ISBN-13 check digit for $_ is ", + (-(sum map {$factors[$_]*$digits[$_]} 0..11)%10) +} diff --git a/challenge-162/wlmb/perl/ch-2.pl b/challenge-162/wlmb/perl/ch-2.pl new file mode 100755 index 0000000000..9e41d77208 --- /dev/null +++ b/challenge-162/wlmb/perl/ch-2.pl @@ -0,0 +1,92 @@ +#!/usr/bin/env perl +# Perl weekly challenge 162 +# Task 2: Wheatstone-Playfair +# +# See https://wlmb.github.io/2022/04/25/PWC162/#task-2-wheatstone-playfair +use v5.12; +use warnings; +use Try::Tiny; # Use try/catch to manage errors. +use POSIX qw(floor); +my %commands=(encrypt=>\&encrypt, decrypt=>\&decrypt); +die "Usage: ch-2.pl C1 K1 S1 [C2 K2 S2]...\n", + "to run command Ci (encrypt or decrypt) on string Si with key Ki\n" + unless @ARGV and @ARGV%3==0; +while(1){ + my ($command, $key, $string)=splice @ARGV, 0, 3; + last unless $command; + my ($l_command, $l_key, $l_string)=map {lc} ($command, $key, $string); # normalize + try { + die "Wrong command: $command\n" unless defined $commands{$l_command}; + my $result=$commands{$l_command}->($l_key, $l_string); # encrypt or decrypt + say "$command($key, $string)=\n\t", join " ", split /(.....)/,$result; + } + catch { + say $_; + } +} +sub encrypt { + my ($key, $message)=@_; + map {s/[^a-z]//g; s/j/i/g;} ($key, $message); # only letters, j=i + my ($table_a, $table_h)=make_tables($key); # map coordinates to letters and viceversa + my @message=split '', $message; + my @encrypted; + while(@message){ + my $first=shift @message; + my $second; + $second=@message && $message[0] ne $first # check availability, repetitions + ?shift @message + :'x'; # dummy x to avoid repetitions + my ($row1, $col1)=@{$table_h->{$first}}; # get coordinates + my ($row2, $col2)=@{$table_h->{$second}}; + push(@encrypted, $table_a->[$row1][$col2], + $table_a->[$row2][$col1]), next # exchange corners + if $row1!=$row2 && $col1 != $col2; # rectangle + push(@encrypted, $table_a->[$row1][($col1+1)%5], + $table_a->[$row2][($col2+1)%5]), next # rotate right + if $row1==$row2 && $col1 != $col2; # single row + push(@encrypted, $table_a->[($row1+1)%5][$col1], + $table_a->[($row2+1)%5][$col2]), next # rotate down + if $row1!=$row2 && $col1 == $col2; # single column + # I can only arrive here if there are two consequtive x's. Not + # sure what to do, so I'll just return them + push @encrypted, 'x', 'x'; # repeated x + } + return join '', @encrypted; +} +sub decrypt { # similar to encrypt but with opposite rotations. + my ($key, $message)=@_; + map {s/[^a-z]//g; s/j/i/g;} ($key, $message); # only letters, j=i + my ($table_a, $table_h)=make_tables($key); + my @message=split '', $message; + die "Encrypted string should have even length\n" if @message%2; + my @decrypted; + while(@message){ + my ($first, $second)=splice @message,0,2; + my ($row1, $col1)=@{$table_h->{$first}}; + my ($row2, $col2)=@{$table_h->{$second}}; + push(@decrypted, $table_a->[$row1][$col2], + $table_a->[$row2][$col1]), next # exchange corners + if $row1!=$row2 && $col1 != $col2; + push(@decrypted, $table_a->[$row1][($col1-1)%5], + $table_a->[$row2][($col2-1)%5]), next # rotate left + if $row1==$row2 && $col1 != $col2; + push(@decrypted, $table_a->[($row1-1)%5][$col1], + $table_a->[($row2-1)%5][$col2]), next # rotate up + if $row1!=$row2 && $col1 == $col2; + # I arrive here for the case xx + push @decrypted, 'x'; # remove repetition + } + return join '', @decrypted; +} + +sub make_tables { + my $key=shift; # only letters a-z and without j's and + my @letters=((split '', $key),('a'..'i', 'k'..'z')); # complete alphabet + my (@table, %table); + my $i=0; + foreach(@letters){ + my ($row, $col)=(floor($i/5), $i%5); + $table[$row][$col]=$_, $table{$_}=[$row,$col], ++$i unless defined $table{$_}; + } + return (\@table, \%table); +} |
