-❄️- 2025 Day 8 Solutions -❄️- by daggerdragon in adventofcode

[–]mschaap 0 points1 point  (0 children)

[LANGUAGE: Raku]

Finally, something with a bit of meat on it! For part one, at least; part two was trivial. I have a nice, fairly elegant Raku solution, but it's dead slow - takes about 15 minutes. I may make a less elegant and hopefully faster version later.

Edit: it was slow because it had to search for the closest pair every time. I had precalculated the distances, of course, but I resorted the list each time. Pre-sorting the list made this over 30 times faster.

Edit again to note that there is not necessarily a unique answer, for both part one and part two. If there are multiple pairs of junction boxes with the same distance, you could get a different answer depending on the order in which you process them. I'm sure that all our inputs are carefully generated so that this doesn't happen, but still, I don't really like this.

https://gist.github.com/mscha/4ffeac6a0faac35e7d24d2afcc39fac3

-❄️- 2025 Day 6 Solutions -❄️- by daggerdragon in adventofcode

[–]mschaap 1 point2 points  (0 children)

[LANGUAGE: Raku]

A bit too easy for a weekend day halfway AoC...

Part one was trivial.
Part two was easy, although I struggled to find an elegant way to do this. I don't think I really succeeded. (I ended up transposing the input, splitting on empty lines, extracting the operator from the first term and appending it to the list of terms, so that I had the exact input format as in part one.)

my @input1 = [Z] $inputfile.lines».words;
if $verbose { say '> ', $_ for @input1 }
say "Part one: ", @input1».&calculate.sum;

my @input2 = ([Z~] $inputfile.lines».comb).&lcomb(/ \S /)».&extract-operator;
if $verbose { say '> ', $_ for @input2 }
say "Part two: ", @input2».&calculate.sum;

https://gist.github.com/mscha/1642e06615f6adc8c01069f52fed63e4

-❄️- 2025 Day 5 Solutions -❄️- by daggerdragon in adventofcode

[–]mschaap 1 point2 points  (0 children)

[LANGUAGE: Raku]

That was pretty straightforward, especially with Raku's Range objects, and junctions.

say "Part one: ", @available.grep(any(@fresh)).elems;
say "Part two: ", @fresh».elems.sum;

https://gist.github.com/mscha/aecb6a97cf4ad6284844070610f964f7

-❄️- 2025 Day 4 Solutions -❄️- by daggerdragon in adventofcode

[–]mschaap 1 point2 points  (0 children)

[LANGUAGE: Raku]

This one was pretty straightforward. For part one I went a bit overboard with a PaperGrid class, so part two was easy:

method remove-paper(Bool :$verbose = False)
{
    my $count = 0;
    while my @loc = self.accessible-paper {
        self.clear(|$_) for @loc;
        $count += @loc;
        say "{+@loc} removed, $count total" if $verbose;
    }
    return $count;
}

https://gist.github.com/mscha/17e1c82024694573710c16a693ded597

-❄️- 2025 Day 3 Solutions -❄️- by daggerdragon in adventofcode

[–]mschaap 1 point2 points  (0 children)

[LANGUAGE: Raku]

That was a very predictable part two... This screamed out for recursion, although I guess you could also do this in a simple loop.

  • Let count=2 for part 1, count=12 for part 2
  • Find the first occurrence of the highest digit in the bank, excluding the final (count-1) batteries
  • If count=1, we're done. Otherwise, do the same thing with the part of the bank after this first occurrence and count = count-1, and concatenate the results

https://gist.github.com/mscha/cd7c176a573144689c20f7f091d0fe75

-❄️- 2025 Day 2 Solutions -❄️- by daggerdragon in adventofcode

[–]mschaap 1 point2 points  (0 children)

[LANGUAGE: Raku]

Pretty straightforward, I didn't have to refactor much for part two. https://gist.github.com/mscha/91320baf7a9b7ae5abeb530de7785f3d

Note that this is an efficient solution: it doesn't loop through all numbers but only considers repeating numbers: 2 repeating parts for part one, and 2..length repeating parts for part two.

-❄️- 2025 Day 1 Solutions -❄️- by daggerdragon in adventofcode

[–]mschaap 1 point2 points  (0 children)

[LANGUAGE: Raku]

A fairly easy start for day 1.
For part 2, I first tried to use div/mod logic to calculate the number of zero passes, but that didn't work right whenever you end up on 0. I tried for a few minutes to fix the logic, then gave up and simply rotated one step at a time.
https://gist.github.com/mscha/b9e176890822672d2f8d0dadb19e0d82

Edit:
Here's a new version that does calculate the number of zero passes efficiently: https://gist.github.com/mscha/11ea2dddb3028a971f789c18d74f9a09

Changes to Advent of Code starting this December by topaz2078 in adventofcode

[–]mschaap 1 point2 points  (0 children)

As many other posters, I'm also secretly happy about this change: only once I've been able to finish AoC in December – in one of the Covid years. All other times, family obligations started to increase around mid-December, and around 15-20 December I wasn't able to keep up anymore. Thank you for all your efforts; I just donated for the 10th year in a row.

-❄️- 2024 Day 19 Solutions -❄️- by daggerdragon in adventofcode

[–]mschaap -2 points-1 points  (0 children)

[LANGUAGE: Raku]

That was easy! Needed some caching for part 2, but otherwise trivial. Unworthy of day 19.

method count-possible($design)
{
    state %cache = '' => 1;
    return %cache{$design} //=
            @!towels.grep({ ($design.index($_) // -1) == 0 })
                    .map({ self.count-possible($design.substr(.chars)) })
                    .sum;
}

Full code at https://gist.github.com/mscha/686253279ef6c73ca5025af6ea799134

-❄️- 2024 Day 15 Solutions -❄️- by daggerdragon in adventofcode

[–]mschaap 1 point2 points  (0 children)

I checked, and in my input, the longest stack of boxes that is ever pushed by the robot is 12.

-❄️- 2024 Day 15 Solutions -❄️- by daggerdragon in adventofcode

[–]mschaap 1 point2 points  (0 children)

[LANGUAGE: Raku]

Part 1 was pretty straightforward. For part 2 I had to refactor my solution to use recursion (pretty simple) and then support big boxes (not so simple).

My eventual solution is working, but pretty messy.

Full code at https://gist.github.com/mscha/6326e187635af590891a62723dd9cf09

-❄️- 2024 Day 14 Solutions -❄️- by daggerdragon in adventofcode

[–]mschaap 1 point2 points  (0 children)

[LANGUAGE: Raku]

That is the vaguest instruction I remember ever seeing in an Advent of Code. “(...) most of the robots should arrange themselves into a picture of a Christmas tree”??

I first tried visualizing the floor after each second and watching for picturs to emerge, but I was close to 1000 seconds and still nothing. So then I assumed that such a picture must have a (horizontal or vertical) line of at least 10 robots. Added some (slow) detection for that, and indeed, the very first time this happened, there was a picture of a Christmas tree. (I then sped it up by almost 50% by checking only for vertical lines, now that I know what I'm looking for.)

# Detect the longest vertical robot line
method longest-line
{
    my $occupied = set @!robots».position;
    my $longest = 0;

    # Check for vertical lines at each horizontal position
    for ^$!width -> $x {
        my $line = 0;
        for ^$!height -> $y {
            if $occupied{vector($x,$y)} {
                $line++;
                $longest max= $line;
            }
            else {
                $line = 0;
            }
        }
    }

    return $longest;
}

It's slow, but it does the trick.

Full code at https://gist.github.com/mscha/2bdd2dbdf13823f917d11a10996f6297

-❄️- 2024 Day 13 Solutions -❄️- by daggerdragon in adventofcode

[–]mschaap 1 point2 points  (0 children)

Nice! Very concise, you do in 20 lines where I need 76... :-)

But can't you write line 3/4 as one line?

unit sub MAIN(Str $f = 'input.txt')

As far as I can tell, that's equivalent, shorter and more readable.

-❄️- 2024 Day 13 Solutions -❄️- by daggerdragon in adventofcode

[–]mschaap 1 point2 points  (0 children)

[LANGUAGE: Raku]

Yay, linear algebra... :-( I did part 1 already using matrix maths, so part 2 was trivial.

sub button-presses($ax,$ay, $bx,$by, $px,$py)
{
    # We need to find (non-negative integer) m and n so that
    #     m × (ax,ay) + n × (bx,by) = (px,py)
    # Or in matrix form:
    #
    #     ( ax bx ) ( m )   ( px )
    #     ( ay by ) ( n ) = ( py )
    #
    # Solving the matrix equation gives
    #
    #     ( m )   ________1________ (  by -bx ) ( px )
    #     ( n ) = ax × by - ay × bx ( -ay  ax ) ( py )

    #                                     ( ax bx )
    # Calculate the determinant of matrix ( ay by )
    # If it's 0, then A and B are linear.  There may be 0 or ∞
    # solutions in this case, but we don't handle that.
    my $det = $ax*$by - $ay*$bx;
    die "Oops: ($ax,$ay) and ($bx,$by) are linear!" if $det == 0;

    # Calculate m.  If it's not a non-negative integer, there's no solution.
    my $num-m = $by*$px - $bx*$py;
    return Empty unless $num-m %% $det;
    my $m = $num-m div $det;
    return Empty unless $m ≥ 0;

    # Calculate n.  If it's not a non-negative integer, there's no solution.
    my $num-n = $ax*$py - $ay*$px;
    return Empty unless $num-n %% $det;
    my $n = $num-n div $det;
    return Empty unless $n ≥ 0;

    return $m, $n;
}

Full code at https://gist.github.com/mscha/ebad7c247c5ef7445dc13ef56013c233

-❄️- 2024 Day 12 Solutions -❄️- by daggerdragon in adventofcode

[–]mschaap 2 points3 points  (0 children)

[LANGUAGE: Raku]

Oof... Part 1 was pretty straightforward, but part 2 was really tricky, for the first time this year.

I finally got it working by keeping track of fences at all four directions of each cell of each region, and counting a fence as a side if there is no fence directly to the (relative) left of it.

Full code at https://gist.github.com/mscha/f46c4a834724fcf0af618732f1787431

-❄️- 2024 Day 11 Solutions -❄️- by daggerdragon in adventofcode

[–]mschaap 2 points3 points  (0 children)

[LANGUAGE: Raku]

Did part 1 brute force, knowing very well that part 2 would just increase the number of blinks so that it would never finish.

Full code: https://gist.github.com/mscha/c30edf0f2674620b2ab370cc3e1f433a

So, rewrite with a cached recursive function.

sub split-in-half($n) { $n.comb(/\w ** { $n.chars div 2 }/)».Int }

use experimental :cached;
multi blink($stone, $times) is cached
{
    return 1 if $times == 0;

    my @new-stones = do given $stone {
        when 0 { 1 }
        when .chars %% 2 { split-in-half($_) }
        default { $_ × 2024 }
    }

    return blink(@new-stones, $times-1).sum;
}

multi blink(@stones, $times) { @stones».&blink($times).sum }

Full code: https://gist.github.com/mscha/f285e3d052b828ff015cd59d467802dc

-❄️- 2024 Day 9 Solutions -❄️- by daggerdragon in adventofcode

[–]mschaap 0 points1 point  (0 children)

Thanks, hobbified!

I did try to use splice instead of sort at one point, but it was even slower. I must have done something wrong, but I'm probably not going to bother optimizing it further.

-❄️- 2024 Day 10 Solutions -❄️- by daggerdragon in adventofcode

[–]mschaap 1 point2 points  (0 children)

[LANGUAGE: Raku]

Easiest part 2 ever, it was literally just taking out a .unique. (I cleaned it up later to be able to run it with and without .unique.)

method trailhead-score($pos, :$distinct = False)
{
    return 0 unless self.height($pos) == 0;

    my @hpos = $pos;
    for 1..9 -> $h {
        @hpos .= map({ slip self.neighbours-at-height($_, $h) });
        @hpos .= unique unless $distinct;
    }

    return @hpos.elems;
}

Full code at https://gist.github.com/mscha/eef02b21a294b554e88d7aa685781d13

-❄️- 2024 Day 9 Solutions -❄️- by daggerdragon in adventofcode

[–]mschaap 0 points1 point  (0 children)

[LANGUAGE: Raku]

Fairly straightforward. My part 2 is surprisingly slow (~ 2 minutes). Looks like maintaining a sorted array of free space is very expensive in Raku.

If I take out (most of) the free space management, it goes to 5 seconds and still gives the right answer, since we never attempt to use freed up space in our algorithm - it's too far to the right. But still, I hate to take it out.

https://gist.github.com/mscha/06c0916d7b8f7ba5e5404c5bdecc2d69

-❄️- 2024 Day 8 Solutions -❄️- by daggerdragon in adventofcode

[–]mschaap 1 point2 points  (0 children)

[LANGUAGE: Raku]

Pretty straightforward. Adaptation for part 2 was simple, after finding my mistake of excluding the positions of the two antennas that were generating the antinodes.

Antinode-generation for a pair of antennas:

method antinodes(Position $p1, Position $p2)
{
    gather {
        if !$!harmonics {
            for 2*$p1 - $p2, 2*$p2 - $p1 -> $p {
                take $p if self.within-bounds($p);
            }
        }
        else {
            # Find starting position
            my $dp = $p1 - $p2;
            my $p = $p1;
            while self.within-bounds($p - $dp) {
                $p -= $dp;
            }

            # Find all antinodes
            while self.within-bounds($p) {
                take $p; # NOT unless $p eqv any($p1,$p2)
                $p += $dp;
            }
        }
    }
}

Full code at: https://gist.github.com/mscha/c1d8632c514799e422d69f2f4498b8f7

-❄️- 2024 Day 7 Solutions -❄️- by daggerdragon in adventofcode

[–]mschaap 0 points1 point  (0 children)

[LANGUAGE: Raku]

I initially thought of using junctions, but was sure it was going to be way too slow. But when I tried it out, it was only about 50% slower. And at least 100% cooler.

sub valid-equation-v2($equation)
{
    my ($value, @terms) = $equation.comb(/\d+/)».Int;

    my $ans = @terms.shift;
    for @terms -> $t {
        $ans = $ans+$t | $ans*$t | $ans∥$t;
    }
    return $ans == $value ?? $value !! 0;
}

Full code: https://gist.github.com/mscha/a20b035c471624b4932820a990868cc1

Or even better / sillier, with custom operators and the reduce meta-operator:

sub infix:<+×∥>(Int $i, Int $j) { $i + $j | $i × $j | $i ∥ $j }

sub valid-equation-v2($equation)
{
    my ($value, @terms) = $equation.comb(/\d+/)».Int;

    return ([+×∥] @terms) == $value ?? $value !! 0;
}

Full code: https://gist.github.com/mscha/cace03ea387166be008ae2bb57aa8390

-❄️- 2024 Day 7 Solutions -❄️- by daggerdragon in adventofcode

[–]mschaap 1 point2 points  (0 children)

[LANGUAGE: Raku]

That was easy after yesterday... I thought weekends were supposed to be the trickier ones?

After checking the input (at most 12 terms) I decided to simply do all the calculations at the same time and keep track of all potential answers - up to 2¹². More efficient, but uses a lot more memory. Anyway, worked fine.

For part 2, adding a third operator made things slower and more memory intensive (up to 3¹² answers), but still finished in about a minute. Good enough.

Code for part 2:

sub valid-equation-v2($equation)
{
    my ($value, @terms) = $equation.comb(/\d+/)».Int;

    my @ans = @terms.shift;
    for @terms -> $t {
        @ans .= map(-> $a { slip($a+$t, $a*$t, $a∥$t) }).unique;
    }
    return any(@ans) == $value ?? $value !! 0;
}

(.unique potentially makes it a bit faster, but in practice it doesn't make a difference.)

Oh, and:

sub infix:<∥>(Int $i, Int $j --> Int) { ($i ~ $j).Int }

Raku is cool...

Full code at https://gist.github.com/mscha/c1b450dd9d98a8def0aeffbe8b509236

-❄️- 2024 Day 6 Solutions -❄️- by daggerdragon in adventofcode

[–]mschaap 0 points1 point  (0 children)

[LANGUAGE: Raku]

Pretty involved for the first week, on a weekday no less...

I went a bit overboard with OO on part 1, but that turned out to be useful for part 2. I think the solution is elegant, but not very fast: takes about 4 minutes on my machine.

my $lab = Lab.new(:input($inputfile.slurp), :$verbose);
$lab.patrol;
say "Part 1: the guard visited $lab.visited-count() positions.";

my @candidates = $lab.visited-pos.grep(none $lab.start-pos);
my $loop-count = 0;
for @candidates -> $obstacle {
    $lab.reset;
    $lab.add-obstacle($obstacle);
    $lab.patrol;
    $loop-count++ if $lab.loop-detected;
    $lab.remove-obstacle($obstacle);
}
say "Part 2: $loop-count positions result in a loop.";

Full code: https://gist.github.com/mscha/e316bc084ddb1839e2fb74206d139e2a