Skip to content

Commit 4586996

Browse files
committed
Solve 2024/18
1 parent a5304dd commit 4586996

3 files changed

Lines changed: 3547 additions & 0 deletions

File tree

lib/2024/day_18.ex

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
defmodule AdventOfCode.Y2024.Day18 do
2+
@moduledoc """
3+
--- Day 18: RAM Run ---
4+
Problem Link: https://adventofcode.com/2024/day/18
5+
Difficulty: m
6+
Tags: graph shortest-path binary-search dijkstra grid-traversal
7+
"""
8+
alias AdventOfCode.Helpers.{InputReader, Transformers}
9+
alias Yog.Pathfinding.Dijkstra
10+
11+
# Constants for the large grid
12+
@size 70
13+
@part1_bytes 1024
14+
15+
def input, do: InputReader.read_from_file(2024, 18)
16+
17+
def run(input \\ input()) do
18+
coords = parse(input)
19+
20+
p1 = solve_p1(coords)
21+
p2 = solve_p2(coords)
22+
23+
{p1, p2}
24+
end
25+
26+
defp solve_p1(coords) do
27+
corrupted = coords |> Enum.take(@part1_bytes) |> MapSet.new()
28+
find_path(corrupted)
29+
end
30+
31+
defp solve_p2(coords) do
32+
# Binary search for the first byte that blocks the path
33+
low = @part1_bytes
34+
high = length(coords) - 1
35+
36+
idx = binary_search(coords, low, high)
37+
{x, y} = Enum.at(coords, idx)
38+
"#{x},#{y}"
39+
end
40+
41+
defp binary_search(_coords, low, high) when low >= high, do: low
42+
43+
defp binary_search(coords, low, high) do
44+
mid = div(low + high, 2)
45+
corrupted = coords |> Enum.take(mid + 1) |> MapSet.new()
46+
47+
if find_path(corrupted) == :failed do
48+
# Path blocked at mid or before
49+
binary_search(coords, low, mid)
50+
else
51+
# Path still exists after mid
52+
binary_search(coords, mid + 1, high)
53+
end
54+
end
55+
56+
defp find_path(corrupted) do
57+
case Dijkstra.implicit_dijkstra_by(
58+
from: {0, 0},
59+
successors_with_cost: fn {x, y} ->
60+
neighbors(x, y)
61+
|> Enum.filter(fn {nx, ny} ->
62+
nx >= 0 and nx <= @size and ny >= 0 and ny <= @size and
63+
not MapSet.member?(corrupted, {nx, ny})
64+
end)
65+
|> Enum.map(fn pos -> {pos, 1} end)
66+
end,
67+
is_goal: fn pos -> pos == {@size, @size} end,
68+
visited_by: fn pos -> pos end
69+
) do
70+
{:ok, dist} -> dist
71+
_ -> :failed
72+
end
73+
end
74+
75+
defp neighbors(x, y), do: [{x + 1, y}, {x - 1, y}, {x, y + 1}, {x, y - 1}]
76+
77+
def parse(data \\ input()) do
78+
data
79+
|> Transformers.lines()
80+
|> Enum.map(fn line ->
81+
[x, y] = String.split(line, ",") |> Enum.map(&String.to_integer/1)
82+
{x, y}
83+
end)
84+
end
85+
end

0 commit comments

Comments
 (0)