aboutsummaryrefslogtreecommitdiff
path: root/challenge-190/wlmb/perl/ch-2.pl
blob: a91f330b1a8dabdcdf94c0371ac653a22e1ed94c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#!/usr/bin/env perl
# Perl weekly challenge 190
# Task 2:  Decoded List
#
# See https://wlmb.github.io/2022/11/07/PWC190/#task-2-decoded-list
use v5.36;
use experimental qw(try);
die <<"EOF" unless @ARGV;
Usage: $0 N1 [N2...]
to decode the numbers N1, N2...
EOF
my @letters=("", "A".."Z"); # Base 1 array of ascii letters
sub iterator($n){ #Create an iterator for all decodings of the number $n
    my $counter=0;
    my $length=length $n; # number of digits
    my @digits0=split "", $n;
    sub {
        COUNTER: while($counter<2**$length){
            my @digits=@digits0; # copy digits
            my @bits=split "",
                my $bits=sprintf "%0.${length}b", $counter++; # convert to binary, advance counter
            next COUNTER if $bits=~/11/; # Don't stick more than 2 consecutive letters
            my @output;
            while(@bits && @digits){
                my $bit=pop @bits;
                next COUNTER if @digits<2 && $bit==1; # Not enough digits to join
                splice(@digits,-2,2,$digits[-2].$digits[-1]),next if $bit==1; # Join last two digits
                unshift @output, my $m=pop @digits if $bit==0; # or pop last number
                next COUNTER if $m==0 or $m>=@letters; # Number too large or too small, restart
            }
            return @letters[@output]; # Found a decoding. Convert numbers to letters and return them
        }
        (); # Didn't find another decoding, return a null list
    }
}
for(@ARGV){
    try {
	die "Only digits allowed: $_" unless /^\d*$/;
        die "Empty input" unless /./;
	my $it=iterator($_);
	print "$_ -> ";
	my @decoded;
	print @decoded," " while(@decoded=$it->()); # Print all possible decodings
	say "";
    }
    catch($m){
        say "Error: $m";
    }
}