• 0 Posts
  • 8 Comments
Joined 2 years ago
cake
Cake day: June 10th, 2023

help-circle
  • Raku

    My actual solution ran in about 2.5 seconds, but I optimized it to run in about 1 second.

    sub MAIN($input) {
        my $file = open $input;
        my @connection-list := $file.slurp.trim.lines>>.split("-")>>.List.List;
    
        my %connections;
        my %all-computers is SetHash;
        for @connection-list -> @c {
            my ($first, $second) = @c.sort;
            %connections{$first} = [] if %connections{$first}:!exists;
            %connections{$second} = [] if %connections{$second}:!exists;
            %connections{$first}.push($second);
            %all-computers{@c.all}++;
        }
        for %connections.values -> $list is rw {
            $list = $list.sort.List;
        }
    
        my $part1-solution = 0;
        for %connections.keys -> $c1 {
            for %connections{$c1}.Seq -> $c2 {
                for (%connections{$c1} ∩ %connections{$c2}).keys -> $c3 {
                    next unless ($c1, $c2, $c3).any.substr(0,1) eq "t";
                    $part1-solution++;
                }
            }
        }
        say "part1 solution: $part1-solution";
    
        my $part2-solution = find-max-party((), %connections, %all-computers).join(",");
        say "part2 solution: $part2-solution";
    }
    
    sub find-max-party(@party, %connections, %available-members) {
        my @max-party = @party;
        for %available-members.keys.sort -> $c1 {
            my @new-party := (|@party, $c1);
            my %new-available-members := %available-members ∩ %connections{$c1};
            my @max-party-candidate = find-max-party(@new-party, %connections, %new-available-members);
            @max-party = @max-party-candidate if @max-party-candidate.elems > @max-party.elems;
            last if @max-party.elems == @party.elems + %available-members.elems;
        }
        return @max-party;
    }
    

  • Raku

    I spent way to much time tweaking the part 2 code to get a working solution. The solution itself is quite simple, though.

    sub MAIN($input) {
        grammar Input {
            token TOP { <register-a> "\n" <register-b> "\n" <register-c> "\n\n" <program> "\n"* }
            token register-a { "Register A: " (\d+) }
            token register-b { "Register B: " (\d+) }
            token register-c { "Register C: " (\d+) }
            token program { "Program: " (\d)+%"," }
        }
        my $parsed = Input.parsefile($input);
        my $register-a = $parsed<register-a>[0].Int;
        my $register-b = $parsed<register-b>[0].Int;
        my $register-c = $parsed<register-c>[0].Int;
        my @program = $parsed<program>[0]>>.Int;
    
        my $part1-solution = run-program($register-a, $register-b, $register-c, @program).join(",");
        say "part1 solution: $part1-solution";
    
        my $part2-solution = search-for-quine(0, $register-b, $register-c, @program, 0);
        say "part2-solution: $part2-solution";
    }
    
    sub run-program($a, $b, $c, @program) {
        my ($register-a, $register-b, $register-c) Z= ($a, $b, $c);
        my $pc = 0;
        my @output;
        while $pc < @program.elems {
            my ($opcode, $operand) Z= @program[$pc, $pc+1];
            my $combo = (given $operand {
                when 0..3 { $operand }
                when 4 { $register-a }
                when 5 { $register-b }
                when 6 { $register-c }
                when 7 { Nil }
                default { say "invalid operand $operand"; exit; }
            });
            given $opcode {
                when 0 { $register-a = $register-a div (2 ** $combo); }
                when 1 { $register-b = $register-b +^ $operand; }
                when 2 { $register-b = $combo mod 8; }
                when 3 { $pc = $operand - 2 if $register-a != 0; }
                when 4 { $register-b = $register-b +^ $register-c; }
                when 5 { @output.push($combo mod 8); }
                when 6 { $register-b = $register-a div (2 ** $combo); }
                when 7 { $register-c = $register-a div (2 ** $combo); }
                default { say "invalid opcode $opcode"; exit; }
            }
            $pc += 2;
        }
        return @output;
    }
    
    sub search-for-quine($a, $b, $c, @program, $idx) {
        return $a if $idx == @program.elems;
        for ^8 {
            my $test-solution = $a * 8 + $_;
            my @output = run-program($test-solution, $b, $c, @program);
            my @program-slice = @program[*-1-$idx..*];
            if @program-slice eqv @output {
                my $found = search-for-quine($test-solution, $b, $c, @program, $idx+1);
                if $found {
                    return $found;
                }
            }
        }
        # Time to back track...
        return False;
    }
    

  • Raku

    Pretty straight-forward problem today.

    sub MAIN($input) {
        my $file = open $input;
        my @map = $file.slurp.trim.lines>>.comb>>.Int;
    
        my @pos-tracking = [] xx 10;
        for 0..^@map.elems X 0..^@map[0].elems -> ($row, $col) {
            @pos-tracking[@map[$row][$col]].push(($row, $col).List);
        }
    
        my %on-possible-trail is default([]);
        my %trail-score-part2 is default(0);
        for 0..^@pos-tracking.elems -> $height {
            for @pos-tracking[$height].List -> ($row, $col) {
                if $height == 0 {
                    %on-possible-trail{"$row;$col"} = set ("$row;$col",);
                    %trail-score-part2{"$row;$col"} = 1;
                } else {
                    for ((1,0), (-1, 0), (0, 1), (0, -1)) -> @neighbor-direction {
                        my @neighbor-position = ($row, $col) Z+ @neighbor-direction;
                        next if @neighbor-position.any < 0 or (@neighbor-position Z>= (@map.elems, @map[0].elems)).any;
                        next if @map[@neighbor-position[0]][@neighbor-position[1]] != $height - 1;
                        %on-possible-trail{"$row;$col"} ∪= %on-possible-trail{"{@neighbor-position[0]};{@neighbor-position[1]}"};
                        %trail-score-part2{"$row;$col"} += %trail-score-part2{"{@neighbor-position[0]};{@neighbor-position[1]}"};
                    }
                }
            }
        }
    
        my $part1-solution = @pos-tracking[9].map({%on-possible-trail{"{$_[0]};{$_[1]}"}.elems}).sum;
        say "part 1: $part1-solution";
    
        my $part2-solution = @pos-tracking[9].map({%trail-score-part2{"{$_[0]};{$_[1]}"}}).sum;
        say "part 2: $part2-solution";
    }
    

  • Raku

    Solution
    sub MAIN($input) {
        my $file = open $input;
        my @map = $file.slurp.lines>>.comb>>.List.List;
        my %freqs;
        for 0..^@map.elems -> $row {
            for 0..^@map[0].elems -> $col {
                if @map[$row; $col] ne "." {
                    my $freq = @map[$row; $col];
                    %freqs{$freq} = [] if %freqs{$freq}:!exists;
                    %freqs{$freq}.push(($row, $col));
                }
            }
        }
        my %antinodes is SetHash;
        for %freqs.kv -> $freq, @locations {
            for (0..^@locations.elems) X (0..^@locations.elems) -> ($loc1, $loc2) {
                next if $loc1 == $loc2;
                my @base = @locations[$loc1].List;
                my @vector = @locations[$loc2].List Z- @base;
                my @antinode1 = @base Z+ @vector.map(* * 2);
                %antinodes{@antinode1.List.raku}++ if point-is-in-map(@map, @antinode1);
                my @antinode2 = @base Z+ @vector.map(* * -1);
                %antinodes{@antinode2.List.raku}++ if point-is-in-map(@map, @antinode2);
            }
        }
        my $part1-solution = %antinodes.elems;
        say "part 1: $part1-solution";
    
    
        my %antinodes2 is SetHash;
        for %freqs.kv -> $freq, @locations {
            for (0..^@locations.elems) X (0..^@locations.elems) -> ($loc1, $loc2) {
                next if $loc1 == $loc2;
                my @base = @locations[$loc1].List;
                my @vector = @locations[$loc2].List Z- @base;
                # make integer unit-ish vector
                for 2..@vector[0] -> $divisor {
                    if @vector[0] %% $divisor and @vector[1] %% $divisor {
                        @vector[0] = @vector[0] div $divisor;
                        @vector[1] = @vector[1] div $divisor;
                    }
                }
                for 0..max(@map.elems, @map[0].elems) -> $length {
                    my @antinode = @base Z+ @vector.map(* * $length);
                    if point-is-in-map(@map, @antinode) {
                        %antinodes2{@antinode.List.raku}++ 
                    } else {
                        last
                    }
                }
                for 1..max(@map.elems, @map[0].elems) -> $length {
                    my @antinode = @base Z+ @vector.map(* * -$length);
                    if point-is-in-map(@map, @antinode) {
                        %antinodes2{@antinode.List.raku}++ 
                    } else {
                        last
                    }
                }
            }
        }
        my $part2-solution = %antinodes2.elems;
        say "part 2: $part2-solution";
    }
    
    sub point-is-in-map(@map, @point) {
        return False if !(0 <= @point[0] < @map.elems);
        return False if !(0 <= @point[1] < @map[0].elems);
        return True;
    }
    

  • Raku

    Oof, my struggle to make custom index walking paths for part 1 did not pay off for part 2.

    Solution
    sub MAIN($input) {
        my $file = (open $input).slurp;
        my @grid is List = $file.lines».comb».list;
        my @transposedGrid is List = [Z] @grid;
        my @reversedGrid is List = @grid».reverse;
        my @transposedReversedGrid is List = @transposedGrid».reverse;
    
        my @horizontalScanRows is List = generateScanHorizontal(@grid);
        my @transposedHorizontalScanRows is List = generateScanHorizontal(@transposedGrid);
    
        my @part-one-counts = [];
        @part-one-counts.push(count-xmas(@grid, @horizontalScanRows)); # Right
        @part-one-counts.push(count-xmas(@transposedGrid, @transposedHorizontalScanRows)); # Down
        @part-one-counts.push(count-xmas(@reversedGrid, @horizontalScanRows)); # Left
        @part-one-counts.push(count-xmas(@transposedReversedGrid, @transposedHorizontalScanRows)); # Up
    
        my @diagonalScanRows is List = generateScanDiagonal(@grid);
        my @transposedDiagonalScanRows is List = generateScanDiagonal(@transposedGrid);
        @part-one-counts.push(count-xmas(@grid, @diagonalScanRows)); # Down Right
        @part-one-counts.push(count-xmas(@grid, @diagonalScanRows».reverse)); # Up Left
        @part-one-counts.push(count-xmas(@reversedGrid, @diagonalScanRows)); # Down Left
        @part-one-counts.push(count-xmas(@reversedGrid, @diagonalScanRows».reverse)); # Up Right
    
        my $part-one-solution = @part-one-counts.sum;
        say "part 1: $part-one-solution";
    
    
        my @part-two-counts = [];
        @part-two-counts.push(countGridMatches(@grid, (<M . S>,<. A .>,<M . S>)));
        @part-two-counts.push(countGridMatches(@grid, (<S . S>,<. A .>,<M . M>)));
        @part-two-counts.push(countGridMatches(@grid, (<S . M>,<. A .>,<S . M>)));
        @part-two-counts.push(countGridMatches(@grid, (<M . M>,<. A .>,<S . S>)));
    
        my $part-two-solution = @part-two-counts.sum;
        say "part 2: $part-two-solution";
    
    }
    
    sub count-xmas(@grid, @scanRows) {
        my $xmas-count = 0;
        for @scanRows -> @scanRow {
            my $xmas-pos = 0;
            for @scanRow -> @pos {
                my $char = @grid[@pos[0]][@pos[1]];
                if "X" eq $char {
                    $xmas-pos = 1;
                }elsif <X M A S>[$xmas-pos] eq $char {
                    if $xmas-pos == 3 {
                        $xmas-pos = 0;
                        $xmas-count += 1;
                    } else {
                        $xmas-pos += 1;
                    }
                } else {
                    $xmas-pos = 0;
                }
            }
        }
        return $xmas-count;
    }
    
    sub generateScanHorizontal(@grid) {
        # Horizontal
        my $rows = @grid.elems;
        my $cols = @grid[0].elems;
        my @scanRows = ();
        for 0..^$rows -> $row {
            my @scanRow = ();
            for 0..^$cols -> $col {
                @scanRow.push(($row, $col));
            }
            @scanRows.push(@scanRow);
        }
        return @scanRows.List».List;
    }
    
    sub generateScanDiagonal(@grid) {
        # Down-right diagonal
        my $rows = @grid.elems;
        my $cols = @grid[0].elems;
        my @scanRows = ();
        for 0..^($rows + $cols - 1) -> $diag {
            my @scanRow = ();
            my $starting-row = max(-$cols + $diag + 1, 0);
            my $starting-col = max($rows - $diag - 1, 0);
            my $diag-len = min($rows - $starting-row, $cols - $starting-col);
            for 0..^$diag-len -> $diag-pos {
                @scanRow.push(($starting-row + $diag-pos, $starting-col + $diag-pos));
            }
            @scanRows.push(@scanRow);
        }
        return @scanRows.List».List;
    }
    
    sub countGridMatches(@grid, @needle) {
        my $count = 0;
        for 0..(@grid.elems - @needle.elems) -> $top {
            TOP-LEFT:
            for 0..(@grid[$top].elems - @needle[0].elems) -> $left {
                for 0..^@needle.elems -> $row-offset {
                    for 0..^@needle[$row-offset].elems -> $col-offset {
                        my $needle-char = @needle[$row-offset][$col-offset];
                        next if $needle-char eq ".";
                        next TOP-LEFT if $needle-char ne @grid[$top+$row-offset][$left+$col-offset];
                    }
                }
                $count += 1;
            }
        }
        return $count;
    }
    

    github


  • Raku

    sub MAIN($input) {
        grammar Muls {
            token TOP { .*? <mul>+%.*? .* }
            token mul { "mul(" <number> "," <number> ")" }
            token number { \d+ }
        }
    
        my $parsedMuls = Muls.parsefile($input);
        my @muls = $parsedMuls<mul>.map({.<number>».Int});
        my $part-one-solution = @muls.map({[*] $_.List}).sum;
        say "part 1: $part-one-solution";
    
        grammar EnabledMuls {
            token TOP { .*? [<.disabled> || <mul>]+%.*? .* }
            token mul { "mul(" <number> "," <number> ")" }
            token number { \d+ }
            token disabled { "don't()" .*? ["do()" || $] }
        }
    
        my $parsedEnabledMuls = EnabledMuls.parsefile($input);
        my @enabledMuls = $parsedEnabledMuls<mul>.map({.<number>».Int});
        my $part-two-solution = @enabledMuls.map({[*] $_.List}).sum;
        say "part 2: $part-two-solution";
    }
    

    github



  • Raku

    I’m trying warm up to Raku again.

    Solution

    github

    use v6;
    
    sub MAIN($input) {
        my $file = open $input;
    
        grammar LocationList {
            token TOP { <row>+%"\n" "\n"* }
            token row { <left=.id> " "+ <right=.id> }
            token id { \d+ }
        }
    
        my $locations = LocationList.parse($file.slurp);
        my @rows = $locations<row>.map({ (.<left>.Int, .<right>.Int)});
        my $part-one-solution = (@rows[*;0].sort Z- @rows[*;1].sort)».abs.sum;
        say "part 1: $part-one-solution";
    
        my $rbag = bag(@rows[*;1].sort);
        my $part-two-solution = @rows[*;0].map({ $_ * $rbag{$_}}).sum;
        say "part 2: $part-two-solution";
    }
    

    I’m happy to see that Lemmy no longer eats Raku code.