From 8d8da8d6a393213a277c0b29b7e4485e1f2c00c2 Mon Sep 17 00:00:00 2001
From: Sloane Perrault <sloane.perrault@gmail.com>
Date: Wed, 21 Sep 2022 09:19:53 -0400
Subject: [PATCH] solve 2015 day 18

---
 2015/README.md      |   3 +-
 2015/lib/2015/18.ex | 105 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 107 insertions(+), 1 deletion(-)
 create mode 100644 2015/lib/2015/18.ex

diff --git a/2015/README.md b/2015/README.md
index 0ed2f5b..d8143d1 100644
--- a/2015/README.md
+++ b/2015/README.md
@@ -16,7 +16,7 @@
 | :--: | :--: | :-:  | :-: | :-:  | :-:  | :-:  |
 |      |      | [1]  | [2] | [3]  | [4]  | [5]  |
 |  [6] | [7]  | [8]  | [9] | [10] | [11] | [12] |
-| [13] | [14] | [15] | [16]| [17] | 18   | 19   |
+| [13] | [14] | [15] | [16]| [17] | [18] | 19   |
 |  20  | 21   | 22   | 23  | 24   | 25   |      |
 
 [1]: ./lib/2015/1.ex
@@ -36,3 +36,4 @@
 [15]: ./lib/2015/15.ex
 [16]: ./lib/2015/16.ex
 [17]: ./lib/2015/17.ex
+[18]: ./lib/2015/18.ex
diff --git a/2015/lib/2015/18.ex b/2015/lib/2015/18.ex
new file mode 100644
index 0000000..1dcf46c
--- /dev/null
+++ b/2015/lib/2015/18.ex
@@ -0,0 +1,105 @@
+import AOC
+
+aoc 2015, 18 do
+  def input() do
+    lists =
+      input_string()
+      |> String.split("\n", trim: true)
+      |> Enum.map(fn line ->
+        line
+        |> String.split("", trim: true)
+        |> Enum.map(&decode_state/1)
+        |> Enum.with_index(1)
+      end)
+      |> Enum.with_index(1)
+
+    for {row, i} <- lists, {cell, j} <- row, into: %{} do
+      {{i, j}, cell}
+    end
+  end
+
+  def decode_state("#"), do: :on
+  def decode_state("."), do: :off
+
+  def encode_state(:on), do: "#"
+  def encode_state(:off), do: :off
+
+  def n({i, j}), do: {i - 1, j}
+  def e({i, j}), do: {i, j + 1}
+  def s({i, j}), do: {i + 1, j}
+  def w({i, j}), do: {i, j - 1}
+  def ne(pos), do: pos |> n() |> e()
+  def se(pos), do: pos |> s() |> e()
+  def sw(pos), do: pos |> s() |> w()
+  def nw(pos), do: pos |> n() |> w()
+
+  def neighbors(map, pos) do
+    [&n/1, &ne/1, &e/1, &se/1, &s/1, &sw/1, &w/1, &nw/1]
+    |> Enum.map(&apply(&1, [pos]))
+    |> Enum.map(&Map.get(map, &1, :off))
+    |> Enum.frequencies()
+  end
+
+  def tick(map) do
+    for {pos, cell} <- map, into: %{} do
+      next_cell =
+        case {cell, neighbors(map, pos)} do
+          {:on, %{on: on}} when on in [2, 3] -> :on
+          {:on, _} -> :off
+          {:off, %{on: 3}} -> :on
+          {:off, _} -> :off
+        end
+
+      {pos, next_cell}
+    end
+  end
+
+  def tick_with_corners_on(map) do
+    for {pos, cell} <- map, into: %{} do
+      if pos in [{1, 1}, {1, 100}, {100, 1}, {100, 100}] do
+        {pos, :on}
+      else
+        next_cell =
+          case {cell, neighbors(map, pos)} do
+            {:on, %{on: on}} when on in [2, 3] -> :on
+            {:on, _} -> :off
+            {:off, %{on: 3}} -> :on
+            {:off, _} -> :off
+          end
+
+        {pos, next_cell}
+      end
+    end
+  end
+
+  def p1 do
+    start = input()
+
+    finish =
+      for _ <- 1..100, reduce: start do
+        curr -> tick(curr)
+      end
+
+    finish
+    |> Map.values()
+    |> Enum.count(&(&1 == :on))
+  end
+
+  def p2 do
+    start =
+      input()
+      |> Map.put({1, 1}, :on)
+      |> Map.put({1, 100}, :on)
+      |> Map.put({100, 1}, :on)
+      |> Map.put({100, 100}, :on)
+
+    finish =
+      for _ <- 1..100, reduce: start do
+        curr -> tick_with_corners_on(curr)
+      end
+
+    finish
+    |> Map.values()
+    |> Enum.count(&(&1 == :on))
+  end
+end