Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Advent of Code Day 6

day_06/day_six.livemd

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, &amp;(&amp;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