Advent of Code Day 6
Setup
test_input = "3,4,3,1,2"
input = File.read!("day_06/input.txt")
defmodule Setup do
def parse(input) when is_binary(input) do
input
|> String.replace("\n", "")
|> String.split(",")
|> Enum.map(&String.to_integer/1)
end
end
{:module, Setup, <<70, 79, 82, 49, 0, 0, 6, ...>>, {:parse, 1}}
Part 1
defmodule DaySixPart1 do
def lanternfish_count(fish_lifespans, days) when days > 0 do
1..days
|> Enum.reduce(fish_lifespans, fn _day, fish_lifespans_acc ->
process_day(fish_lifespans_acc)
end)
|> length()
end
defp process_day(fish_lifespans) do
Enum.reduce(fish_lifespans, [], fn
0, fish_lifespans_acc ->
[6, 8 | fish_lifespans_acc]
fish, fish_lifespans_acc ->
[fish - 1 | fish_lifespans_acc]
end)
end
end
data = Setup.parse(input)
DaySixPart1.lanternfish_count(data, 80)
346063
Part 2
defmodule DaySixPart2 do
@moduledoc """
Use a map with **eight** keys instead of a list with over a **trillion** elements.
"""
@initial for i <- 0..8, do: {i, 0}, into: %{}
def lanternfish_count(data, days) when days > 0 do
@initial
|> Map.merge(Enum.frequencies(data))
|> lanternfish_in_x_days(days)
|> Map.values()
|> Enum.sum()
end
defp lanternfish_in_x_days(fish, x) do
Enum.reduce(1..x, fish, fn _day, acc ->
lanternfish_for_next_day(acc)
end)
end
defp lanternfish_for_next_day(fish) do
Enum.reduce(8..0, %{}, fn day, fish_acc ->
deduct_timer_and_spawn(fish, day, fish_acc)
end)
end
defp deduct_timer_and_spawn(fish_before, 0, fish_after) do
quantity = Map.fetch!(fish_before, 0)
fish_after
|> Map.update!(6, &(&1 + quantity))
|> Map.put(8, quantity)
end
defp deduct_timer_and_spawn(fish_before, day, fish_after) do
Map.put(fish_after, day - 1, Map.fetch!(fish_before, day))
end
end
data = Setup.parse(input)
DaySixPart2.lanternfish_count(data, 256)
1572358335990
Part 2 - Pattern Matching
# Inspired by this Haskell tweet https://twitter.com/boudrobam/status/1467931786290343942
defmodule DaySixPart2PatternMatching do
@initial for i <- 0..8, do: {i, 0}, into: %{}
def lanternfish_count(data, days) when days > 0 do
f = Map.merge(@initial, Enum.frequencies(data))
[f[0], f[1], f[2], f[3], f[4], f[5], f[6], f[7], f[8]]
|> lanternfish_in_x_days(days)
|> Enum.sum()
end
defp lanternfish_in_x_days(fish, x) do
Enum.reduce(1..x, fish, fn _day, acc ->
lanternfish_counts_next_day(acc)
end)
end
defp lanternfish_counts_next_day([f0, f1, f2, f3, f4, f5, f6, f7, f8]) do
[f1, f2, f3, f4, f5, f6, f7 + f0, f8, f0]
end
end
data = Setup.parse(input)
DaySixPart2PatternMatching.lanternfish_count(data, 256)
# 26984457539
1572358335990