Advent of Code 2024

I might have to look for help on this one.

Day 11 Part 2 Hint

Itā€™s not necessary to keep track of each individual stone. Just keep track of each unique stone value and the quantity of each.

I looked at this after I solved it. Thanks for posting it though.

Day 11 spoiler

My struggle was how to keep track of the growth considering that each stone could ultimately spawn itself.

My first epiphany was when I realized that only when there are an even number of digits does the number of stones increase, and then only by 1.

My next epiphany is that I could consider each stone by itself and just keep adding to my result. There was no need to keep manipulating an array.

My next epiphany was that I could cache the result of a stone instead of recalculating it.

My next epiphany was that 0-9 could be calculated easily and could save time of caching.

My final epiphany was that I could cache how much a stone added to the final result over a given number of blinks.

Once compiled, my solution takes about 300 ms.

As I rule I try to avoid string manipulation. In this case, I didnā€™t, and it cost me. Day 11 part 2 dropped from 300 ms to around 100 ms when I replaced my manipulated string key with a calculated integer.

Day 11 Spoiler

I used a pair of dictionaries with the stone value as the key, and the quantity of each as the dictionaryā€™s stored value. After 75 iterations there were only 3772 dictionary entries, so it ran in about 250ms. (All integer calculations, but running on older MacBook.)

Public Sub Day11part2()
  LoadData
  var inpData() As String = Split(pzlData," ")
  var dStone(1) As Dictionary 'use 2 dictionaries to prevent corruption during updates
  dStone(0) = new Dictionary
  dStone(1) = new Dictionary
  var v() As Variant
  var k() As Variant
  var cu As Integer = 1
  var pv As Integer = 1-cu
  for i as Integer = 0 to inpData.LastIndex 'Load dict with starting values
    dStone(cu).Value(inpData(i).ToInteger)=dStone(cu).Lookup(inpData(i).ToInteger,0)+1
  next i
  for blink as Integer = 1 to 75
    pv = cu 'switch current and previous dictionary indices
    cu = 1-cu
    dStone(cu).RemoveAll
    v = dStone(pv).Values
    k = dStone(pv).Keys
    for i as Integer = 0 to k.LastIndex
      var stn As Integer = k(i)
      if stn=0 then 'change 0 to 1
        dStone(cu).Value(1)=dStone(cu).Lookup(1,0)+v(i)
      Else
        var nd As Integer = DigitCount(stn)
        if IsEven(nd) then 'split into two values
          var b As Integer = 10^(nd\2)
          var t As Integer = stn mod b
          dStone(cu).Value(t)=dStone(cu).Lookup(t,0)+v(i)
          t = stn\b
          dStone(cu).Value(t)=dStone(cu).Lookup(t,0)+v(i)
        Else 'multiply by 2024
          var t As Integer = stn*2024
          dStone(cu).Value(t)=dStone(cu).Lookup(t,0)+v(i)
        end if
      end if
    next i
  next blink
  var sTotal As Integer =0
  v=dStone(cu).Values
  for i as Integer = 0 to v.LastIndex
    sTotal = sTotal+v(i) 'sTotal is the puzzle solution
  next i
End Sub

Day 11 Spoiler

I wrote a recursive solution for Part 1 assuming that Iā€™d need another approach for Part 2. Fortunately simply adding a cache dictionary was all that was needed.

Public Function CountStones(n as integer, blinks as Integer) As Integer
  Static cache as New Dictionary
  
  dim lookupKey as Integer = n * 1024 + blinks
  if cache.HasKey( lookupKey ) then
    Return cache.Value( lookupKey )
  end
  
  dim result as Integer
  
  if blinks = 0 then
    result = 1
  elseif n = 0 then
    result = CountStones( 1, blinks-1 )
  else
    dim digits as Integer = Log10MBS( n ) + 1 // Log(n) / Log(10) is wrong for 1000
    if ( digits mod 2 ) <> 0 then
      // odd number of digits
      result = CountStones( n * 2024, blinks-1 )
    else
      // even number of digits
      dim factor as Integer = 10 ^ (digits/2)
      dim n1 as Integer = n \ factor
      dim n2 as Integer = n mod factor
      
      result = result + CountStones( n1, blinks-1 )
      result = result + CountStones( n2, blinks-1 )
    end
  end
  
  cache.Value( lookupKey ) = result
  
  Return result
End Function

Mine looks similar to yours.

For Day 12 part 2, thereā€™s some code I wrote many years ago that will solve it. The only problem is that itā€™s written in 6502 assembly language. So either I have to translate it into XOJO, or else write a 6502 emulator. :slight_smile:

2 Likes

I had guessed that for part 2 of Day 12, the twist was going to be that the grid repeated a million times in various directions. Was relieved to see the actual part 2.

Day 13 was easy for a change.

Meanwhile, Iā€™m still working towards a solution to Day 12 Part 2. Iā€™ve got my old 6502 ASM routine rewritten in Xojo, but still need to add a bit more code to it. I know that Iā€™m over-complicating this, but itā€™s all useful code that Iā€™m adding to my Point2D class.

Day 14 was also straightforward, although part 2 added an interesting twist. Will be interested to see how others approach this.

I fell way behind. :grimacing:

Dealing with some family issues (my parents are making the transition from their home to a Senior Living Center) as well as very busy work schedule, and, having ā€œa bad bugā€ (which is what people want to seem to call Covid these days ĀÆ\(惄)/ĀÆ )

I donā€™t know how the Advent of Code works/ends?

Will I still be able to catch up over the Christmas break or is it all over on the 25th? I was really enjoying the challenge!

Very few of us have the time necessary to spend on these as the puzzles get more difficult and it gets closer to Christmas. Iā€™m falling behind now too. But, you can continue to work on them as long as you want. They wonā€™t disappear.

All of the previous years are still there to work on too, if you run out of things to do. :slight_smile:

2 Likes

Same boat here, minus the bad bug. Work at your own pace.

1 Like

Just finished Day 14.

Day 14 spoiler

It happened that my data came up with 8 or so robots in the center, but thatā€™s not guaranteed, i.e., the tree can be anywhere on the board.

My ultimate solution runs in less than a second compiled and goes like this:

  • I cycle through each second starting at 1.
  • I create a MemoryBlock of size (height * width).
  • For each robotā€™s position, I set the byte corresponding to that index (y * width + x) to 1.
  • Using a Ptr, I cycle through the MemoryBlock looking for an UInt64 of &h0101010101010101, or 8 robots in a row.
  • If that doesnā€™t exist, I continue to next second. If it does, I have my answer.

I found an even faster way. Now Day 14 runs in under 200 ms.

Day 14 spoiler

I create and fill in the MemoryBlock as above, but using the value &h44, which is ā€œDā€ (just for convenience).

After positioning all the robots, I convert my MemoryBlock to a string and use IndexOfBytes( "DDDDDDDD" ) <> -1.

Day 14 spoiler

For part 1 the final positions can be calculated directly without any iteration. So part 1 execution is very fast (3 ms).

For part 2 there didnā€™t seem to be any choice but to do the calculation for each iteration until a feature was found that might signify the Easter Egg. Trying to figure out what an easily detectable feature might be, I guessed that a sequence of contiguous robots (signified by 1ā€™s in my output array) would occur somewhere in a horizontal line. So, I just created a variable to count contiguous ones. If/when the count hit 10 (10 robots in a horizontal line), I assumed that was the feature I was looking for. It turned out to be a correct assumption. Part 2 runs in about 500 ms on my older macbook.

Just finished Day 15.

Day 15 spoiler

Part 1 is pretty straightforward. I created a string array and for every move I check for a gap in that direction. If I hit a wall first, I ignore the instruction. If the gap is right next to the robot, I just move it. Otherwise, I move the box directly next to the robot the gap, then move the robot to the now-empty space.

Part 2 is tricker. Again, I look for a gap in the right direction. If I canā€™t find one, I move on, If itā€™s right next to the robot, I move the robot. Otherwise, if the move is horizontal, I shift everything over as required.

If the move is vertical, I send the square above (or below) the robot to a recursive method that collects each point that is affected. If it finds a wall, it returns nil. Otherwise, it returns a Dictionary with the points as a key and the character that point contains as a value. I then use that information to shift the characters in the grid and move the robot.

This all happens practically instantly even in the IDE.

I was part way through Day 15 part 2, but got interrupted, and havenā€™t had time to get back to it. Hopefully tomorrow. Or if Day 19 looks too intimidating, I may try to finish Day 15 tonight instead.

Iā€™ve given up on Day 12 part 2 for now, Iā€™ll circle back to it later.