diff options
Diffstat (limited to 'src/day12.exs')
-rw-r--r-- | src/day12.exs | 132 |
1 files changed, 132 insertions, 0 deletions
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}") |