add aoc2024 day 18
This commit is contained in:
parent
9b9b00ed61
commit
7f25d06cc2
1 changed files with 262 additions and 0 deletions
262
elixir/livebook/2024/day18.livemd
Normal file
262
elixir/livebook/2024/day18.livemd
Normal file
|
@ -0,0 +1,262 @@
|
||||||
|
# AOC 2024 Day 18
|
||||||
|
|
||||||
|
## Input
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
e1 = """
|
||||||
|
5,4
|
||||||
|
4,2
|
||||||
|
4,5
|
||||||
|
3,0
|
||||||
|
2,1
|
||||||
|
6,3
|
||||||
|
2,4
|
||||||
|
1,5
|
||||||
|
0,6
|
||||||
|
3,3
|
||||||
|
2,6
|
||||||
|
5,1
|
||||||
|
1,2
|
||||||
|
5,5
|
||||||
|
2,5
|
||||||
|
6,5
|
||||||
|
1,4
|
||||||
|
0,4
|
||||||
|
6,4
|
||||||
|
1,1
|
||||||
|
6,1
|
||||||
|
1,0
|
||||||
|
0,5
|
||||||
|
1,6
|
||||||
|
2,0
|
||||||
|
"""
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Section
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
defmodule Grid2D do
|
||||||
|
defstruct width: nil, height: nil
|
||||||
|
|
||||||
|
@directions %{
|
||||||
|
straight: [
|
||||||
|
{1, 0},
|
||||||
|
{-1, 0},
|
||||||
|
{0, 1},
|
||||||
|
{0, -1}
|
||||||
|
],
|
||||||
|
diagonal: [
|
||||||
|
{1, 1},
|
||||||
|
{1, -1},
|
||||||
|
{-1, 1},
|
||||||
|
{-1, -1}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
def new(width, height) do
|
||||||
|
%Grid2D{width: width, height: height}
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
## Examples
|
||||||
|
iex> Grid2D.index_to_coord(Grid2D.new(5,5), 0)
|
||||||
|
{0,0}
|
||||||
|
|
||||||
|
iex> Grid2D.index_to_coord(Grid2D.new(5,5), 5)
|
||||||
|
{0,1}
|
||||||
|
|
||||||
|
iex> Grid2D.index_to_coord(Grid2D.new(5,5), 6)
|
||||||
|
{1,1}
|
||||||
|
|
||||||
|
iex> Grid2D.index_to_coord(Grid2D.new(5,5), 3)
|
||||||
|
{3,0}
|
||||||
|
|
||||||
|
iex> Grid2D.index_to_coord(Grid2D.new(5,5), 24)
|
||||||
|
{4, 4}
|
||||||
|
"""
|
||||||
|
def index_to_coord(%Grid2D{width: w}, index) do
|
||||||
|
{rem(index, w), floor(index / w)}
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
## Examples
|
||||||
|
iex> Grid2D.coord_to_index( Grid2D.new(5,5), {0,0})
|
||||||
|
0
|
||||||
|
|
||||||
|
iex> Grid2D.coord_to_index( Grid2D.new(5,5), {4,4})
|
||||||
|
24
|
||||||
|
|
||||||
|
iex> Grid2D.coord_to_index( Grid2D.new(5,5), {2,2})
|
||||||
|
12
|
||||||
|
"""
|
||||||
|
def coord_to_index(%Grid2D{width: w}, {x, y}) do
|
||||||
|
y * w + x
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
iex> Grid2D.neighbors({0, 0}, Grid2D.new(5,5)) |> MapSet.new()
|
||||||
|
MapSet.new([{0, 1}, {1, 1}, {1,0}])
|
||||||
|
|
||||||
|
iex> Grid2D.neighbors({2, 2}, Grid2D.new(5,5)) |> MapSet.new()
|
||||||
|
[{1, 1}, {1, 2}, {1, 3}, {2, 1}, {2, 3}, {3, 1}, {3, 2}, {3, 3}] |> MapSet.new()
|
||||||
|
|
||||||
|
iex> Grid2D.neighbors({4, 4}, Grid2D.new(5,5)) |> MapSet.new
|
||||||
|
[{3, 3}, {3, 4}, {4, 3}] |> MapSet.new
|
||||||
|
"""
|
||||||
|
def neighbors(p, g, allowed_directions \\ :all) do
|
||||||
|
directions(allowed_directions)
|
||||||
|
|> Stream.map(&add(p, &1))
|
||||||
|
|> Stream.filter(&is_in_grid?(&1, g))
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Distance from two points in a grid
|
||||||
|
|
||||||
|
iex> Grid2D.distance(Grid2D.new(5,5), {0,0}, {0,1}, :radial)
|
||||||
|
1
|
||||||
|
|
||||||
|
iex> Grid2D.distance(Grid2D.new(5,5), {0,0}, {1,1}, :radial)
|
||||||
|
1
|
||||||
|
|
||||||
|
iex> Grid2D.distance(Grid2D.new(5,5), {0,0}, {1,2}, :radial)
|
||||||
|
2
|
||||||
|
"""
|
||||||
|
def distance(grid, p1, p2, type)
|
||||||
|
def distance(_, {x, y}, {a, b}, :radial), do: max(abs(x - a), abs(y - b))
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Add two points together pairwise
|
||||||
|
|
||||||
|
iex> Grid2D.add({0,0}, {0,1})
|
||||||
|
{0,1}
|
||||||
|
|
||||||
|
iex> Grid2D.add({2,3}, {3,2})
|
||||||
|
{5,5}
|
||||||
|
"""
|
||||||
|
def add({x, y}, {j, k}), do: {x + j, y + k}
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Test if a point is in a grid
|
||||||
|
|
||||||
|
iex> Grid2D.is_in_grid?({1, 2}, %Grid2D{width: 3, height: 4})
|
||||||
|
true
|
||||||
|
|
||||||
|
iex> Grid2D.is_in_grid?({2, 2}, %Grid2D{width: 2, height: 4})
|
||||||
|
false
|
||||||
|
"""
|
||||||
|
def is_in_grid?({x, y}, %Grid2D{width: w, height: h}), do: x < w and y < h and x >= 0 and y >= 0
|
||||||
|
|
||||||
|
@spec directions(allowed :: :all | :straight | :diagonal) :: any()
|
||||||
|
def directions(allowed \\ :all)
|
||||||
|
def directions(:all), do: directions(:straight) ++ directions(:diagonal)
|
||||||
|
def directions(:straight), do: @directions |> Map.get(:straight)
|
||||||
|
def directions(:diagonal), do: @directions |> Map.get(:diagonal)
|
||||||
|
|
||||||
|
def mul({x, y}, n), do: {n*x, n*y}
|
||||||
|
end
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
defmodule RAMRun do
|
||||||
|
def parse(input, take, w, h) do
|
||||||
|
corrupted = input
|
||||||
|
|> String.split("\n", trim: true)
|
||||||
|
|> then(fn l -> if take != nil, do: Enum.take(l, take), else: l end)
|
||||||
|
|> Enum.map(fn l -> l |> String.split(",") |> Enum.map(&String.to_integer/1) |> List.to_tuple() end)
|
||||||
|
|> Enum.into(MapSet.new)
|
||||||
|
|
||||||
|
dist = for x <- 0..(w-1),
|
||||||
|
y <- 0..(h-1),
|
||||||
|
!MapSet.member?(corrupted, {x, y}),
|
||||||
|
reduce: %{}
|
||||||
|
do
|
||||||
|
dist -> dist |> Map.put({x, y}, :infinity)
|
||||||
|
end
|
||||||
|
|
||||||
|
dist = dist |> Map.put({0, 0}, 0)
|
||||||
|
|
||||||
|
{corrupted, dist, Grid2D.new(w, h)}
|
||||||
|
end
|
||||||
|
|
||||||
|
def draw(p, grid) do
|
||||||
|
for y <- 0..(grid.height-1) do
|
||||||
|
for x <- 0..(grid.width-1) do
|
||||||
|
(if p |> MapSet.member?({x, y}), do: "#", else: ".")
|
||||||
|
<> (if x == grid.width-1, do: "\n", else: "")
|
||||||
|
end
|
||||||
|
end |> Enum.join("")
|
||||||
|
end
|
||||||
|
|
||||||
|
def solve_p1(input) do
|
||||||
|
{cor, dist, grid} = parse(input, 1024, 71, 71)
|
||||||
|
{_, len} = find_path(cor, dist, grid)
|
||||||
|
len
|
||||||
|
end
|
||||||
|
|
||||||
|
def solve_p1(input, take, w, h) do
|
||||||
|
{cor, dist, grid} = parse(input, take, w, h)
|
||||||
|
case find_path(cor, dist, grid) do
|
||||||
|
{:unsolvable, _} -> :unsolvable
|
||||||
|
{_, len} -> len
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def solve_p2(input) do
|
||||||
|
min = 1024 # solve_p1(input)
|
||||||
|
fails? = fn i ->
|
||||||
|
{cor, dist, grid} = RAMRun.parse(input, i, 71, 71)
|
||||||
|
match?({:unsolvable, _}, RAMRun.find_path(cor, dist, grid))
|
||||||
|
end
|
||||||
|
max = Enum.count(input |> String.split("\n", trim: true))
|
||||||
|
binary_search(min, max, div(max - min, 2), fails?)
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_path(cor, dist, grid) do
|
||||||
|
find_path(grid, cor, [{{0,0}, 0}], dist, {grid.width-1, grid.height - 1})
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_path(_, _, [{dest,_} | _], dist, dest), do: {dist, dist[dest]}
|
||||||
|
def find_path(_, _, [], dist, _), do: {:unsolvable, dist}
|
||||||
|
def find_path(grid, corrupted, [{next,d} | pq], dist, dest) do
|
||||||
|
{dist, pq} = for nb <- Grid2D.neighbors(next, grid, :straight),
|
||||||
|
!MapSet.member?(corrupted, nb),
|
||||||
|
reduce: {dist, pq} do
|
||||||
|
{dist, pq} ->
|
||||||
|
if dist[nb] > d + 1 do
|
||||||
|
{
|
||||||
|
dist |> Map.put(nb, d + 1),
|
||||||
|
[{nb, d + 1} | (pq |> Enum.filter(&(elem(&1, 0) != nb)))]
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{dist, pq}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
find_path(grid, corrupted, pq |> Enum.sort_by(fn {_coord, d} -> d end), dist, dest)
|
||||||
|
end
|
||||||
|
|
||||||
|
def binary_search(x, y, i, p) when y == x + 1 do
|
||||||
|
if p.(i), do: i, else: i + 1
|
||||||
|
end
|
||||||
|
def binary_search(min, max, i, predicate) do
|
||||||
|
if predicate.(i) do
|
||||||
|
binary_search(min, i, div(i - min, 2) + min, predicate)
|
||||||
|
else
|
||||||
|
binary_search(i, max, div(max - i, 2) + i, predicate)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
RAMRun.solve_p1(input)
|
||||||
|
```
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
n = RAMRun.solve_p2(input)
|
||||||
|
input |> String.split("\n") |> Enum.at(n - 1) |> IO.puts
|
||||||
|
```
|
Loading…
Add table
Reference in a new issue