From 65851ff53bead21b288a093620824578f501f637 Mon Sep 17 00:00:00 2001
From: Alexandre Jesus <adbjesus@gmail.com>
Date: Thu, 12 Dec 2024 21:49:50 +0000
Subject: Day 12

---
 src/day12.exs | 132 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 132 insertions(+)
 create mode 100644 src/day12.exs

(limited to 'src')

diff --git a/src/day12.exs b/src/day12.exs
new file mode 100644
index 0000000..522bb39
--- /dev/null
+++ b/src/day12.exs
@@ -0,0 +1,132 @@
+defmodule Day12 do
+  def part1(data) do
+    grid = data
+    |> String.split("\n", trim: true)
+    |> Enum.map(&String.codepoints/1)
+
+    grid = for {l, i} <- Enum.with_index(grid),
+               {c, j} <- Enum.with_index(l) do
+             {{i, j}, c}
+    end
+    |> Map.new()
+
+    for pos <- Map.keys(grid), reduce: {MapSet.new(), 0} do
+      {vis, res} -> solve1(vis, grid, pos)
+      |> then(fn {vis, a, p} -> {vis, res + a*p} end)
+    end
+    |> elem(1)
+  end
+
+  defp solve1(vis, grid, pos) do
+    if MapSet.member?(vis, pos) do
+      {vis, 0, 0}
+    else
+      for npos <- dirs(pos), reduce: {MapSet.put(vis, pos), 1, 0} do
+        {vis, a, p} ->
+          case Map.fetch(grid, npos) do
+            :error ->
+              {vis, a, p+1}
+            {_, v} ->
+              if v == Map.fetch!(grid, pos) do
+                {vis, na, np} = solve1(vis, grid, npos)
+                {vis, a+na, p+np}
+              else
+                {vis, a, p+1}
+              end
+          end
+      end
+    end
+  end
+
+  defp dirs({i, j}) do
+    [{i+1, j}, {i-1, j}, {i, j+1}, {i, j-1}]
+  end
+
+  def part2(data) do
+    grid = data
+    |> String.split("\n", trim: true)
+    |> Enum.map(&String.codepoints/1)
+
+    grid = for {l, i} <- Enum.with_index(grid),
+               {c, j} <- Enum.with_index(l) do
+             {{i, j}, c}
+    end
+    |> Map.new()
+
+    for pos <- Map.keys(grid), reduce: {MapSet.new(), 0} do
+      {vis, res} -> solve2(vis, grid, pos)
+      |> then(fn {vis, a, walls} -> {vis, res + a*perim(walls)} end)
+    end
+    |> elem(1)
+  end
+
+  defp solve2(vis, grid, pos) do
+    if MapSet.member?(vis, pos) do
+      {vis, 0, []}
+    else
+      for {npos, dir} <- dirs2(pos), reduce: {MapSet.put(vis, pos), 1, []} do
+        {vis, a, walls} ->
+          case Map.fetch(grid, npos) do
+            :error ->
+              {vis, a, [{dir, npos} | walls]}
+            {_, v} ->
+              if v == Map.fetch!(grid, pos) do
+                {vis, na, nwalls} = solve2(vis, grid, npos)
+                {vis, a+na, walls ++ nwalls}
+              else
+                {vis, a, [{dir, npos} | walls]}
+              end
+          end
+      end
+    end
+  end
+
+  defp dirs2({i, j}) do
+    [{{i+1, j}, :down},
+     {{i-1, j}, :up},
+     {{i, j+1}, :right},
+     {{i, j-1}, :left}]
+  end
+
+  defp dist({a, b}, {c, d}), do: abs(a-c) + abs(b-d)
+  defp dist(_, nil), do: nil
+  defp dist(nil, _), do: nil
+
+  defp perim([]), do: 0
+
+  defp perim(walls) do
+    gwalls = walls
+    |> Enum.group_by(&elem(&1,0), &elem(&1,1))
+
+    for {dir, walls} <- gwalls do
+      swalls =
+        cond do
+          dir == :up or dir == :down ->
+            Enum.sort(walls)
+          true ->
+            Enum.sort_by(walls, fn {i, j} -> {j, i} end)
+        end
+
+      for w <- swalls, reduce: {nil, 0} do
+        {prev, acc} ->
+          if dist(w, prev) == 1 do
+            {w, acc}
+          else
+            {w, acc+1}
+          end
+      end
+      |> elem(1)
+    end
+    |> Enum.sum()
+  end
+end
+
+data = IO.read(:stdio, :eof)
+
+{time1 , ans1} = :timer.tc(fn -> Day12.part1(data) end)
+IO.puts("Time  : #{time1 / 1000000}")
+IO.puts("Answer: #{ans1}")
+
+{time2 , ans2} = :timer.tc(fn -> Day12.part2(data) end)
+IO.puts("Time  : #{time2 / 1000000}")
+IO.puts("Answer: #{ans2}")
-- 
cgit v1.2.3