--- AOC Day 6: Lanternfish ---

The sea floor is getting steeper. Maybe the sleigh keys got carried this way?

A massive school of glowing lanternfish swims past. They must spawn quickly to reach such large numbers - maybe exponentially quickly? You should model their growth rate to be sure.

Although you know nothing about this specific species of lanternfish, you make some guesses about their attributes. Surely, each lanternfish creates a new lanternfish once every 7 days.

However, this process isn’t necessarily synchronized between every lanternfish - one lanternfish might have 2 days left until it creates another lanternfish, while another might have 4. So, you can model each fish as a single number that represents the number of days until it creates a new lanternfish.

Furthermore, you reason, a new lanternfish would surely need slightly longer before it’s capable of producing more lanternfish: two more days for its first cycle.

So, suppose you have a lanternfish with an internal timer value of 3:

  • After one day, its internal timer would become 2.
  • After another day, its internal timer would become 1.
  • After another day, its internal timer would become 0.
  • After another day, its internal timer would reset to 6, and it would create a new lanternfish with an internal timer of 8.
  • After another day, the first lanternfish would have an internal timer of 5, and the second lanternfish would have an internal timer of 7.

A lanternfish that creates a new fish resets its timer to 6not 7 (because 0 is included as a valid timer value). The new lanternfish starts with an internal timer of 8 and does not start counting down until the next day.

Realizing what you’re trying to do, the submarine automatically produces a list of the ages of several hundred nearby lanternfish (your puzzle input). For example, suppose you were given the following list:

3,4,3,1,2

This list means that the first fish has an internal timer of 3, the second fish has an internal timer of 4, and so on until the fifth fish, which has an internal timer of 2. Simulating these fish over several days would proceed as follows:

Initial state: 3,4,3,1,2 After 1 day: 2,3,2,0,1 After 2 days: 1,2,1,6,0,8 After 3 days: 0,1,0,5,6,7,8 After 4 days: 6,0,6,4,5,6,7,8,8 After 5 days: 5,6,5,3,4,5,6,7,7,8 After 6 days: 4,5,4,2,3,4,5,6,6,7 After 7 days: 3,4,3,1,2,3,4,5,5,6 After 8 days: 2,3,2,0,1,2,3,4,4,5 After 9 days: 1,2,1,6,0,1,2,3,3,4,8 After 10 days: 0,1,0,5,6,0,1,2,2,3,7,8 After 11 days: 6,0,6,4,5,6,0,1,1,2,6,7,8,8,8 After 12 days: 5,6,5,3,4,5,6,0,0,1,5,6,7,7,7,8,8 After 13 days: 4,5,4,2,3,4,5,6,6,0,4,5,6,6,6,7,7,8,8 After 14 days: 3,4,3,1,2,3,4,5,5,6,3,4,5,5,5,6,6,7,7,8 After 15 days: 2,3,2,0,1,2,3,4,4,5,2,3,4,4,4,5,5,6,6,7 After 16 days: 1,2,1,6,0,1,2,3,3,4,1,2,3,3,3,4,4,5,5,6,8 After 17 days: 0,1,0,5,6,0,1,2,2,3,0,1,2,2,2,3,3,4,4,5,7,8 After 18 days: 6,0,6,4,5,6,0,1,1,2,6,0,1,1,1,2,2,3,3,4,6,7,8,8,8,8

Each day, a 0 becomes a 6 and adds a new 8 to the end of the list, while each other number decreases by 1 if it was present at the start of the day.

In this example, after 18 days, there are a total of 26 fish. After 80 days, there would be a total of 5934.

Find a way to simulate lanternfish. How many lanternfish would there be after 80 days?

 

All credit for the above puzzle goes to Eric Wastl and Advent of Code.

Don’t forget to submit your solutions at adventofcode.com**** and join the KX leaderboard using code 1526329-bb0915a5.

 

 

This post contains solutions!

Part 1

It’s tempting to model the lanternfish population as a vector of timer states, subtracting 1 on each day, resetting each 0 to 6 and appending an 8. That lets us model progress day by day.

q)3{,[;sum[n]#8] (x-1)+7*n:x=0}\3431234312232011216080105678

And count them after 18 and 80 days.

q)count18{,[;sum[n]#8] (x-1)+7*n:x=0}/3431226q)count80{,[;sum[n]#8] (x-1)+7*n:x=0}/343125934

Part 2

But all this gets out of hand over 256 days as the vector count exceeds 26 billion.

A vector is an ordered list, but we do not need the lanternfish in any order. We need only represent how many fish have their timers in a given state. We could do this with a dictionary.

q)counteachgroup343123|24|11|12|1

But even this is more information than we need. There are only nine possible timer values. A vector of nine integers will number the fish at each timer state.

q)showlf:@[9#0;;1+]34312 / lanternfish school011210000

Notice in the application of Amend At above the index vector 3 4 3 1 2 contains two 3s. The unary third argument of Amend At, 1+, is applied twice at index 3. The iteration is implicit in Amend At and need not be specified.

Now we represent a day’s count-down with a 1 rotate, which happily rotates the fish with expired timers round to position 8. But position 8 represents newly spawned fish. So we need also to reset their parents’ timers by adding them at position 6.

q)3{@[1rotatex;6;+;firstx]}\lf011210000112100000121000101210001111

Above, we used the Scan form of the Do iterator to apply the unary lambda {@[1 rotate x;6;+;first x]}, returning a list of the results of 0, 1, 2, and 3 iterations.

After the required iterations, count the fish with sum.

q)sum256{@[1rotatex;6;+;x0]}/lf26984457539

Above we used the Over form of the Do iterator to evaluate the lambda 256 times. (Unlike the Scan form, the Over form returns the result of only the last iteration.)

Our complete solution:

lf:@[9#0;;1+]valuefirstread0`:day6.txt / lanternfisha[`$("6-1";"6-2")]:sumeach@[;80256]256{@[1rotatex;6;+;first0]}\lf

Above, rather than run the same Do iteration first 80 then 256 times, we have run it 256 times with Scan, then selected the 80th and 256th state vectors from the result.