Powered by AppSignal & Oban Pro

Day 21

2021/day21.livemd

Day 21

Section

input = File.read!("day21.txt")

[[_, p1], [_, p2]] = Regex.scan(~r/: (\d)/, input)

p1 = String.to_integer(p1)
p2 = String.to_integer(p2)

{p1, p2}
{3, 5}
round =
  Stream.unfold(0, &{&1, &1 + 1})
  |> Stream.scan({{0, p1}, {0, p2}}, fn n, {{s, p}, other} ->
    p = rem(p - 1 + 6 + n * 9, 10) + 1

    {other, {s + p, p}}
  end)
  |> Enum.take_while(fn {{s, _}, {_, _}} -> s < 1000 end)

{rounds, {{loser, _}, _}} = {length(round), List.last(round)}

rounds * 3 * loser
720750
defmodule Day21.Task2 do
  def play(p1, p2) do
    tid = :ets.new(__MODULE__, [])
    {a, b} = cached_round({p1 - 1, 0}, {p2 - 1, 0}, tid)
    :ets.delete(tid)
    max(a, b)
  end

  defp cached_round(a, b, tid) do
    case :ets.lookup(tid, {a, b}) do
      [{_, v}] ->
        v

      [] ->
        value = round(a, b, tid)

        :ets.insert(tid, {{a, b}, value})

        value
    end
  end

  defp round({_, s}, _, _) when s >= 21, do: {1, 0}
  defp round(_, {_, s}, _) when s >= 21, do: {0, 1}

  defp round({pos, score}, p2, tid) do
    for a <- 1..3, b <- 1..3, c <- 1..3, reduce: {0, 0} do
      {wins1, wins2} ->
        next = rem(pos + a + b + c, 10)

        {nscore2, nscore1} =
          cached_round(
            p2,
            {next, score + next + 1},
            tid
          )

        {wins1 + nscore1, wins2 + nscore2}
    end
  end
end
{:module, Day21.Task2, <<70, 79, 82, 49, 0, 0, 12, ...>>, {:round, 3}}
Day21.Task2.play(p1, p2)
275067741811212