aboutsummaryrefslogtreecommitdiff
path: root/2021/day-06/day-06.livemd
diff options
context:
space:
mode:
Diffstat (limited to '2021/day-06/day-06.livemd')
-rw-r--r--2021/day-06/day-06.livemd151
1 files changed, 151 insertions, 0 deletions
diff --git a/2021/day-06/day-06.livemd b/2021/day-06/day-06.livemd
new file mode 100644
index 0000000..87224e9
--- /dev/null
+++ b/2021/day-06/day-06.livemd
@@ -0,0 +1,151 @@
+<!-- livebook:{"persist_outputs":true} -->
+
+# Advent of Code 2021, Day 6
+
+## Short problem summary
+
+A school of fish reproduce according to the following rules:
+
+* Every fish has an "internal timer"
+* The timer decrements by one every day
+* If the timer is at 0, the timer is instead reset to 6,
+ and a new fish with an internal timer of 8 is added to the school
+
+Questions:
+
+1. How many fish are in the school after 80 days?
+2. How many fish are in the school after 256 days?
+
+## Setting up
+
+The initial input is a list of fish, represented by the initial value of their internal timer:
+
+```elixir
+school =
+ with {:ok, data} <- File.read("day-06/input.txt") do
+ data
+ |> String.trim()
+ |> String.split(",")
+ |> Enum.map(&String.to_integer/1)
+ end
+```
+
+```output
+[5, 4, 3, 5, 1, 1, 2, 1, 2, 1, 3, 2, 3, 4, 5, 1, 2, 4, 3, 2, 5, 1, 4, 2, 1, 1, 2, 5, 4, 4, 4, 1, 5,
+ 4, 5, 2, 1, 2, 5, 5, 4, 1, 3, 1, 4, 2, 4, 2, 5, 1, ...]
+```
+
+Every fish with the same starting internal timer will reproduce at the same time,
+as will all of the children of those fish and their children, and so forth,
+so we don't need to track individual fish; we just need to group the fish based on
+their starting internal timer and track those groups throughout the simulation.
+
+```elixir
+starting_state = Enum.frequencies(school)
+```
+
+```output
+%{1 => 88, 2 => 45, 3 => 54, 4 => 52, 5 => 61}
+```
+
+Every time a day passes, the following things happen:
+
+* All the internal timers decrement by 1
+* The group of fish with an internal timer of -1 is reset to 6
+ (added to any existing fish whose timers are already at 6),
+ and an equal-sized group of fish with internal timer 8 is added
+
+```elixir
+defmodule Day06 do
+ def next_day(state) do
+ with one_day_older <- Enum.into(state, %{}, fn {k, v} -> {k - 1, v} end),
+ {n, s} <- Map.pop(one_day_older, -1, 0) do
+ Map.update(s, 6, n, &(&1 + n))
+ |> Map.put(8, n)
+ end
+ end
+end
+
+day1 = Day06.next_day(starting_state)
+```
+
+```output
+%{0 => 88, 1 => 45, 2 => 54, 3 => 52, 4 => 61, 6 => 0, 8 => 0}
+```
+
+After the first day there's not any fish old enough to reproduce yet, but after the second day,
+
+```elixir
+day2 = Day06.next_day(day1)
+```
+
+```output
+%{0 => 45, 1 => 54, 2 => 52, 3 => 61, 5 => 0, 6 => 88, 7 => 0, 8 => 88}
+```
+
+The 88 fish whose timers were at 0 have rolled over to 6 and created 88 more fish with timers at 8.
+
+## Solution
+
+Now we just need to apply the transformation function the necessary number
+of times and sum up the total population in the end:
+
+```elixir
+part1_state =
+ Enum.reduce(
+ Enum.to_list(1..80),
+ starting_state,
+ fn _, acc -> Day06.next_day(acc) end
+ )
+ |> IO.inspect()
+ |> Enum.reduce(0, fn {_, v}, acc -> v + acc end)
+```
+
+```output
+%{
+ 0 => 24572,
+ 1 => 43660,
+ 2 => 30525,
+ 3 => 48458,
+ 4 => 41318,
+ 5 => 47697,
+ 6 => 57731,
+ 7 => 23218,
+ 8 => 33738
+}
+```
+
+```output
+350917
+```
+
+Identically for part 2,
+
+```elixir
+part2_state =
+ Enum.reduce(
+ Enum.to_list(1..256),
+ starting_state,
+ fn _, acc -> Day06.next_day(acc) end
+ )
+ |> IO.inspect()
+ |> Enum.reduce(0, fn {_, v}, acc -> v + acc end)
+```
+
+```output
+%{
+ 0 => 139170477178,
+ 1 => 162618979933,
+ 2 => 169389497028,
+ 3 => 188231720546,
+ 4 => 207908029672,
+ 5 => 217769615201,
+ 6 => 252681772250,
+ 7 => 117023886952,
+ 8 => 138124736869
+}
+```
+
+```output
+1592918715629
+```