defmodule Submarine1 do import AoC.Util.Tuple @sub_key :sub @default {0,0} def reset(), do: Process.delete(@sub_key) defp update(key, f, default), do: Process.put(key, f.(Process.get(key, default))) def forward(n), do: update(@sub_key, &pairwise_sum(&1, {n, 0}), @default) def down(n), do: update(@sub_key, &pairwise_sum(&1, {0, n}), @default) def up(n), do: update(@sub_key, &pairwise_sum(&1, {0, -n}), @default) def checksum() do {x, y} = Process.get(@sub_key, @default) x * y end end defmodule Submarine2 do import AoC.Util.Tuple @sub_key :sub @default {0, 0, 0} def reset(), do: Process.delete(@sub_key) defp update(key, f, default), do: Process.put(key, f.(Process.get(key, default))) def forward(n), do: update(@sub_key, fn {_,_,a} = p -> pairwise_sum(p, {n, a*n, 0}) end, @default) def down(n), do: update(@sub_key, &pairwise_sum(&1, {0, 0, n}), @default) def up(n), do: update(@sub_key, &pairwise_sum(&1, {0, 0, -n}), @default) def checksum() do {x, y, _} = Process.get(@sub_key, @default) x * y end end gen_code = fn instructions, part -> instructions = Code.string_to_quoted(instructions) quote do unquote( case part do :part1 -> quote do: import Submarine1 :part2 -> quote do: import Submarine2 end ) reset() unquote(instructions) checksum() end end solve = fn instructions, part -> {soln, _} = Code.eval_quoted(gen_code.(instructions, part)) soln end test_input = """ forward 5 down 5 forward 8 up 3 down 8 forward 2 """ if solve.(test_input, :part1) != 150 or solve.(test_input, :part2) != 900 do raise "unable to solve example input" end input = File.read!("./lib/Y2021/day2.txt") solve.(input, :part2) |> IO.puts()