# Advent of Code 2021 - Day 6

This topic is about Day 6 of the Advent of Code 2021.

We have a private leaderboard (shared with users of Erlang Forums ):

The entry code is:
`370884-a6a71927`

1 Like

Here is my solution:

1 Like

I went with representing the frequencies as a tuple and a simulation step was:

``````defp simulate({s0, s1, s2, s3, s4, s5, s6, s7, s8}, days) when days > 0 do
simulate({s1, s2, s3, s4, s5, s6, s7 + s0, s8, s0}, days - 1)
end
``````

Full solution

Itâ€™d be nice to see an Nx solution here, it fits this problem very nicely.

7 Likes
1 Like

I did it the â€śslowâ€ť way the first time and got instant gratification, the later part, not so instant (in fact not all) until I remembered bucket sort and my good old friends `Enum.frequencies` and `Map.merge/3`

Hereâ€™s how the end product looked like:

``````defmodule AdventOfCode.Y2021.Day06 do

def run_1, do: input!() |> parse() |> multiply(80) |> Enum.sum()
def run_2, do: input!() |> parse() |> multiply(256) |> Enum.sum()
def parse(f), do: f |> String.split(",") |> Enum.map(&String.to_integer/1) |> Enum.frequencies()

def multiply(fishes, day) do
(day == 0 && Map.values(fishes)) ||
multiply(
Map.pop(fishes, 0)
|> then(
&Map.merge(
for({k, v} <- elem(&1, 1), into: %{}, do: {k - 1, v}),
%{6 => elem(&1, 0) || 0, 8 => elem(&1, 0) || 0},
fn _, a, b -> a + b end
)
),
day - 1
)
end
end
``````
1 Like

struggling with the size of part 2. I want to do this in a mathematical way following an exponential growth equation like `number of fishes * (1 + rate of growth)^number of days` but this doesnâ€™t work.

2 Likes

I enjoyed this one!

``````defmodule Aoc.Y2021.Day06 do
@moduledoc """
"""
import Aoc.Helper.IO

def run_part1(), do: get_input() |> solve_part1()
def run_part2(), do: get_input() |> solve_part2()

def solve_part1(data), do: solve(data, 80)
def solve_part2(data), do: solve(data, 256)

def solve(data, days), do: data |> fish_count() |> simulate(days) |> Enum.sum()
def fish_count(data), do: Enum.map(0..8, fn n -> Map.get(Enum.frequencies(data), n, 0) end)

def simulate(fish_count, 0), do: fish_count

def simulate([zeroth, first, second, third, fourth, fifth, sixth, seventh, eighth], days) do
simulate([first, second, third, fourth, fifth, sixth, seventh + zeroth, eighth, zeroth], days - 1)
end

defp get_input(), do: get_integer_input("2021", "06", ",")
end

``````
6 Likes

I wish I analyzed input properties more before jumping to solving things. The same happened with Bingo, the solution looked different in my head before and after I realized itâ€™s a 5x5 grid all the time.

Looking forward to what you come up with. I was thinking the same thing but got lazy (scared) and backed away.

Iâ€™m going to have to go to bed without solving it. My initial idea was `Bn= B0 x 2^Kt` where `B0` is the original count for each timer value. `K` is 1/7 and `t` (for part 2) is `256 - the original timer value`. Just calculate that for each original timer value and then add them together. Not sure how to make it work though.

DERP.
Not accounting for the maturation time with each generation.

1 Like

I solved Part I with a `DynamicSupervisor` and a process per a fish approach

I expected fish behavioral features are to be changed in Part II and boom.

2 Likes

Really nice!

1 Like

Itâ€™s nice when it works on part 2 without modifications. Not the most clever solution but itâ€™s fast enough!

``````defmodule AdventOfCode.Day06 do
def part1(input) do
input
|> parse_input()
|> Enum.frequencies()
|> pass_days(80)
|> Map.values()
|> Enum.sum()
end

def part2(input) do
input
|> parse_input()
|> Enum.frequencies()
|> pass_days(256)
|> Map.values()
|> Enum.sum()
end

def pass_days(fish, 0), do: fish

def pass_days(fish, days_left) do
fish
|> Enum.reduce(%{}, fn
{0, qt}, acc ->
acc |> Map.update(6, qt, &(&1 + qt)) |> Map.put(8, qt)

{n, qt}, acc ->
acc |> Map.update(n - 1, qt, &(&1 + qt))
end)
|> pass_days(days_left - 1)
end

def parse_input(input) do
input |> String.trim() |> String.split(",") |> Enum.map(&String.to_integer/1)
end
end
``````
1 Like

My solution:
I used 2 maps to track two groups of fish. `fish` cycles 7 days, `spawn` cycles 9 days.
I couldnâ€™t figure a way to track of all fish at the same rate, so I split into two groups, each with a different â€śgestationâ€ť rate.

Runs ~250ms on Apple M1.

``````defmodule Advent.Y2021.D06 do
@spec part_one([integer()], integer()) :: integer()
def part_one(seed, days) do
map_solution(seed, days)
end

@spec part_two([integer()], integer()) :: integer()
def part_two(seed, days) do
map_solution(seed, days)
end

defp map_solution(seed, days) do
fish = Enum.frequencies(seed)

Stream.unfold({0, fish, %{}}, fn
{day, fish, spawn} ->
fish_cycle = rem(day, 7)
spawn_cycle = rem(day, 9)

num_new_fish = Map.get(spawn, spawn_cycle, 0)
fish = Map.update(fish, fish_cycle, num_new_fish, &(&1 + num_new_fish))

spawn = Map.put(spawn, spawn_cycle, Map.fetch!(fish, fish_cycle))

{{fish, spawn}, {day + 1, fish, spawn}}
end)
|> Enum.at(days - 1)
|> (fn {fish, spawn} ->
Map.merge(fish, spawn, fn _k, v1, v2 -> v1 + v2 end)
end).()
|> Map.values()
|> Enum.sum()
end
end

input =
Path.join(["d06_input.txt"])
|> File.stream!()
|> Stream.map(&String.trim/1)
|> Enum.at(0)
|> String.split(",")
|> Enum.map(&String.to_integer/1)

``````

I originally had a function `list_solution/2` I used for part one. It took way too long for part two (I killed attempt before completion). But here it is incase thereâ€™s any interest

``````@spec list_solution([integer()], integer()) :: integer()
defp list_solution(seed, days) do
Stream.iterate(seed, fn fish ->
num_spawning_fish = Enum.count(fish, &(&1 == 0))

fish =
Enum.map(fish, fn
0 -> 6
n -> n - 1
end)

fish ++ List.duplicate(8, num_spawning_fish)
end)
|> Enum.at(days)
|> length()
end
``````

Looking at others, I see that updating the map key isnâ€™t expensive, and performance is similar - probably the cleaner/nicer way to go!

My solution with Livebook: y2021/day-06.livemd

My solution
Using frequencies as everybody else

Eh, one of the more difficult days for me.
It was immediately clear to me that recursion with big lists is not the way to solve this.
But I was not able to realize that `Enum.frequencies` is the key to this. Quite frustrating, once again.
I had to take a peek here. Once I saw it mentioned, it was so obvious.
day_06.livemd

1 Like

Mine was very similar to some others here

``````fish
|> Enum.reduce(%{}, fn {timer, count}, acc ->
if timer > 0 do
tally_fish(acc, timer - 1, count)
else
acc
|> tally_fish(6, count)
|> tally_fish(8, count)
end
end)
``````

Full solution here

1 Like