Skip to content

Commit fb7bf48

Browse files
committed
Solve 2023/17
1 parent 4fb79ff commit fb7bf48

3 files changed

Lines changed: 249 additions & 0 deletions

File tree

lib/2023/day_17.ex

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
defmodule AdventOfCode.Y2023.Day17 do
2+
@moduledoc """
3+
--- Day 17: Clumsy Crucible ---
4+
Problem Link: https://adventofcode.com/2023/day/17
5+
Difficulty: hard
6+
Tags: graph dijkstra shortest-path state-space grid
7+
"""
8+
alias AdventOfCode.Helpers.{InputReader, Transformers}
9+
alias Yog.Pathfinding.Dijkstra
10+
11+
def input, do: InputReader.read_from_file(2023, 17)
12+
13+
def run(input \\ input()) do
14+
grid = parse_grid(input)
15+
{max_x, max_y} = grid |> Map.keys() |> Enum.max()
16+
17+
p1 = solve(grid, max_x, max_y, 0, 3)
18+
p2 = solve(grid, max_x, max_y, 4, 10)
19+
20+
{p1, p2}
21+
end
22+
23+
defp solve(grid, max_x, max_y, min_turn, max_straight) do
24+
# State: {x, y, dir_atom, consecutive_count}
25+
# dir_atom: :r, :l, :d, :u, :start
26+
Dijkstra.implicit_dijkstra_by(
27+
from: {0, 0, :start, 0},
28+
successors_with_cost: fn {x, y, dir, count} ->
29+
# Possible directions to try
30+
dirs = if dir == :start, do: [:r, :d], else: next_dirs(dir, count, min_turn, max_straight)
31+
32+
Enum.flat_map(dirs, fn d ->
33+
{nx, ny} = move(x, y, d)
34+
35+
case Map.fetch(grid, {nx, ny}) do
36+
{:ok, cost} ->
37+
# New count is count + 1 if straight, else 1
38+
ncount = if d == dir, do: count + 1, else: 1
39+
[{{nx, ny, d, ncount}, cost}]
40+
41+
:error ->
42+
[]
43+
end
44+
end)
45+
end,
46+
is_goal: fn {x, y, _, count} ->
47+
x == max_x and y == max_y and count >= min_turn
48+
end,
49+
visited_by: fn state -> state end
50+
)
51+
|> case do
52+
{:ok, dist} -> dist
53+
_ -> :failed
54+
end
55+
end
56+
57+
defp next_dirs(dir, count, min_turn, max_straight) do
58+
# Rules:
59+
# 1. Can always go straight if count < max_straight
60+
# 2. Can turn if count >= min_turn
61+
# 3. NO REVERSE
62+
63+
# Straight?
64+
straight = if count < max_straight, do: [dir], else: []
65+
# Turns?
66+
turns = if count >= min_turn, do: turns(dir), else: []
67+
68+
straight ++ turns
69+
end
70+
71+
defp turns(:r), do: [:u, :d]
72+
defp turns(:l), do: [:u, :d]
73+
defp turns(:u), do: [:l, :r]
74+
defp turns(:d), do: [:l, :r]
75+
76+
defp move(x, y, :r), do: {x + 1, y}
77+
defp move(x, y, :l), do: {x - 1, y}
78+
defp move(x, y, :u), do: {x, y - 1}
79+
defp move(x, y, :d), do: {x, y + 1}
80+
81+
defp parse_grid(input) do
82+
input
83+
|> Transformers.lines()
84+
|> Enum.with_index()
85+
|> Enum.reduce(%{}, fn {line, y}, grid ->
86+
line
87+
|> String.graphemes()
88+
|> Enum.with_index()
89+
|> Enum.reduce(grid, fn {char, x}, acc ->
90+
Map.put(acc, {x, y}, String.to_integer(char))
91+
end)
92+
end)
93+
end
94+
95+
def parse(data \\ input()), do: data
96+
end

0 commit comments

Comments
 (0)