Skip to content

Commit 4def78e

Browse files
committed
day23
1 parent 2ec0a13 commit 4def78e

File tree

2 files changed

+170
-0
lines changed

2 files changed

+170
-0
lines changed

23/sample_input

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#.#####################
2+
#.......#########...###
3+
#######.#########.#.###
4+
###.....#.>.>.###.#.###
5+
###v#####.#v#.###.#.###
6+
###.>...#.#.#.....#...#
7+
###v###.#.#.#########.#
8+
###...#.#.#.......#...#
9+
#####.#.#.#######.#.###
10+
#.....#.#.#.......#...#
11+
#.#####.#.#.#########v#
12+
#.#...#...#...###...>.#
13+
#.#.#v#######v###.###v#
14+
#...#.>.#...>.>.#.###.#
15+
#####v#.#.###v#.#.###.#
16+
#.....#...#...#.#.#...#
17+
#.#########.###.#.#.###
18+
#...###...#...#...#.###
19+
###.###.#.###v#####v###
20+
#...#...#.#.>.>.#.>.###
21+
#.###.###.#.###.#.#v###
22+
#.....###...###...#...#
23+
#####################.#
24+

23/sol.exs

+146
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
defmodule Day23 do
2+
def read_input(file_path) do
3+
file_path
4+
|> File.read!()
5+
|> String.split("\n", trim: true)
6+
|> Enum.map(&String.graphemes/1)
7+
|> extract_start_end()
8+
end
9+
10+
def extract_start_end(grid) do
11+
start_col = grid |> Enum.at(0) |> Enum.find_index(fn x -> x == "." end)
12+
end_col = grid |> Enum.at(-1) |> Enum.find_index(fn x -> x == "." end)
13+
end_row = length(grid) - 1
14+
15+
%{
16+
map: grid |> Enum.map(&List.to_tuple/1) |> List.to_tuple(),
17+
start: {0, start_col},
18+
end: {end_row, end_col},
19+
nrows: grid |> length(),
20+
ncols: grid |> Enum.at(0) |> length()
21+
}
22+
end
23+
24+
def create_map(%{nrows: nrows, ncols: ncols} = grid) do
25+
Enum.reduce(0..(nrows - 1), %{}, fn r, acc ->
26+
Enum.reduce(0..(ncols - 1), acc, fn c, acc ->
27+
if get({r, c}, grid) != "#" do
28+
neighbors = [
29+
{r - 1, c},
30+
{r + 1, c},
31+
{r, c - 1},
32+
{r, c + 1}
33+
]
34+
35+
reachable =
36+
Enum.filter(neighbors, fn {nr, nc} ->
37+
get({nr, nc}, grid) != "#"
38+
end)
39+
|> Enum.map(fn {nr, nc} -> {{nr, nc}, 1} end)
40+
41+
Map.put(acc, {r, c}, reachable)
42+
else
43+
acc
44+
end
45+
end)
46+
end)
47+
end
48+
49+
def eliminate_direct_node(map, node) do
50+
reachable = map[node]
51+
52+
if length(reachable) == 2 do
53+
[{{n1, c1}, d1}, {{n2, c2}, d2}] = reachable
54+
55+
map
56+
|> Map.update({n1, c1}, [], fn old ->
57+
[{{n2, c2}, d1 + d2} | old |> Enum.reject(fn {l, _d} -> l == node end)]
58+
end)
59+
|> Map.update({n2, c2}, [], fn old ->
60+
[{{n1, c1}, d1 + d2} | old |> Enum.reject(fn {l, _d} -> l == node end)]
61+
end)
62+
|> Map.delete(node)
63+
else
64+
map
65+
end
66+
end
67+
68+
def eliminate_all(map) do
69+
case Enum.filter(map, fn {_, reach} -> length(reach) == 2 end) do
70+
[] -> map
71+
[{node, _} | _] -> eliminate_all(eliminate_direct_node(map, node))
72+
end
73+
end
74+
75+
def get({r, c}, %{nrows: nrows, ncols: ncols}) when r < 0 or c < 0 or r >= nrows or c >= ncols,
76+
do: "#"
77+
78+
def get({r, c}, %{map: map}), do: map |> elem(r) |> elem(c)
79+
80+
def next_steps_partA({{r, c} = loc, seen, d}, grid) do
81+
new_seen = MapSet.put(seen, loc)
82+
83+
case get(loc, grid) do
84+
"." -> [{r + 1, c}, {r - 1, c}, {r, c + 1}, {r, c - 1}]
85+
"v" -> [{r + 1, c}]
86+
"^" -> [{r - 1, c}]
87+
">" -> [{r, c + 1}]
88+
"<" -> [{r, c - 1}]
89+
_ -> []
90+
end
91+
|> Enum.reject(fn l -> get(l, grid) == "#" or MapSet.member?(seen, l) end)
92+
|> Enum.map(fn l -> {l, new_seen, d + 1} end)
93+
end
94+
95+
def longest_path(grid, fun), do: longest_path(grid, fun, [{grid[:start], MapSet.new(), 0}], 0)
96+
97+
def longest_path(_, _, [], curr_max), do: curr_max
98+
99+
def longest_path(grid, fun, [{loc, _, _} = s | rest], curr_max) do
100+
next_s = fun.(s, grid)
101+
102+
next_max =
103+
if loc == grid[:end] do
104+
max(curr_max, elem(s, 2))
105+
else
106+
curr_max
107+
end
108+
109+
longest_path(grid, fun, rest ++ next_s, next_max)
110+
end
111+
112+
def longest_path(%{start: s} = graph) do
113+
longest_path(graph, [{s, MapSet.new(), 0}], 0)
114+
end
115+
116+
def longest_path(_, [], curr_max), do: curr_max
117+
118+
def longest_path(graph, [{loc, seen, dist} | rest], curr_max) do
119+
new_seen = MapSet.put(seen, loc)
120+
121+
if(loc == graph[:end]) do
122+
longest_path(graph, rest, max(curr_max, dist))
123+
else
124+
reach =
125+
graph[:graph][loc]
126+
|> Enum.reject(fn {l, _} -> MapSet.member?(seen, l) end)
127+
|> Enum.map(fn {l, d} -> {l, new_seen, d + dist} end)
128+
129+
longest_path(graph, reach ++ rest, curr_max)
130+
end
131+
end
132+
133+
def partA(file_path) do
134+
file_path |> read_input() |> longest_path(&next_steps_partA/2)
135+
end
136+
137+
def partB(file_path) do
138+
file_path
139+
|> read_input()
140+
|> (fn x -> %{start: x[:start], end: x[:end], graph: x |> create_map() |> eliminate_all()} end).()
141+
|> longest_path()
142+
end
143+
end
144+
145+
IO.puts(Day23.partA("./input"))
146+
IO.puts(Day23.partB("./input"))

0 commit comments

Comments
 (0)