You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
97 lines
2.6 KiB
Elixir
97 lines
2.6 KiB
Elixir
defmodule Day6 do
|
|
def run do
|
|
map =
|
|
File.read!("data.txt")
|
|
|> String.split("\n")
|
|
|> Enum.drop(-1)
|
|
|> Enum.map(&String.graphemes(&1))
|
|
|
|
height = length(map)
|
|
width = length(hd(map))
|
|
|
|
# find initial location
|
|
idx = Enum.concat(map) |> Enum.find_index(&(&1 != "#" and &1 != "."))
|
|
location = {div(idx, height), rem(idx, width)}
|
|
|
|
initialState = %{
|
|
map: map,
|
|
visited: MapSet.new(),
|
|
location: location,
|
|
bearing: 0,
|
|
height: height,
|
|
width: width
|
|
}
|
|
|
|
visited = walk(initialState, false)
|
|
IO.puts("Part 1: #{MapSet.size(visited)}")
|
|
|
|
# for each visited location, try putting an obstacle there
|
|
num_positions =
|
|
Enum.reduce(visited, 0, fn {row, col}, acc ->
|
|
# generate new map with obstacle in the path
|
|
new_row = List.replace_at(Enum.at(map, row), col, "#")
|
|
new_map = List.replace_at(map, row, new_row)
|
|
acc + walk(%{initialState | map: new_map}, true)
|
|
end)
|
|
|
|
IO.puts("Part 2: #{num_positions}")
|
|
end
|
|
|
|
defp walk(state, detect_loops?) do
|
|
# if we're detecting loops, and this location & bearing is already in our map, get out
|
|
if detect_loops? and MapSet.member?(state.visited, {state.location, state.bearing}) do
|
|
1
|
|
else
|
|
# add current location to set
|
|
visited =
|
|
if detect_loops? do
|
|
# if we're detecting loops, also record the bearing
|
|
MapSet.put(state.visited, {state.location, state.bearing})
|
|
else
|
|
MapSet.put(state.visited, state.location)
|
|
end
|
|
|
|
{row, col} = state.location
|
|
|
|
# grab next location
|
|
{next_row, next_col} =
|
|
case state.bearing do
|
|
0 -> {row - 1, col}
|
|
1 -> {row, col + 1}
|
|
2 -> {row + 1, col}
|
|
3 -> {row, col - 1}
|
|
end
|
|
|
|
in_bounds =
|
|
next_row >= 0 and next_row < state.height and
|
|
next_col >= 0 and next_col < state.width
|
|
|
|
if !in_bounds do
|
|
# if not in bounds, end recursion
|
|
if detect_loops? do
|
|
# if we're detecting loops, we didn't find one. return 0
|
|
0
|
|
else
|
|
# otherwise return the list of visited locations
|
|
visited
|
|
end
|
|
else
|
|
cell = Enum.at(state.map, next_row) |> Enum.at(next_col)
|
|
|
|
new_state =
|
|
if cell == "#" do
|
|
# keep location the same, turn 90° right
|
|
new_bearing = Integer.mod(state.bearing + 1, 4)
|
|
%{state | visited: visited, bearing: new_bearing}
|
|
else
|
|
%{state | visited: visited, location: {next_row, next_col}}
|
|
end
|
|
|
|
walk(new_state, detect_loops?)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
Day6.run()
|