summaryrefslogtreecommitdiffstats
path: root/2020/day-08/day-08.raku
blob: ee9556f81dfff088fb02b609078eadcbfb66e09b (plain) (blame)
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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#!/usr/bin/env raku

# Instruction grammar seperates the operation & argument.
grammar Instruction {
    rule TOP { <operation> <argument> }
    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<instruction>) -> $match {
                @instructions[$idx]<instruction> = "nop $match<argument>"
                if $match<operation> eq "jmp";

                @instructions[$idx]<instruction> = "jmp $match<argument>"
                if $match<operation> eq "nop";

                $acc = execute-from(@instructions, 1);

                @instructions[$idx]<instruction> = "$match<operation> $match<argument>";
                @instructions = reset-executed(@instructions);

                last if $repeated == 0;
            }
        }
    }
    say "Part $part: ", $acc;
}

sub reset-executed (
    @instructions is copy --> List
) {
    $_<executed> = 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<executed>;
    return $acc if $entry<executed>;

    if Instruction.parse($entry<instruction>) -> $match {
        $entry<executed> = 1;
        given $match<operation> {
            when 'acc' {
                $acc += $match<argument>;
                $acc += execute-from(@instructions, $idx + 1);
            }
            when 'jmp' {
                $acc += execute-from(@instructions, $idx + $match<argument>);
            }
            when 'nop' {
                $acc += execute-from(@instructions, $idx + 1);
            }
        }
    }
    return $acc;
}