summary refs log blame commit diff stats
path: root/2020/day-11/day-11.partial.raku
blob: 2f5e48e188980e6f74eadfdd3052ca46313b31b1 (plain) (tree)





































































































                                                                      
#!/usr/bin/env raku

sub MAIN (
    Int $part where * == 1|2 = 1 #= part to run (1 or 2)
) {
    my @input = "input".IO.lines;
    my Int $x-max = @input[0].chars - 1;
    my Int $row-length = $x-max + 1;

    my @seats = @input.join.comb;
    my $max-seats = @seats.end;

    my @directions = (+$row-length, -$row-length); # down, up

    # @non-left-corner contains directions that left corner can't
    # follow. It should only be followed by non-left corner seats.
    my @non-left-corner = (
        -$row-length - 1, # up-left
        -1, # left
        +$row-length - 1, # down-left
    );

    # @non-right-corner contains directions that right corner can't
    # follow. It should only be followed by non-right corner seats.
    my @non-right-corner = (
        -$row-length + 1, # up-right
        +1, # right
        +$row-length + 1, # down-right
    );

    my Int $round = 0;
    loop {
        my @changed;
        my Bool $change = False;

        INNER: for @seats.kv -> $idx, $seat {
            @changed[$idx] = $seat;
            given $seat {
                when '.' { next INNER; }
                when 'L' {
                    if adjacent-occupied($idx, 1) == 0 {
                        @changed[$idx] = '#';
                        $change = True;
                    }
                }
                when '#' {
                    if adjacent-occupied($idx, 1) >= 4 {
                        @changed[$idx] = 'L';
                        $change = True;
                    }
                }
            }
        }
        $round++;
        print "Round $round.\r";

        last unless $change;

        for @changed.kv -> $idx, $changed_seat {
            @seats[$idx] = $changed_seat;
        }
    }

    my Int $occupied = @seats.comb('#').elems;
    say "Part $part: ", $occupied;

    # adjacent-occupied returns the number of adjacent cells that have
    # been occupied by others. $visibility_range should be 1 if only
    # directly adjacent seats are to be counted. Make it -1 for
    # infinite visibility. It ignores floors ('.').
    sub adjacent-occupied (
        Int $idx, Int $visibility_range --> Int
    ) {
        my Int $occupied = 0;

        for @directions -> $direction {
            with @seats[$idx + $direction] {
                $occupied++ if $_ eq '#';
            }
        }

        # Elements in right corner can't follow @non-right-corner.
        unless ($idx + 1) % 10 == 0 {
            for @non-right-corner -> $direction {
                with @seats[$idx + $direction] {
                    $occupied++ if $_ eq '#';
                }
            }
        }

        # Elements in left corner can't follow @non-left-corner.
        unless $idx % 10 == 0 {
            for @non-left-corner -> $direction {
                with @seats[$idx + $direction] {
                    $occupied++ if $_ eq '#';
                }
            }
        }

        return $occupied;
    }
}