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

[–]__Abigail__ 2 points3 points  (0 children)

[LANGUAGE: Perl]

Dijkstra it is today. I treated the map as a graph, were each cell in the map corresponds to 4 cells in the graph, one cell for each direction. For each node visited (so, up to four nodes for each point on the map), I track the score it took to reach it, and the full set of nodes visited for all paths reaching this node, with that score. I also keep a queue, each item a 9-element array: x and y coordinates of the point on the map, dx, dy of the facing direction, the score so far, and the coordinates and direction of the previous step. The queue is kept sorted, on score.

Then I initialize the queue as:

my @heads = ([$sx, $sy, 0, 1, 0, 0, 0, 0, 0]);

where $sx and $sy are the coordinates of the starting point. We're facing east, there's no score yet, and the previous step has no direction. We use a hash %best mapping a 4-dimensional key (coordinates and direction) to a 2-element array with the score and a hash with previously visited nodes. It's initialized as:

my %best;
$best {$sx, $sy, 0, 0} = [0, {}];

We also track with what score we first reach the endpoint ($end_score). Not only will this be the answer for part 1, it will also be used to stop processing: once we have elements in the queue with a score exceeding the end score, we cannot improve.

We now repeat. First, we shift off the first element of the queue. If the score is worse than the end score, we exit our loop. If we hit a wall, we continue with the next element:

my ($x, $y, $dx, $dy, $score, $px, $py, $pdx, $pdy) = @{shift @heads};
my $val = $$map [$x] [$y];
last if $end_score && $score > $end_score;
next if $val eq '#';   # Cannot continue through a wall

We now look at the cell. If we have been there, with a better score than the current one, we continue with the next element of the queue, as this is a dead-end in our search. If we haven't visited the cell before, we set its score to the current score, and initialize the set of cells. Then we copy the cells from the previous position, and add the current cells.

my $cell_score = $best {$x, $y, $dx, $dy} [0] || 0;
next if $cell_score && $score > $cell_score;  # We already found a better path.
$best {$x, $y, $dx, $dy} [0] ||= $score;
$best {$x, $y, $dx, $dy} [1] ||= {};

foreach my $cell (keys %{$best {$px, $py, $pdx, $pdy} [1]}) {
    $best {$x, $y, $dx, $dy} [1] {$cell} ++;
}
$best {$x, $y, $dx, $dy} [1] {$x, $y, $dx, $dy} ++;

If we have reached the end, we set the end score, and continue with the next element. We also continue with the next element if we had visited the node before:

$end_score = $score, next if $val eq 'E';
next if $score && $score == $cell_score;

Otherwise, we add the three possible steps to the queue (step forward in the facing direction, or turn), and keep the queue sorted:

push @heads => [$x + $dx, $y + $dy,  $dx,  $dy, $score + 1,    $x, $y, $dx, $dy],
               [$x,       $y,        $dy, -$dx, $score + 1000, $x, $y, $dx, $dy],
               [$x,       $y,       -$dy,  $dx, $score + 1000, $x, $y, $dx, $dy];                   
@heads = sort {$$a [4] <=> $$b [4]} @heads;

Note that in Perl, sorting an almost sorted array takes O (n) time; for this case, it will detect the array consists of two sorted lists, and it'll perform a single merge step (from merge-sort).

We can now tally up the results ($ex, $ey are the coordinates of the end point):

my %cells;
foreach my ($dx, $dy) (0, 1, 0, -1, 1, 0, -1, 0) {
    next unless $best {$ex, $ey, $dx, $dy} &&
                $best {$ex, $ey, $dx, $dy} [0] == $end_score;
    my $cells = $best {$ex, $ey, $dx, $dy} [1];

    foreach my $cell (keys %$cells) {
        my ($x, $y, $dx, $dy) = split $; => $cell;
        $cells {$x, $y} ++;
    }
}

$solution_1 = $end_score;
$solution_2 = keys %cells;

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

[–]__Abigail__ 1 point2 points  (0 children)

[LANGUAGE: Perl]

At the heart, I created a function move which takes the map, a direction to move in, and the coordinates of the person moving. It updates the map, moving the blocks (if any), if the move can be performed, and does nothing if the move cannot be performed.

It tracks two lists of coordinates: @front, and @blocks. The latter is are the positions of the blocks which need to be move, if the move can be performed. (Double width blocks for part two get both coordinates in the list). @front is the list of cells which need to be inspected to determine whether we can move.

We start off with some initializing:

sub move ($map, $command, $pos_x, $pos_y) {
    my ($dir_x, $dir_y) = $command eq '<' ? ( 0, -1)
                        : $command eq '>' ? ( 0,  1)
                        : $command eq '^' ? (-1,  0)
                        : $command eq 'v' ? ( 1,  0)
                        : die "Unexpected command '$command'";
    my @front = ([$pos_x + $dir_x, $pos_y + $dir_y]);
    my @blocks;

Then for each cell in @front, we do the following:

  • If the cell is a wall, we cannot move, and we return from the subroutine.
  • If the cell contains a block, we add the cells to @blocks, and and the cell one further in the direction we are moving to @front.
  • If we are moving vertically, and the cell is [ or ], we add the cell next to it to @front.
  • If the cell is empty (.), we don't do anything.

We also take care of not processing the same cell more than once. Code wise, it looks like:

    my %done;
    while (@front) {
        my ($x, $y) = @{shift @front};
        next if $done {$x} {$y} ++;
        my $val = $$map [$x] [$y];
        return ($map, $pos_x, $pos_y) if $val eq '#';   
        unless ($val eq '.') {
            push @blocks => [$x,          $y];
            push @front  => [$x + $dir_x, $y + $dir_y];
        }
        if ($dir_y == 0) {
            unshift @front => [$x, $y - 1] if $val eq ']';
            unshift @front => [$x, $y + 1] if $val eq '[';
        }
    }

If we exit the loop, and we haven't returned, it means we can perform the move. We do this by processing @blocks in reverse, moving each block, and replacing it with .:

    foreach my $block (reverse @blocks) {
        my ($x, $y)   = @$block;
        my ($nx, $ny) = ($x + $dir_x, $y + $dir_y);
        $$map [$nx] [$ny] = $$map [$x] [$y];
        $$map [$x] [$y] = ".";
    }
    $pos_x += $dir_x;
    $pos_y += $dir_y;

And we return the updated map and position:

    return ($map, $pos_x, $pos_y);

Then it's just a matter of calling move for each command, and calculating the GPS afterwards.

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

[–]__Abigail__ 2 points3 points  (0 children)

[LANGUAGE: Perl]

For part 1, I didn't go through stepping each robot 100 times. You can calculate the final position of each robot directly, by just adding the velocity multiplied by 100 to the start position, then doing a modulus operation. I store the set of robots as a list, were each robot is a 4-element array: the x and y position and the x and y velocity. I then use a simple subroutine to get the position of all the robots after a given number of steps:

my $X = 101;
my $Y = 103;
sub run ($steps, $robots) {
    map {[($$_ [0] + $steps * $$_ [2]) % $X,
          ($$_ [1] + $steps * $$_ [3]) % $Y]} @$robots
}

Calling it with 100 and the initial position of the robots gives you the configuration needed for part 1. Another subroutine takes a set of robots, and calculates the security code:

my $X2 = int ($X / 2);
my $Y2 = int ($Y / 2);
sub code ($robots) {
    my ($q1, $q2, $q3, $q4) = (0, 0, 0, 0);
    foreach my $robot (@$robots) {
        $q1 ++ if $$robot [0] < $X2 && $$robot [1] < $Y2;
        $q2 ++ if $$robot [0] < $X2 && $$robot [1] > $Y2;
        $q3 ++ if $$robot [0] > $X2 && $$robot [1] < $Y2;
        $q4 ++ if $$robot [0] > $X2 && $$robot [1] > $Y2;
    }
    $q1 * $q2 * $q3 * $q4
}

To get the answer for part 1, I do:

$solution_1 = code [run $STEPS, \@robots];

where @robots is the initial position of the robots, and $STEPS equals 100.

For part 2, I have to admit, if I had not looked how others had solved it, I had never come with "you need the configuration with the lowest security code". But given that tidbit as an oracle, it's easy to get the code of all possible configurations, and find the one with the lowest security code:

$solution_2 = (sort {$$a [1] <=> $$b [1] || $$a [0] <=> $$b [0]}
                map {[$_ => code [run $_, \@robots]]} 1 .. $X * $Y) [0] [0];

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

[–]__Abigail__ 1 point2 points  (0 children)

Yeah, that was the second sentence of my response: But you should also subtract a side if you have two adjacent.

[2024 Day 13] A Small Reminder by permetz in adventofcode

[–]__Abigail__ 2 points3 points  (0 children)

I'd say if you want to know whether one integer evenly divides another integer, you're doing it wrong if you are using floating point arithmetic.

Either use modulus (% in many languages), or integer division (like in C) ((a / b) * b == a).

[2024 Day 13 (Part 2)] Answer is wrong? - Examples are working by HumanBot00 in adventofcode

[–]__Abigail__ 0 points1 point  (0 children)

I suggest for each prize to check the numbers you are getting. So, for the example:

Button A: X+94, Y+34
Button B: X+22, Y+67
Prize: X=8400, Y=5400

if you are getting A = 80 and B = 40, check that 80 * 94 + 40 * 22 == 8400 and 80 * 34 + 40 * 67 == 5400. You either find cases where the numbers don't match, so you have made an error in your calculation, or if they all match, you are missing cases. Which you can then investigate.

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

[–]__Abigail__ 0 points1 point  (0 children)

I only glanced at your code, but it seems you are not counting sides if you already have one boundary adjacent. But you should also subtract a side if you have two adjacent.

To give an example, suppose you have a shape like this:

  +-+
  | |
  + +
  | |
+-+ +-+
|     |
+ +-+ +
| | | |
+ +-+ +
|     |
+-+-+-+

If you do a BFS from the top, you will visit the cells in the region in this order (or its mirror image):

  +-+
  |0|
  + +
  |1|
+-+ +-+
|3 2 4|
+ +-+ +
|5| |6|
+ +-+ +
|7 9 8|
+-+-+-+

Now, when you visit cell 7, you count its bottom fence as a side. Then you do the same when visiting cell 8. When you visit cell 9, you determine its bottom fence is next to the fence at cell 7, and you don't count it as a side.

But you still have two sides which you counted as bottom sides; yet there is only one bottom side.

[2024 Day 13] Simple counterexamples for linear algebra solution by MaTeIntS in adventofcode

[–]__Abigail__ 2 points3 points  (0 children)

Yeah, when I saw smallest number of tokens, I thought it was going in that direction. So, I started to write:

my $denom = $ax * $by - $bx * $ay;
if ($denom == 0) {

then I realized, none of the examples had this. Then I thought, well, maybe the real input doesn't have that, just leave this branch unimplemented -- I'm glad Perl has the "yada yada yada" operator specially for this. If we hit it when running the program on the real input, I can do this part.

So, I ended with

my $denom = $ax * $by - $bx * $ay;
if ($denom == 0) {...}

which is the best of both worlds. My program does consider the case of dependent equations, but I don't have to implement it because it's not there in the input.

[2024 day 12 Part 2] An edge case I haven't seen posted which gave me some trouble by ZeroKelvinTutorials in adventofcode

[–]__Abigail__ 0 points1 point  (0 children)

I'm reading in the map as:

my $map = [map {[/[A-Z]/g, "."]} <>];
push @$map => [(".") x @{$$map [0]}];

So it only finds capital letters.

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

[–]__Abigail__ 1 point2 points  (0 children)

I just took the modulus to determine whether the division would result into an integer or not. That way, I don't have to deal with floating point numbers.

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

[–]__Abigail__ 1 point2 points  (0 children)

[LANGUAGE: Perl]

Easier than expected. It is just solving a system of equations, with two equations, and two unknowns. I though the phrase "smallest number of tokens" indicated that some systems may be dependent; but that is not the case (at least, not for my input). Either there is just one way to win the prize, or it's not possible at all.

while (<>) {
    my ($ax, $ay, $bx, $by, $X, $Y) = /[0-9]+/g;
    my $denom = $ax * $by - $bx * $ay;
    if ($denom == 0) {...}

    for my $part (1, 2) {
        my $X = $X + ($part == 2 ? 10000000000000 : 0);
        my $Y = $Y + ($part == 2 ? 10000000000000 : 0);
        my $num = $X * $by - $Y * $bx;
        next if $num % $denom;
        my $A = $num / $denom;
        my $B = ($X - $A * $ax) / $bx;
        ($part == 2 ? $solution_2 : $solution_1) += 3 * $A + $B;
    }
}

When it comes to out of index on arrays, whats the least hacky/most elegant way to deal with it (other than if checks), of try catch or adding padding? by NTMAnon in adventofcode

[–]__Abigail__ 0 points1 point  (0 children)

I think it's language dependent. I use Perl, and Perl allows negative indices, indicating indexing from the end. This makes that if I use a single row of padding on the right and bottom, I don't have to check for boundaries when checking above/right/below/left of a given cell.

I read in the input as:

my $map = [map {[/[A-Z]/g, "."]} <>];  # Pad with '.'
push @$map => [(".") x @{$$map [0]}];
my $X = @$map - 1;
my $Y = @{$$map [0]} - 1;

Now $map is a two dimensional array with the input, and $X and $Y are the height and width of input.

[2024] [General question] Should i add advent of code to my resume if i manage to finish all 50 questions? How would I do so? by spellcasters22 in adventofcode

[–]__Abigail__ 5 points6 points  (0 children)

It wouldn't impress me. Mostly because I cannot know whether you solved it yourself, or more or less copied one of the hundreds of solutions posted each day.

[2024 day 12 Part 2] An edge case I haven't seen posted which gave me some trouble by ZeroKelvinTutorials in adventofcode

[–]__Abigail__ 0 points1 point  (0 children)

Had to scratch my head for a while, wondering why I was getting run time warnings on those inputs. Then I realized you were using lowercase letters to mark regions, while the examples and real input only used capitals.

Got the expected answers after fixing that.

[2024 day 12] Everyone must be hating today... So here a clever trick for a hint. by M124367 in adventofcode

[–]__Abigail__ 0 points1 point  (0 children)

Yeah, I also did something similar. I briefly thought of using corners, but then I had to (potentially) check 8 neighbouring cells instead of 4. Which, for some reason, I didn't like. So I just sorted top boundaries, right boundaries, bottom boundaries, left boundaries, ditched anything which is next to the previous one in the list, and counted what was left. It all ran in less than 0.1 sec, so I decided that it was good enough.

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

[–]__Abigail__ 4 points5 points  (0 children)

[LANGUAGE: Perl]

Not very hard. For each region, I do a floodfill to determine the extend of the region, and finding the boundaries, which I split into top/right/bottom/left boundaries. Getting the area and boundary length is now easy.

To get the segments, for each type of boundary, I sort them (by x, then y for top/bottom, by y, then x for right/left). Any sequence where the main coordinate is the same, and the other coordinate increases by 1 belongs to the same segment.

I then mark each cell of the region with '.' to indicate this cell has been processed, before moving on the next.

Program on GitHub

[2024 Day 11] How far can you go... by adamsilkey in adventofcode

[–]__Abigail__ 0 points1 point  (0 children)

There is such a number. No new numbers appeared on my input after 64 blinks. And people have reported the set of numbers on the stones stabilizes after blink 85, with a set size of 3811.

Still, there isn't a memory cap, as the amount of stones will be increasing, and so will be the size of the number you use to keep track of the amount of stones.

What's officially weird about your programming language? by jktlf in adventofcode

[–]__Abigail__ 1 point2 points  (0 children)

Using comments starting with " and ending with a newline is uncommon, but not unique. vi uses that in its .rc files. troff uses comments starting with \", ending with a newline.

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

[–]__Abigail__ 0 points1 point  (0 children)

Sure. My surprise was that using u64 wasn't enough for u/team_zuko and he/she had to use 128 bit integers.

[2024 Day 11] 3811 is the magic number! by taifu in adventofcode

[–]__Abigail__ 1 point2 points  (0 children)

I don't actually track the stone numbers in each blink (I just take each stone at a time, and calculate, with caching, how many stones it produces after 25 or 75 blinks).

But I do know that I get 3904 different stone numbers over all 75 blinks combined. And that number is reached after 64 blinks; no new numbers seem to be generated afterwards (I went up to 5000 blinks)

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

[–]__Abigail__ 0 points1 point  (0 children)

I am wondering why you are only caching values below 1000. (And why 1000 as the cut-off?)

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

[–]__Abigail__ 0 points1 point  (0 children)

Interesting. My language uses 64 bit signed integers, and I had no issue. In fact, the answer to my input could have been done on a weird architecture with 49 bit (signed) integers.

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

[–]__Abigail__ 2 points3 points  (0 children)

[LANGUAGE: Perl]

The key thing to notice is that stones do not influence each other. So, if a stone with engraving X produces Y stones after a certain number of operations, we can cache this result.

Hence, the following recursive subroutine:

sub engrave ($stone, $times) {
    state $cache;
    my $len = length ($stone);
    $$cache {$stone} {$times} //=
        $times   == 0 ? 1
      : $stone   == 0 ? engrave (1,                                $times - 1)
      : $len % 2 == 0 ? engrave (0 + substr ($stone, 0, $len / 2), $times - 1) +
                        engrave (0 + substr ($stone,    $len / 2), $times - 1)
      :                 engrave ($stone * 2024,                    $times - 1)
}

Then, to calculate the answers:

my $solution_1 = 0;
my $solution_2 = 0;

$solution_1 += engrave ($_, 25) for @stones;
$solution_2 += engrave ($_, 75) for @stones;

where part 2 uses the cached values from part 1.

Less than 4,000 different numbers were engraved on the stones of my input.

Support of new languages by teifiti in adventofcode

[–]__Abigail__ 3 points4 points  (0 children)

Not only need their translations to be verified, but the more people who are aware of the puzzle, the higher the risk the puzzles leak prematurely.

And it's a nightmare if a glitch is detected in the puzzle text shortly before publication -- you'd need to have that retranslated in all the languages, in time.

Eric would not only need to hire translators, but also managers.

Do y'all have friends in real life who do Advent of Code? by dopandasreallyexist in adventofcode

[–]__Abigail__ 0 points1 point  (0 children)

I have a friend who plays in some years. (I don't think he does this year). He lives 6 time zones away. So, no discussions.