-
Notifications
You must be signed in to change notification settings - Fork 1
/
day12.exs
executable file
·86 lines (73 loc) · 2.63 KB
/
day12.exs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#!/usr/bin/env elixir
# Copyright 2022 Google LLC
#
# Use of this source code is governed by an MIT-style
# license that can be found in the LICENSE file or at
# https://opensource.org/licenses/MIT.
#
# https://adventofcode.com/2022/day/12
defmodule Day12 do
@moduledoc """
Input is a 2D grid of lower case letters indicating heights, with `S` and `E`
marking the start and end positions. Movement in the cardinal direction is
allowed if height does not increase by more than one (unlimited decrease is
allowed).
"""
@doc "Count the steps in the shortest path from start to end."
def part1(input) do
{grid, start, stop} = parse(input)
bfs(:queue.from_list([{start, 0}]), grid, stop, MapSet.new([start]))
end
@doc "Find the shorted path to end starting at any `a` position."
def part2(input) do
{grid, _start, stop} = parse(input)
starts = Map.filter(grid, fn {_, height} -> height == ?a end) |> Map.keys()
bfs(:queue.from_list(Enum.map(starts, &{&1, 0})), grid, stop, MapSet.new(starts))
end
defp parse(input) do
input
|> Enum.with_index(1)
|> Enum.reduce({%{}, 0, 0}, fn {line, row}, accout ->
to_charlist(line)
|> Enum.with_index(1)
|> Enum.reduce(accout, fn {char, col}, {grid, start, stop} ->
coord = {row, col}
case char do
?S -> {Map.put(grid, coord, ?a), coord, stop}
?E -> {Map.put(grid, coord, ?z), start, coord}
_ -> {Map.put(grid, coord, char), start, stop}
end
end)
end)
end
defp bfs(queue, grid, target, visited) do
case :queue.out(queue) do
{:empty, _} ->
:not_found
{{:value, {^target, moves}}, _} ->
moves
{{:value, {coord, moves}}, q} ->
with do
next =
valid_moves(coord, grid)
|> Enum.filter(&(!MapSet.member?(visited, &1)))
|> Enum.map(&{&1, moves + 1})
q = Enum.reduce(next, q, &:queue.in(&1, &2))
bfs(q, grid, target, MapSet.union(visited, MapSet.new(next |> Enum.map(&elem(&1, 0)))))
end
end
end
defp valid_moves({row, col}, grid) do
cur_height = grid[{row, col}]
[{-1, 0}, {1, 0}, {0, -1}, {0, 1}]
|> Enum.map(fn {x, y} -> {row + x, col + y} end)
|> Enum.filter(&Map.has_key?(grid, &1))
|> Enum.filter(fn coord -> grid[coord] - cur_height <= 1 end)
end
def main() do
unless function_exported?(Runner, :main, 1), do: Code.compile_file("../runner.ex", __DIR__)
success = Runner.main(Day12, System.argv())
exit({:shutdown, if(success, do: 0, else: 1)})
end
end
if Path.absname(:escript.script_name()) == Path.absname(__ENV__.file), do: Day12.main()