#!/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, :tgl, :jnz, :cpy }; method compile-line($pos) { my token reg { <[abcd]> }; my token val { '-'? \d+ } @!optimized[$pos] = ''; given @!instructions[$pos] { when m:s/^ cpy $/ { my $val = +$; my $reg = ~$; return { %!register{$reg} = $val }; } when m:s/^ cpy $/ { my $regFrom = ~$[0]; my $regTo = ~$[1]; return { %!register{$regTo} = %!register{$regFrom} }; } when m:s/^ inc $/ { my $reg = ~$; # See if we can optimize away a loop if $pos < +@!instructions - 2 && @!instructions[$pos+1] ~~ m:s/^ dec $/ { my $reg2 = ~$; if $reg2 ne $reg && @!instructions[$pos+2] ~~ m:s/^ jnz $reg2 '-2' $/ { if $pos < +@!instructions - 4 && @!instructions[$pos+3] ~~ m:s/^ dec $/ { my $reg3 = ~$; 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 $/ { my $reg = ~$; # See if we can optimize away a loop if $pos < +@!instructions - 2 && @!instructions[$pos+1] ~~ m:s/^ inc $/ { my $reg2 = ~$; if $reg2 ne $reg && @!instructions[$pos+2] ~~ m:s/^ jnz $reg '-2' $/ { if $pos < +@!instructions - 4 && @!instructions[$pos+3] ~~ m:s/^ dec $/ { my $reg3 = ~$; 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 $/ { my $nonzero = +$; my $offset = $; return { $!pos = $pos + $offset if $nonzero }; } when m:s/^ jnz $/ { my $nonzero = ~$; my $offset = $; return { $!pos = $pos + $offset if %!register{$nonzero} }; } when m:s/^ jnz $/ { my $nonzero = +$; my $offset = $; return { $!pos = $pos + %!register{$offset} if $nonzero }; } when m:s/^ jnz $/ { my $nonzero = ~$; my $offset = $; return { $!pos = $pos + %!register{$offset} if %!register{$nonzero} }; } when m:s/^ tgl $/ { my $val = $; return { self.toggle($pos+$val) }; } when m:s/^ tgl $/ { my $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.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; $computer.run; say '' if $verbose; say "The state of the register: ", $computer.register.pairs.sort.map({ "$_.key()=$_.value()"}).join(', '); say "$computer.counter() instructions processed."; }