Solved 2024/18 P1

This commit is contained in:
2024-12-18 07:09:36 +01:00
parent d119956812
commit 2674f6e723
6 changed files with 596 additions and 6 deletions
+170
View File
@@ -0,0 +1,170 @@
## \-\-- Day 16: Reindeer Maze \-\--
It\'s time again for the [Reindeer Olympics](/2015/day/14)! This year,
the big event is the *Reindeer Maze*, where the Reindeer compete for the
*[lowest
score]{title="I would say it's like Reindeer Golf, but knowing Reindeer, it's almost certainly nothing like Reindeer Golf."}*.
You and The Historians arrive to search for the Chief right as the event
is about to start. It wouldn\'t hurt to watch a little, right?
The Reindeer start on the Start Tile (marked `S`) facing *East* and need
to reach the End Tile (marked `E`). They can move forward one tile at a
time (increasing their score by `1` point), but never into a wall (`#`).
They can also rotate clockwise or counterclockwise 90 degrees at a time
(increasing their score by `1000` points).
To figure out the best place to sit, you start by grabbing a map (your
puzzle input) from a nearby kiosk. For example:
###############
#.......#....E#
#.#.###.#.###.#
#.....#.#...#.#
#.###.#####.#.#
#.#.#.......#.#
#.#.#####.###.#
#...........#.#
###.#.#####.#.#
#...#.....#.#.#
#.#.#.###.#.#.#
#.....#...#.#.#
#.###.#.#.#.#.#
#S..#.....#...#
###############
There are many paths through this maze, but taking any of the best paths
would incur a score of only `7036`. This can be achieved by taking a
total of `36` steps forward and turning 90 degrees a total of `7` times:
###############
#.......#....E#
#.#.###.#.###^#
#.....#.#...#^#
#.###.#####.#^#
#.#.#.......#^#
#.#.#####.###^#
#..>>>>>>>>v#^#
###^#.#####v#^#
#>>^#.....#v#^#
#^#.#.###.#v#^#
#^....#...#v#^#
#^###.#.#.#v#^#
#S..#.....#>>^#
###############
Here\'s a second example:
#################
#...#...#...#..E#
#.#.#.#.#.#.#.#.#
#.#.#.#...#...#.#
#.#.#.#.###.#.#.#
#...#.#.#.....#.#
#.#.#.#.#.#####.#
#.#...#.#.#.....#
#.#.#####.#.###.#
#.#.#.......#...#
#.#.###.#####.###
#.#.#...#.....#.#
#.#.#.#####.###.#
#.#.#.........#.#
#.#.#.#########.#
#S#.............#
#################
In this maze, the best paths cost `11048` points; following one such
path would look like this:
#################
#...#...#...#..E#
#.#.#.#.#.#.#.#^#
#.#.#.#...#...#^#
#.#.#.#.###.#.#^#
#>>v#.#.#.....#^#
#^#v#.#.#.#####^#
#^#v..#.#.#>>>>^#
#^#v#####.#^###.#
#^#v#..>>>>^#...#
#^#v###^#####.###
#^#v#>>^#.....#.#
#^#v#^#####.###.#
#^#v#^........#.#
#^#v#^#########.#
#S#>>^..........#
#################
Note that the path shown above includes one 90 degree turn as the very
first move, rotating the Reindeer from facing East to facing North.
Analyze your map carefully. *What is the lowest score a Reindeer could
possibly get?*
Your puzzle answer was `108504`.
The first half of this puzzle is complete! It provides one gold star: \*
## \-\-- Part Two \-\-- {#part2}
Now that you know what the best paths look like, you can figure out the
best spot to sit.
Every non-wall tile (`S`, `.`, or `E`) is equipped with places to sit
along the edges of the tile. While determining which of these tiles
would be the best spot to sit depends on a whole bunch of factors (how
comfortable the seats are, how far away the bathrooms are, whether
there\'s a pillar blocking your view, etc.), the most important factor
is *whether the tile is on one of the best paths through the maze*. If
you sit somewhere else, you\'d miss all the action!
So, you\'ll need to determine which tiles are part of *any* best path
through the maze, including the `S` and `E` tiles.
In the first example, there are `45` tiles (marked `O`) that are part of
at least one of the various best paths through the maze:
###############
#.......#....O#
#.#.###.#.###O#
#.....#.#...#O#
#.###.#####.#O#
#.#.#.......#O#
#.#.#####.###O#
#..OOOOOOOOO#O#
###O#O#####O#O#
#OOO#O....#O#O#
#O#O#O###.#O#O#
#OOOOO#...#O#O#
#O###.#.#.#O#O#
#O..#.....#OOO#
###############
In the second example, there are `64` tiles that are part of at least
one of the best paths:
#################
#...#...#...#..O#
#.#.#.#.#.#.#.#O#
#.#.#.#...#...#O#
#.#.#.#.###.#.#O#
#OOO#.#.#.....#O#
#O#O#.#.#.#####O#
#O#O..#.#.#OOOOO#
#O#O#####.#O###O#
#O#O#..OOOOO#OOO#
#O#O###O#####O###
#O#O#OOO#..OOO#.#
#O#O#O#####O###.#
#O#O#OOOOOOO..#.#
#O#O#O#########.#
#O#OOO..........#
#################
Analyze your map further. *How many tiles are part of at least one of
the best paths through the maze?*
Answer:
Although it hasn\'t changed, you can still [get your puzzle
input](16/input).
+87
View File
@@ -0,0 +1,87 @@
#!/bin/python3
import sys,time,re
from pprint import pprint
sys.path.insert(0, '../../')
from fred import list2int,get_re,nprint,lprint,loadFile,dijkstra,toGrid,dfs,bfs,findInGrid,get_value_in_direction,addTuples,subTuples,create_graph_from_grid,manhattan_distance
start_time = time.time()
input_f = 'input'
#########################################
# #
# Part 1 #
# #
#########################################
def part1():
grid = toGrid(input_f)
start = findInGrid(grid,'S')
end = findInGrid(grid,'E')
print(start,end)
def a_star(graph, start, end, heuristic):
import heapq
# Priority queue stores (f_score, current_node, current_direction)
priority_queue = [(0, start, None)]
g_scores = {node: float('inf') for node in graph} # Cost from start to each node
g_scores[start] = 0
f_scores = {node: float('inf') for node in graph}
f_scores[start] = heuristic(start, end)
previous_nodes = {node: None for node in graph}
while priority_queue:
current_f_score, current_node, previous_direction = heapq.heappop(priority_queue)
if current_node == end:
break
for neighbor, weight in graph[current_node]:
current_direction = (
neighbor[0] - current_node[0], # Row direction (-1, 0, 1)
neighbor[1] - current_node[1] # Col direction (-1, 0, 1)
)
turn_cost = 1000 if previous_direction and current_direction != previous_direction else 0
tentative_g_score = g_scores[current_node] + weight + turn_cost
if tentative_g_score < g_scores[neighbor]:
g_scores[neighbor] = tentative_g_score
f_scores[neighbor] = tentative_g_score + heuristic(neighbor, end)
previous_nodes[neighbor] = current_node
heapq.heappush(priority_queue, (f_scores[neighbor], neighbor, current_direction))
path = []
while end is not None:
path.append(end)
end = previous_nodes[end]
path.reverse()
return path, g_scores[path[-1]]
graph = create_graph_from_grid(grid,start,end,'#')
path, score = a_star(graph,start,end,manhattan_distance)
#print(path)
return score #Instead of implementing edge cases, just try with turning (adding 1000) to the result, if that's wrong, just don't add it. I got it right by not turning right away.
start_time = time.time()
print('Part 1:',part1(), '\t\t', round((time.time() - start_time)*1000), 'ms')
#########################################
# #
# Part 2 #
# #
#########################################
def part2():
return
start_time = time.time()
print('Part 2:',part2(), '\t\t', round((time.time() - start_time)*1000), 'ms')