mscha icon

AoC 2016 day 23

mscha | PRO | 12/23/16 10:20:02 AM UTC | 0 ⭐ | 815 👁️ | Never ⏰ | []
Perl 6 |

0 B

|

None

|

0 👍

/

0 👎

#!/usr/bin/env perl6
 
use v6.c;
 
class Computer
{
    has @.instructions;
    has @.optimized;
    has @.code;
    has Int $.pos = 0;
    has Int $.counter = 0;
    has %.register = :a(0), :b(0), :c(0), :d(0);
    has Bool $.verbose = False;
 
    constant %TOGGLE = { :inc<dec>, :dec<inc>, :tgl<inc>, :jnz<cpy>, :cpy<jnz> };
 
    method compile-line($pos)
    {
        my token reg { <[abcd]> };
        my token val { '-'? \d+ }
 
        @!optimized[$pos] = '';
 
        given @!instructions[$pos] {
            when m:s/^ cpy <val> <reg> $/ {
                my $val = +$<val>;
                my $reg = ~$<reg>;
                return { %!register{$reg} = $val };
            }
            when m:s/^ cpy <reg> <reg> $/ {
                my $regFrom = ~$<reg>[0];
                my $regTo = ~$<reg>[1];
                return { %!register{$regTo} = %!register{$regFrom} };
            }
            when m:s/^ inc <reg> $/ {
                my $reg = ~$<reg>;
 
                # See if we can optimize away a loop
                if $pos < +@!instructions - 2 && @!instructions[$pos+1] ~~ m:s/^ dec <reg> $/ {
                    my $reg2 = ~$<reg>;
                    if $reg2 ne $reg && @!instructions[$pos+2] ~~ m:s/^ jnz $reg2 '-2' $/ {
                        if $pos < +@!instructions - 4 && @!instructions[$pos+3] ~~ m:s/^ dec <reg> $/ {
                            my $reg3 = ~$<reg>;
                            if $reg3 ne $reg|$reg2 && @!instructions[$pos+4] ~~ m:s/^ jnz $reg3 '-5' $/ {
                                # Multiplication
                                @!optimized[$pos] = "$reg += $reg2 * $reg3; $reg2 = $reg3 = 0";
                                return { %!register{$reg} += %!register{$reg2} * %!register{$reg3};
                                         %!register{$reg2} = 0;
                                         %!register{$reg3} = 0;
                                         $!pos = $pos+5; };
                            }
                        }
 
                        # Addition
                        @!optimized[$pos] = "$reg += $reg2; $reg2 = 0";
                        return { %!register{$reg} += %!register{$reg2};
                                 %!register{$reg2} = 0;
                                 $!pos = $pos+3; }
                    }
                }
 
                # Unoptimized increment
                return { %!register{$reg}++ };
            }
            when m:s/^ dec <reg> $/ {
                my $reg = ~$<reg>;
 
                # See if we can optimize away a loop
                if $pos < +@!instructions - 2 && @!instructions[$pos+1] ~~ m:s/^ inc <reg> $/ {
                    my $reg2 = ~$<reg>;
                    if $reg2 ne $reg && @!instructions[$pos+2] ~~ m:s/^ jnz $reg '-2' $/ {
                        if $pos < +@!instructions - 4 && @!instructions[$pos+3] ~~ m:s/^ dec <reg> $/ {
                            my $reg3 = ~$<reg>;
                            if $reg3 ne $reg|$reg2 && @!instructions[$pos+4] ~~ m:s/^ jnz $reg3 '-5' $/ {
                                # Multiplication
                                @!optimized[$pos] = "$reg2 += $reg * $reg3; $reg = $reg3 = 0";
                                return { %!register{$reg2} += %!register{$reg} * %!register{$reg3};
                                         %!register{$reg} = 0;
                                         %!register{$reg3} = 0;
                                         $!pos = $pos+5; };
                            }
                        }
 
                        # Addition
                        @!optimized[$pos] = "$reg2 += $reg; $reg = 0";
                        return { %!register{$reg2} += %!register{$reg};
                                 %!register{$reg} = 0;
                                 $!pos = $pos+3;}
                    }
                }
 
                # Unoptimized decrement
                return { %!register{$reg}-- };
            }
            when m:s/^ jnz <nonzero=val> <offset=val> $/ {
                my $nonzero = +$<nonzero>;
                my $offset = $<offset>;
                return { $!pos = $pos + $offset if $nonzero };
            }
            when m:s/^ jnz <nonzero=reg> <offset=val> $/ {
                my $nonzero = ~$<nonzero>;
                my $offset = $<offset>;
                return { $!pos = $pos + $offset if %!register{$nonzero} };
            }
            when m:s/^ jnz <nonzero=val> <offset=reg> $/ {
                my $nonzero = +$<nonzero>;
                my $offset = $<offset>;
                return { $!pos = $pos + %!register{$offset} if $nonzero };
            }
            when m:s/^ jnz <nonzero=reg> <offset=reg> $/ {
                my $nonzero = ~$<nonzero>;
                my $offset = $<offset>;
                return { $!pos = $pos + %!register{$offset} if %!register{$nonzero} };
            }
            when m:s/^ tgl <val> $/ {
                my $val = $<val>;
                return { self.toggle($pos+$val) };
            }
            when m:s/^ tgl <reg> $/ {
                my $reg = ~$<reg>;
                return { self.toggle($pos+%!register{$reg}) };
            }
            default {
                die "Invalid instruction: $_";
            }
        }
    }
 
    method compile()
    {
        say "Compiling..." if $!verbose;
 
        @!code = gather for 0..^@!instructions -> $pos {
            take self.compile-line($pos);
        }
    }
 
    method toggle(Int $pos)
    {
        return unless @!instructions[$pos];
 
        my ($instr, $args) = @!instructions[$pos].split(' ', 2);
        die "Don't know how to toggle '$instr'" unless %TOGGLE{$instr};
        @!instructions[$pos] = "%TOGGLE{$instr} $args";
 
        # We need to recompile the whole program, since the code may have been part of a
        # loop that was optimized away.
        self.compile();
    }
 
    method run()
    {
        self.compile unless @!code;
 
        while @!code[$!pos] {
            $!counter++;
            say "$!pos: (%!register<a b c d>.join(' ')) ",
                            @!optimized[$!pos] ?? "@!optimized[$!pos] [optimized]"
                                               !! @!instructions[$!pos] if $!verbose;
            @!code[$!pos++]();
        }
    }
}
 
sub MAIN(IO() $inputfile where *.f, Int :$a=12, Int :$b=0, Int :$c=0, Int :$d=0, Bool :v(:$verbose))
{
    my $computer = Computer.new(:instructions($inputfile.lines), :$verbose);
    $computer.register<a b c d> = $a, $b, $c, $d;
    $computer.run;
 
    say '' if $verbose;
    say "The state of the register: ",
         $computer.register.pairs.sort.map({ "$_.key()=$_.value()"}).join(', ');
    say "$computer.counter() instructions processed.";
}

Comments