Skip to content

Commit

Permalink
2024 day 16, part 2
Browse files Browse the repository at this point in the history
Feels like I'm doing some unnecessary stuff to figure out all of the paths
non-exponentially but hey it's quick
  • Loading branch information
sevenseacat committed Dec 16, 2024
1 parent 3a9871c commit d825939
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 25 deletions.
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
lib/y*/input/day*.txt filter=git-crypt diff=git-crypt
test/y*/input/day*.txt filter=git-crypt diff=git-crypt
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
My Elixir solutions for [Advent of Code](https://adventofcode.com/) (all years).

<!-- stars start -->
<p><img src="https://img.shields.io/static/v1?label=Total&message=449%20stars&style=for-the-badge&color=green" alt="449 stars" /></p>
<p><a href="./lib/y2024/"><img src="https://img.shields.io/static/v1?label=2024&message=30%20stars&style=for-the-badge&color=yellow" alt="30 stars" /></a><br />
<p><img src="https://img.shields.io/static/v1?label=Total&message=450%20stars&style=for-the-badge&color=green" alt="450 stars" /></p>
<p><a href="./lib/y2024/"><img src="https://img.shields.io/static/v1?label=2024&message=31%20stars&style=for-the-badge&color=yellow" alt="31 stars" /></a><br />
<a href="./lib/y2023/"><img src="https://img.shields.io/static/v1?label=2023&message=44%20stars&style=for-the-badge&color=green" alt="44 stars" /></a><br />
<a href="./lib/y2022/"><img src="https://img.shields.io/static/v1?label=2022&message=%E2%AD%90%EF%B8%8F%2050%20stars%20%E2%AD%90%EF%B8%8F&style=for-the-badge&color=brightgreen" alt="50 stars" /></a><br />
<a href="./lib/y2021/"><img src="https://img.shields.io/static/v1?label=2021&message=46%20stars&style=for-the-badge&color=green" alt="46 stars" /></a><br />
Expand Down
5 changes: 3 additions & 2 deletions lib/y2024/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

My Elixir solutions for [Advent of Code 2024](https://adventofcode.com/2024).

<!-- stars 2024 start --><img src="https://img.shields.io/static/v1?label=2024&message=30%20stars&style=for-the-badge&color=yellow" alt="30 stars" /><!-- stars 2024 end -->
<!-- stars 2024 start --><img src="https://img.shields.io/static/v1?label=2024&message=31%20stars&style=for-the-badge&color=yellow" alt="31 stars" /><!-- stars 2024 end -->

## Benchmarks

Expand Down Expand Up @@ -43,5 +43,6 @@ day 13, part 1 1294.57 0.77 ms ±4.01% 0.76 ms 0.
day 14, part 1 516.12 1.94 ms ±8.66% 1.85 ms 2.30 ms
day 14, part 2 1.41 707.27 ms ±0.37% 707.76 ms 710.36 ms
day 15, part 1 5.06 197.80 ms ±0.60% 197.59 ms 201.50 ms
day 16, part 1 13.98 71.55 ms ±6.08% 73.25 ms 80.54 ms
day 16, part 1 12.91 77.43 ms ±5.49% 77.14 ms 86.71 ms
day 16, part 2 13.07 76.51 ms ±5.00% 76.09 ms 85.69 ms
```
80 changes: 60 additions & 20 deletions lib/y2024/day16.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,24 @@ defmodule Y2024.Day16 do
alias Advent.PathGrid

def part1(path_grid) do
from = Enum.find(path_grid.units, &(&1.identifier == "S")).position
to = Enum.find(path_grid.units, &(&1.identifier == "E")).position
do_parts(path_grid) |> elem(0)
end

{_path, score} = find_lowest_score(path_grid.graph, from, to)
score
def part2(path_grid) do
do_parts(path_grid)
|> elem(1)
|> MapSet.size()
end

# @doc """
# iex> Day16.part2("update or delete me")
# "update or delete me"
# """
# def part2(input) do
# input
# end
defp do_parts(path_grid) do
from = Enum.find(path_grid.units, &(&1.identifier == "S")).position
to = Enum.find(path_grid.units, &(&1.identifier == "E")).position
find_lowest_scores(path_grid.graph, from, to)
end

defp find_lowest_score(graph, from, to) do
defp find_lowest_scores(graph, from, to) do
queue = queue_next_states(PriorityQueue.new(), {from, :east, [{from, :east}], 0}, graph)
do_search(PriorityQueue.pop(queue), graph, to, MapSet.new([{from, :east}]))
do_search(PriorityQueue.pop(queue), graph, to, %{{from, :east} => {0, []}})
end

defp queue_next_states(queue, {coord, facing, path, score}, graph) do
Expand All @@ -48,13 +48,53 @@ defmodule Y2024.Day16 do

defp do_search({{:value, {coord, facing, path, score}}, queue}, graph, to, seen) do
if coord == to do
{path, score}
# We've found a best path! We might have had multiple branches that combined
# together to get to this point though
{score, get_all_paths(path, seen)}
else
if MapSet.member?(seen, {coord, facing}) do
do_search(PriorityQueue.pop(queue), graph, to, seen)
else
queue = queue_next_states(queue, {coord, facing, path, score}, graph)
do_search(PriorityQueue.pop(queue), graph, to, MapSet.put(seen, {coord, facing}))
case Map.get(seen, {coord, facing}) do
{^score, old_path} ->
# We've found an alternate way of getting to this point
# We can cut this path (but record that it existed)
seen = Map.put(seen, {coord, facing}, {score, [path | old_path]})
do_search(PriorityQueue.pop(queue), graph, to, seen)

# We've seen a better way of getting to this point (skip)
{old_score, _old_path} when old_score < score ->
do_search(PriorityQueue.pop(queue), graph, to, seen)

_ ->
# Either the first time we've seen this coordinate, or this is a better way
# of getting to it (actions are the same)
seen = Map.put(seen, {coord, facing}, {score, path})
queue = queue_next_states(queue, {coord, facing, path, score}, graph)
do_search(PriorityQueue.pop(queue), graph, to, seen)
end
end
end

# This feels rather unnecessary but I can't think of a better way to do it
# A BFS over all of the points in the path to build all of the ways we got there
defp get_all_paths(path, seen) do
do_get_all_paths(path, [], MapSet.new(), seen)
end

defp do_get_all_paths([], [], path, _seen), do: path

defp do_get_all_paths([], next, path, seen) do
do_get_all_paths(List.flatten(next), [], path, seen)
end

defp do_get_all_paths([{coord, _} = one | rest], next, path, seen) do
if MapSet.member?(path, coord) do
do_get_all_paths(rest, next, path, seen)
else
case Map.fetch(seen, one) do
:error ->
do_get_all_paths(rest, next, MapSet.put(path, coord), seen)

{:ok, {_score, points}} ->
do_get_all_paths(rest, [points | next], MapSet.put(path, coord), seen)
end
end
end
Expand All @@ -64,5 +104,5 @@ defmodule Y2024.Day16 do
end

def part1_verify, do: input() |> parse_input() |> part1()
# def part2_verify, do: input() |> parse_input() |> part2()
def part2_verify, do: input() |> parse_input() |> part2()
end
7 changes: 6 additions & 1 deletion test/y2024/day16_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,13 @@ defmodule Y2024.Day16Test do
assert test_data("sample2") |> Day16.parse_input() |> Day16.part1() == 11048
end

test "part2" do
assert test_data("sample1") |> Day16.parse_input() |> Day16.part2() == 45
assert test_data("sample2") |> Day16.parse_input() |> Day16.part2() == 64
end

test "verification, part 1", do: assert(Day16.part1_verify() == 91464)
# test "verification, part 2", do: assert(Day16.part2_verify() == "update or delete me")
test "verification, part 2", do: assert(Day16.part2_verify() == 494)

def test_data(name), do: File.read!("test/y2024/input/#{name}.txt")
end

0 comments on commit d825939

Please sign in to comment.