Advent of Code 2021, Day 6

These days I don’t get much time to write code. In a lot of ways Outlook and Zoom have become my most used Integrated development Environments, and my emacs sessions are much more likely to involve markdown for a slidedeck than code for a module. But, like I’ve talked about here before I still look at both my practical coding skills and theoretical computational science knowledge as indespencable. And I expect the same the managers and directors on my engineering teams.

Because of that, this year I decided to spend some of my December nights doing this year’s Advent of Code. It helps keep the rust off without being a blocker for anyone at work. So far it is going well. Specifically I was pleased with my solution to day 6. I’m using elixir for most all of the challenges, and this felt like a particularly elegant solution. It would have been fun to model with GenServers for each lanternfish, but there are limited to everything and that limit is far short of ~1.5 trillion fish processes on my laptop.

defmodule Day6 do
  @starter %{
      0 => 0,
      1 => 0,
      2 => 0,
      3 => 0,
      4 => 0,
      5 => 0,
      6 => 0,
      7 => 0,
      8 => 0,
    }

  def part1(starting_pop, iterations) do
    pop_map =
    starting_pop
    |> Enum.frequencies()
    |> Map.merge(@starter, fn _k, v1, v2 -> v1 end)

    Enum.reduce(0..iterations, pop_map, fn(_x, acc) -> tick(acc) end)
    |> Map.values
    |> Enum.sum

  end

  def tick(population = %{}) do
    population
    |> Enum.reduce(@starter, fn({k,v}, acc) ->
      case k do
        0 ->
          {_, ret} =
            acc
          |> Map.put(8, v)
            |> Map.get_and_update(6, fn(current_value) -> {current_value, current_value + v} end)
          ret

        7 ->
          {_, ret} =
            acc
            |> Map.get_and_update(6, fn(current_value) -> {current_value, current_value + v} end)
          ret

        num ->
          Map.put(acc, num - 1, v)
      end
    end)
  end
end

And not that I’m too concerned about raw speed with the BEAM, but being able to run over the full data in less than 300 microseconds isn’t too shabby.

iex(1)> :timer.tc(fn -> Day6.part1([3,5,3,5,1,3,1,1,5,5,1,1,1,2,2,2,3,1,1,5,1,1,5,5,3,2,2,5,4,4,1,5,1,4,4,5,2,4,1,1,5,3,1,1,4,1,1,1,1,4,1,1,1,1,2,1,1,4,1,1,1,2,3,5,5,1,1,3,1,4,1,3,4,5,1,4,5,1,1,4,1,3,1,5,1,2,1,1,2,1,4,1,1,1,4,4,3,1,1,1,1,1,4,1,4,5,2,1,4,5,4,1,1,1,2,2,1,4,4,1,1,4,1,1,1,2,3,4,2,4,1,1,5,4,2,1,5,1,1,5,1,2,1,1,1,5,5,2,1,4,3,1,2,2,4,1,2,1,1,5,1,3,2,4,3,1,4,3,1,2,1,1,1,1,1,4,3,3,1,3,1,1,5,1,1,1,1,3,3,1,3,5,1,5,5,2,1,2,1,4,2,3,4,1,4,2,4,2,5,3,4,3,5,1,2,1,1,4,1,3,5,1,4,1,2,4,3,1,5,1,1,2,2,4,2,3,1,1,1,5,2,1,4,1,1,1,4,1,3,3,2,4,1,4,2,5,1,5,2,1,4,1,3,1,2,5,5,4,1,2,3,3,2,2,1,3,3,1,4,4,1,1,4,1,1,5,1,2,4,2,1,4,1,1,4,3,5,1,2,1], 255) end)
{288, 1653250886439}