#!/usr/bin/env raku # Instruction grammar seperates the operation & argument. grammar Instruction { rule TOP { } token operation { acc|jmp|nop } token argument { .* } } my Int $repeated = 0; sub MAIN ( Int $part where * == 1|2 = 1 #= part to run (1 or 2) ) { # To match the numbers in instructions with array we just push an # element at 0th index. my @instructions = %(instruction => "", executed => 0),; for "input".IO.lines { push @instructions, %( instruction => $_.Str, executed => 0, ); } my Int $acc; if $part == 1 { $acc = execute-from(@instructions, 1) if $part == 1; } elsif $part == 2 { for @instructions.kv -> $idx, $entry { next if $idx == 0; $repeated = 0; if Instruction.parse($entry) -> $match { @instructions[$idx] = "nop $match" if $match eq "jmp"; @instructions[$idx] = "jmp $match" if $match eq "nop"; $acc = execute-from(@instructions, 1); @instructions[$idx] = "$match $match"; @instructions = reset-executed(@instructions); last if $repeated == 0; } } } say "Part $part: ", $acc; } sub reset-executed ( @instructions is copy --> List ) { $_ = 0 for @instructions; return @instructions; } # execute-from takes an index & executes instructions from that point. # It returns the accumulator value. sub execute-from ( @instructions, Int $idx --> Int ) { my Int $acc = 0; return $acc unless @instructions[$idx]; my $entry = @instructions[$idx]; $repeated++ if $entry; return $acc if $entry; if Instruction.parse($entry) -> $match { $entry = 1; given $match { when 'acc' { $acc += $match; $acc += execute-from(@instructions, $idx + 1); } when 'jmp' { $acc += execute-from(@instructions, $idx + $match); } when 'nop' { $acc += execute-from(@instructions, $idx + 1); } } } return $acc; }