From fbf79992097520c152a4a53960463939e5a8af87 Mon Sep 17 00:00:00 2001
From: sloane <git@sloanelybutsurely.com>
Date: Thu, 28 Nov 2024 19:33:42 -0500
Subject: [PATCH] solve 2015 day 23

---
 2015/README.md      |  14 ++---
 2015/lib/2015/23.ex | 121 ++++++++++++++++++++++++++++++++++++++++++++
 README.md           |   2 +-
 3 files changed, 130 insertions(+), 7 deletions(-)
 create mode 100644 2015/lib/2015/23.ex

diff --git a/2015/README.md b/2015/README.md
index a0ee495..cf82aec 100644
--- a/2015/README.md
+++ b/2015/README.md
@@ -12,12 +12,12 @@
   ```
 </details>
 
-|  S   |  M   |  T   |  W  |  T   |  F   |  S   |
-| :--: | :--: | :-:  | :-: | :-:  | :-:  | :-:  |
-|      |      | [1]  | [2] | [3]  | [4]  | [5]  |
-|  [6] | [7]  | [8]  | [9] | [10] | [11] | [12] |
-| [13] | [14] | [15] | [16]| [17] | [18] | [19] |
-| [20] | [21] | 22   | 23  | 24   | 25   |      |
+|  S   |  M   |  T   |  W   |  T   |  F   |  S   |
+| :--: | :--: | :-:  | :-:  | :-:  | :-:  | :-:  |
+|      |      | [1]  | [2]  | [3]  | [4]  | [5]  |
+|  [6] | [7]  | [8]  | [9]  | [10] | [11] | [12] |
+| [13] | [14] | [15] | [16] | [17] | [18] | [19] |
+| [20] | [21] | 22   | [23] | 24   | 25   |      |
 
 [1]: ./lib/2015/1.ex
 [2]: ./lib/2015/2.ex
@@ -40,3 +40,5 @@
 [19]: ./lib/2015/19.ex
 [20]: ./lib/2015/20.ex
 [21]: ./lib/2015/21.ex
+
+[23]: ./lib/2015/23.ex
diff --git a/2015/lib/2015/23.ex b/2015/lib/2015/23.ex
new file mode 100644
index 0000000..42b33b0
--- /dev/null
+++ b/2015/lib/2015/23.ex
@@ -0,0 +1,121 @@
+import AOC
+
+aoc 2015, 23 do
+  defmodule Program do
+    import Integer, only: [is_even: 1]
+
+    @enforce_keys [:instructions]
+    defstruct [:instructions, ptr: 0, a: 0, b: 0]
+
+    def new(inp) do
+      instructions =
+        for {line, idx} <- Enum.with_index(inp), into: %{} do
+          {idx, parse_line(line)}
+        end
+
+      %Program{instructions: instructions}
+    end
+
+    def run(%Program{ptr: ptr, instructions: instructions} = prgm) do
+      case Map.fetch(instructions, ptr) do
+        {:ok, inst} ->
+          prgm
+          |> exec_inst(inst)
+          |> run()
+
+        :error ->
+          prgm
+      end
+    end
+
+    defp exec_inst(prgm, {:hlf, r}) do
+      prgm
+      |> update!(r, &div(&1, 2))
+      |> step()
+    end
+
+    defp exec_inst(prgm, {:tpl, r}) do
+      prgm
+      |> update!(r, &(&1 * 3))
+      |> step()
+    end
+
+    defp exec_inst(prgm, {:inc, r}) do
+      prgm
+      |> update!(r, &(&1 + 1))
+      |> step()
+    end
+
+    defp exec_inst(prgm, {:jmp, offset}) do
+      update!(prgm, :ptr, &(&1 + offset))
+    end
+
+    defp exec_inst(prgm, {:jie, r, offset}) do
+      if prgm |> fetch!(r) |> is_even() do
+        update!(prgm, :ptr, &(&1 + offset))
+      else
+        step(prgm)
+      end
+    end
+
+    defp exec_inst(prgm, {:jio, r, offset}) do
+      if fetch!(prgm, r) == 1 do
+        update!(prgm, :ptr, &(&1 + offset))
+      else
+        step(prgm)
+      end
+    end
+
+    def reg(%Program{} = prgm, r) do
+      prgm
+      |> Map.get(reg_key(r))
+    end
+
+    defp reg_key("a"), do: :a
+    defp reg_key("b"), do: :b
+    defp reg_key(other), do: other
+
+    defp update!(%Program{} = prgm, r, fun) do
+      Map.update!(prgm, reg_key(r), fun)
+    end
+
+    def fetch!(%Program{} = prgm, r) do
+      Map.fetch!(prgm, reg_key(r))
+    end
+
+    defp step(prgm), do: update!(prgm, :ptr, &(&1 + 1))
+
+    defp parse_line("hlf " <> r), do: {:hlf, r}
+    defp parse_line("tpl " <> r), do: {:tpl, r}
+    defp parse_line("inc " <> r), do: {:inc, r}
+    defp parse_line("jmp +" <> offset), do: {:jmp, String.to_integer(offset)}
+    defp parse_line("jmp -" <> offset), do: {:jmp, 0 - String.to_integer(offset)}
+
+    defp parse_line(<<"jie ", <<r::binary-size(1)>>, ", +", offset::binary>>),
+      do: {:jie, r, String.to_integer(offset)}
+
+    defp parse_line(<<"jie ", <<r::binary-size(1)>>, ", -", offset::binary>>),
+      do: {:jie, r, 0 - String.to_integer(offset)}
+
+    defp parse_line(<<"jio ", <<r::binary-size(1)>>, ", +", offset::binary>>),
+      do: {:jio, r, String.to_integer(offset)}
+
+    defp parse_line(<<"jio ", <<r::binary-size(1)>>, ", -", offset::binary>>),
+      do: {:jio, r, 0 - String.to_integer(offset)}
+  end
+
+  def p1 do
+    input_stream()
+    |> Program.new()
+    |> Program.run()
+    |> Program.fetch!(:b)
+  end
+
+  def p2 do
+    input_stream()
+    |> Program.new()
+    |> Map.put(:a, 1)
+    |> Program.run()
+    |> Program.fetch!(:b)
+  end
+end
diff --git a/README.md b/README.md
index cced555..a90a087 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@
 
 | Year | Stars | Languages |
 | - | - | - |
-| [2015] | **42/50** 🌟 | Elixir |
+| [2015] | **44/50** 🌟 | Elixir |
 | 2016 | | |
 | [2017] | **18/50** 🌟 | Haskell |
 | 2018 | | |