Day 17
This commit is contained in:
167
lib/day17.ex
Normal file
167
lib/day17.ex
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
defmodule Aoc2023.Day17 do
|
||||||
|
use Aoc2023
|
||||||
|
|
||||||
|
defmodule PriorityQueue do
|
||||||
|
defstruct [:set]
|
||||||
|
def new(contents \\ []), do: %__MODULE__{set: :gb_sets.from_list(contents)}
|
||||||
|
|
||||||
|
def add(%__MODULE__{set: set}, elem, prio) do
|
||||||
|
%__MODULE__{set: :gb_sets.add({prio, elem}, set)}
|
||||||
|
end
|
||||||
|
|
||||||
|
def size(%__MODULE__{set: set}) do
|
||||||
|
:gb_sets.size(set)
|
||||||
|
end
|
||||||
|
|
||||||
|
def pop_min(%__MODULE__{set: set}) do
|
||||||
|
case :gb_sets.size(set) do
|
||||||
|
0 ->
|
||||||
|
{nil, new()}
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
{res, set} = :gb_sets.take_smallest(set)
|
||||||
|
{res, %__MODULE__{set: set}}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defimpl Inspect do
|
||||||
|
import Inspect.Algebra
|
||||||
|
|
||||||
|
def inspect(%PriorityQueue{set: set}, opts) do
|
||||||
|
concat(["#PriorityQueue.new(", to_doc(:gb_sets.to_list(set), opts), ")"])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse(input) do
|
||||||
|
lines = input |> lines
|
||||||
|
|
||||||
|
{
|
||||||
|
{lines |> length() |> Kernel.-(1), lines |> head |> String.length() |> Kernel.-(1)},
|
||||||
|
lines
|
||||||
|
|> Enum.with_index()
|
||||||
|
|> Enum.flat_map(fn {row, y} ->
|
||||||
|
row
|
||||||
|
|> String.to_charlist()
|
||||||
|
|> Enum.with_index()
|
||||||
|
|> Enum.map(fn {cost, x} -> {{y, x}, String.to_integer(<<cost>>)} end)
|
||||||
|
end)
|
||||||
|
|> Enum.into(%{})
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp a_star(starts, neighbors, heuristic, is_goal) do
|
||||||
|
loop = fn recur, explored, frontier ->
|
||||||
|
{explored, frontier}
|
||||||
|
{{_, current}, frontier} = PriorityQueue.pop_min(frontier)
|
||||||
|
|
||||||
|
if is_goal.(current) do
|
||||||
|
{explored, current}
|
||||||
|
else
|
||||||
|
cost_to_here = Map.get(explored, current) |> elem(1)
|
||||||
|
|
||||||
|
{explored, frontier} =
|
||||||
|
for {neighbor, cost} <- neighbors.(current), reduce: {explored, frontier} do
|
||||||
|
{explored, frontier} ->
|
||||||
|
cost_ = cost_to_here + cost
|
||||||
|
|
||||||
|
if Map.get(explored, neighbor, {nil, cost_}) |> elem(1) < cost_ do
|
||||||
|
{explored, frontier}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Map.put(explored, neighbor, {current, cost_}),
|
||||||
|
PriorityQueue.add(frontier, neighbor, cost_ + heuristic.(neighbor))
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
recur.(recur, explored, frontier)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
{explored, goal} =
|
||||||
|
loop.(
|
||||||
|
loop,
|
||||||
|
starts |> Enum.map(&{&1, {nil, 0}}) |> Enum.into(%{}),
|
||||||
|
starts |> Enum.map(&{0, &1}) |> PriorityQueue.new()
|
||||||
|
)
|
||||||
|
|
||||||
|
# This doesn't include the starting point
|
||||||
|
path_find = fn recur, point, path ->
|
||||||
|
case Map.get(explored, point) do
|
||||||
|
nil -> path
|
||||||
|
{nil, _} -> path
|
||||||
|
{prev, _} -> recur.(recur, prev, [point | path])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
path_find.(path_find, goal, [])
|
||||||
|
end
|
||||||
|
|
||||||
|
def part1({{my, mx}, grid}) do
|
||||||
|
# Nodes are y, x, dir, straight length
|
||||||
|
a_star(
|
||||||
|
[{0, 0, :d, 0}, {0, 0, :r, 0}],
|
||||||
|
fn {y, x, dir, len} ->
|
||||||
|
case dir do
|
||||||
|
:u ->
|
||||||
|
[{y, x - 1, :l, 0}, {y, x + 1, :r, 0}] ++
|
||||||
|
if len < 2, do: [{y + 1, x, dir, len + 1}], else: []
|
||||||
|
|
||||||
|
:d ->
|
||||||
|
[{y, x - 1, :l, 0}, {y, x + 1, :r, 0}] ++
|
||||||
|
if len < 2, do: [{y - 1, x, dir, len + 1}], else: []
|
||||||
|
|
||||||
|
:l ->
|
||||||
|
[{y + 1, x, :u, 0}, {y - 1, x, :d, 0}] ++
|
||||||
|
if len < 2, do: [{y, x - 1, dir, len + 1}], else: []
|
||||||
|
|
||||||
|
:r ->
|
||||||
|
[{y + 1, x, :u, 0}, {y - 1, x, :d, 0}] ++
|
||||||
|
if len < 2, do: [{y, x + 1, dir, len + 1}], else: []
|
||||||
|
end
|
||||||
|
|> Enum.filter(fn {y, x, _, _} -> y in 0..my and x in 0..mx end)
|
||||||
|
|> Enum.map(fn {y, x, _, _} = pos -> {pos, Map.get(grid, {y, x})} end)
|
||||||
|
end,
|
||||||
|
fn {y, x, _, _} -> abs(y - my) + abs(x - mx) end,
|
||||||
|
fn {y, x, _, _} -> {y, x} == {my, mx} end
|
||||||
|
)
|
||||||
|
|> Enum.map(fn {y, x, _, _} -> Map.get(grid, {y, x}) end)
|
||||||
|
|> Enum.sum()
|
||||||
|
end
|
||||||
|
|
||||||
|
def part2({{my, mx}, grid}) do
|
||||||
|
# Nodes are y, x, dir, straight length
|
||||||
|
a_star(
|
||||||
|
[{0, 0, :r, 0}, {0, 0, :d, 0}, {0, 0, :u, 0}, {0, 0, :l, 0}],
|
||||||
|
fn {y, x, dir, len} ->
|
||||||
|
(if(len < 9,
|
||||||
|
do:
|
||||||
|
case dir do
|
||||||
|
:u -> [{y + 1, x, :u, len + 1}]
|
||||||
|
:d -> [{y - 1, x, :d, len + 1}]
|
||||||
|
:l -> [{y, x - 1, :l, len + 1}]
|
||||||
|
:r -> [{y, x + 1, :r, len + 1}]
|
||||||
|
end,
|
||||||
|
else: []
|
||||||
|
) ++
|
||||||
|
if(len > 2,
|
||||||
|
do:
|
||||||
|
case dir do
|
||||||
|
:u -> [{y, x - 1, :l, 0}, {y, x + 1, :r, 0}]
|
||||||
|
:d -> [{y, x - 1, :l, 0}, {y, x + 1, :r, 0}]
|
||||||
|
:l -> [{y + 1, x, :u, 0}, {y - 1, x, :d, 0}]
|
||||||
|
:r -> [{y + 1, x, :u, 0}, {y - 1, x, :d, 0}]
|
||||||
|
end,
|
||||||
|
else: []
|
||||||
|
))
|
||||||
|
|> Enum.filter(fn {y, x, _, _} -> y in 0..my and x in 0..mx end)
|
||||||
|
|> Enum.map(fn {y, x, _, _} = pos -> {pos, Map.get(grid, {y, x})} end)
|
||||||
|
end,
|
||||||
|
fn {y, x, _, _} -> abs(y - my) + abs(x - mx) end,
|
||||||
|
fn {y, x, _, len} -> {y, x} == {my, mx} and len > 2 end
|
||||||
|
)
|
||||||
|
|> Enum.map(fn {y, x, _, _} -> Map.get(grid, {y, x}) end)
|
||||||
|
|> Enum.sum()
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -18,6 +18,7 @@ defmodule Mix.Tasks.Aoc do
|
|||||||
defp module(14), do: Aoc2023.Day14
|
defp module(14), do: Aoc2023.Day14
|
||||||
defp module(15), do: Aoc2023.Day15
|
defp module(15), do: Aoc2023.Day15
|
||||||
defp module(16), do: Aoc2023.Day16
|
defp module(16), do: Aoc2023.Day16
|
||||||
|
defp module(17), do: Aoc2023.Day17
|
||||||
defp module(18), do: Aoc2023.Day18
|
defp module(18), do: Aoc2023.Day18
|
||||||
# [MODULE INSERTION POINT]
|
# [MODULE INSERTION POINT]
|
||||||
|
|
||||||
|
|||||||
13
tests/day17/1
Normal file
13
tests/day17/1
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
2413432311323
|
||||||
|
3215453535623
|
||||||
|
3255245654254
|
||||||
|
3446585845452
|
||||||
|
4546657867536
|
||||||
|
1438598798454
|
||||||
|
4457876987766
|
||||||
|
3637877979653
|
||||||
|
4654967986887
|
||||||
|
4564679986453
|
||||||
|
1224686865563
|
||||||
|
2546548887735
|
||||||
|
4322674655533
|
||||||
1
tests/day17/1.1
Normal file
1
tests/day17/1.1
Normal file
@@ -0,0 +1 @@
|
|||||||
|
102
|
||||||
1
tests/day17/1.2
Normal file
1
tests/day17/1.2
Normal file
@@ -0,0 +1 @@
|
|||||||
|
94
|
||||||
5
tests/day17/2
Normal file
5
tests/day17/2
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
111111111111
|
||||||
|
999999999991
|
||||||
|
999999999991
|
||||||
|
999999999991
|
||||||
|
999999999991
|
||||||
1
tests/day17/2.2
Normal file
1
tests/day17/2.2
Normal file
@@ -0,0 +1 @@
|
|||||||
|
71
|
||||||
Reference in New Issue
Block a user