Solved 2024/18 P1
This commit is contained in:
parent
d119956812
commit
2674f6e723
170
2024/16/16.md
Normal file
170
2024/16/16.md
Normal 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
2024/16/solution.py
Normal file
87
2024/16/solution.py
Normal 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')
|
144
2024/18/18.md
Normal file
144
2024/18/18.md
Normal file
@ -0,0 +1,144 @@
|
||||
## \-\-- Day 18: RAM Run \-\--
|
||||
|
||||
You and The Historians look a lot more pixelated than you remember.
|
||||
You\'re [inside a computer](/2017/day/2) at the North Pole!
|
||||
|
||||
Just as you\'re about to check out your surroundings, a program runs up
|
||||
to you. \"This region of memory isn\'t safe! The User misunderstood what
|
||||
a [pushdown
|
||||
automaton](https://en.wikipedia.org/wiki/Pushdown_automaton)
|
||||
is and their algorithm is pushing whole *bytes* down on top of us!
|
||||
Run!\"
|
||||
|
||||
The algorithm is fast - it\'s going to cause a byte to fall into your
|
||||
memory space once every
|
||||
[nanosecond](https://www.youtube.com/watch?v=9eyFDBPk4Yw)!
|
||||
Fortunately, you\'re *faster*, and by quickly scanning the algorithm,
|
||||
you create a *list of which bytes will fall* (your puzzle input) in the
|
||||
order they\'ll land in your memory space.
|
||||
|
||||
Your memory space is a two-dimensional grid with coordinates that range
|
||||
from `0` to `70` both horizontally and vertically. However, for the sake
|
||||
of example, suppose you\'re on a smaller grid with coordinates that
|
||||
range from `0` to `6` and the following list of incoming byte positions:
|
||||
|
||||
5,4
|
||||
4,2
|
||||
4,5
|
||||
3,0
|
||||
2,1
|
||||
6,3
|
||||
2,4
|
||||
1,5
|
||||
0,6
|
||||
3,3
|
||||
2,6
|
||||
5,1
|
||||
1,2
|
||||
5,5
|
||||
2,5
|
||||
6,5
|
||||
1,4
|
||||
0,4
|
||||
6,4
|
||||
1,1
|
||||
6,1
|
||||
1,0
|
||||
0,5
|
||||
1,6
|
||||
2,0
|
||||
|
||||
Each byte position is given as an `X,Y` coordinate, where `X` is the
|
||||
distance from the left edge of your memory space and `Y` is the distance
|
||||
from the top edge of your memory space.
|
||||
|
||||
You and The Historians are currently in the top left corner of the
|
||||
memory space (at `0,0`) and need to reach the exit in the bottom right
|
||||
corner (at `70,70` in your memory space, but at `6,6` in this example).
|
||||
You\'ll need to simulate the falling bytes to plan out where it will be
|
||||
safe to run; for now, simulate just the first few bytes falling into
|
||||
your memory space.
|
||||
|
||||
As bytes fall into your memory space, they make that coordinate
|
||||
*corrupted*. Corrupted memory coordinates cannot be entered by you or
|
||||
The Historians, so you\'ll need to plan your route carefully. You also
|
||||
cannot leave the boundaries of the memory space; your only hope is to
|
||||
reach the exit.
|
||||
|
||||
In the above example, if you were to draw the memory space after the
|
||||
first `12` bytes have fallen (using `.` for safe and `#` for corrupted),
|
||||
it would look like this:
|
||||
|
||||
...#...
|
||||
..#..#.
|
||||
....#..
|
||||
...#..#
|
||||
..#..#.
|
||||
.#..#..
|
||||
#.#....
|
||||
|
||||
You can take steps up, down, left, or right. After just 12 bytes have
|
||||
corrupted locations in your memory space, the shortest path from the top
|
||||
left corner to the exit would take `22` steps. Here (marked with `O`) is
|
||||
one such path:
|
||||
|
||||
OO.#OOO
|
||||
.O#OO#O
|
||||
.OOO#OO
|
||||
...#OO#
|
||||
..#OO#.
|
||||
.#.O#..
|
||||
#.#OOOO
|
||||
|
||||
Simulate the first kilobyte (`1024` bytes) falling onto your memory
|
||||
space. Afterward, *what is the minimum number of steps needed to reach
|
||||
the exit?*
|
||||
|
||||
Your puzzle answer was `294`.
|
||||
|
||||
The first half of this puzzle is complete! It provides one gold star: \*
|
||||
|
||||
## \-\-- Part Two \-\-- {#part2}
|
||||
|
||||
The Historians aren\'t as used to moving around in this pixelated
|
||||
universe as you are. You\'re afraid they\'re not going to be fast enough
|
||||
to make it to the exit before the path is completely blocked.
|
||||
|
||||
To determine how fast everyone needs to go, you need to determine *the
|
||||
first byte that will cut off the path to the exit*.
|
||||
|
||||
In the above example, after the byte at `1,1` falls, there is still a
|
||||
path to the exit:
|
||||
|
||||
O..#OOO
|
||||
O##OO#O
|
||||
O#OO#OO
|
||||
OOO#OO#
|
||||
###OO##
|
||||
.##O###
|
||||
#.#OOOO
|
||||
|
||||
However, after adding the very next byte (at `6,1`), there is no longer
|
||||
a path to the exit:
|
||||
|
||||
...#...
|
||||
.##..##
|
||||
.#..#..
|
||||
...#..#
|
||||
###..##
|
||||
.##.###
|
||||
#.#....
|
||||
|
||||
So, in this example, the coordinates of the first byte that prevents the
|
||||
exit from being reachable are `6,1`.
|
||||
|
||||
Simulate more of the bytes that are about to corrupt your memory space.
|
||||
*What are the coordinates of the first byte that will prevent the exit
|
||||
from being reachable from your starting position?* (Provide the answer
|
||||
as two integers separated by a comma with no other characters.)
|
||||
|
||||
Answer:
|
||||
|
||||
Although it hasn\'t changed, you can still [get your puzzle
|
||||
input](18/input).
|
||||
|
76
2024/18/solution.py
Normal file
76
2024/18/solution.py
Normal file
@ -0,0 +1,76 @@
|
||||
#!/bin/python3
|
||||
import sys,time,re
|
||||
from pprint import pprint
|
||||
sys.path.insert(0, '../../')
|
||||
from fred import list2int,get_re,nprint,lprint,loadFile,bfs,get_value_in_direction,addTuples,grid_valid
|
||||
start_time = time.time()
|
||||
|
||||
input_f = 'input'
|
||||
|
||||
#########################################
|
||||
# #
|
||||
# Part 1 #
|
||||
# #
|
||||
#########################################
|
||||
def part1():
|
||||
instructions = []
|
||||
grid = []
|
||||
w = 70
|
||||
h = 70
|
||||
end = (h,w)
|
||||
start = (0,0)
|
||||
with open(input_f) as file:
|
||||
for line in file:
|
||||
l = list2int(line.rstrip().split(','))
|
||||
instructions.append((l[0],l[1]))
|
||||
#print(instructions)
|
||||
|
||||
grid = [[ '.' for x in range(0,w+1)] for y in range(0,h+1)]
|
||||
|
||||
for i in range(1024):
|
||||
x = instructions[i]
|
||||
grid[x[0]][x[1]] = '#'
|
||||
|
||||
|
||||
|
||||
def is_goal(node):
|
||||
#print(node)
|
||||
|
||||
return True if node == end else False
|
||||
|
||||
def get_neighbors(node):
|
||||
directions = ['up','down','left','right']
|
||||
offsets = {
|
||||
'up': (-1, 0),
|
||||
'down': (1, 0),
|
||||
'left': (0, -1),
|
||||
'right': (0, 1),
|
||||
}
|
||||
neighbors = []
|
||||
|
||||
# Loop through all the directions
|
||||
for d in directions:
|
||||
tmp = addTuples(offsets[d],node)
|
||||
if get_value_in_direction(grid,node,d) != '#' and grid_valid(tmp[0],tmp[1],grid):
|
||||
neighbors.append((tmp[0],tmp[1]))
|
||||
# Return the list of valid neighbors
|
||||
return neighbors
|
||||
|
||||
goal_nodes, path = bfs((0,0),is_goal,get_neighbors)
|
||||
print(goal_nodes)
|
||||
return len(path[goal_nodes[0]])-1
|
||||
|
||||
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')
|
Binary file not shown.
125
fred.py
125
fred.py
@ -2,6 +2,17 @@ import sys,re,heapq
|
||||
from itertools import permutations
|
||||
from termcolor import colored
|
||||
|
||||
def findInGrid(grid:list,x:str) -> set:
|
||||
"""Find the location of a character in a grid.
|
||||
|
||||
|
||||
"""
|
||||
|
||||
for r,row in enumerate(grid):
|
||||
for c, char in enumerate(row):
|
||||
if char == x:
|
||||
return (r,c)
|
||||
|
||||
def loadFile(input_f):
|
||||
"""
|
||||
Loads a file and returns its lines as a list.
|
||||
@ -179,7 +190,6 @@ def dprint(graph: dict):
|
||||
except Exception as e:
|
||||
raise RuntimeError(f"An error occurred while printing the dictionary: {e}")
|
||||
|
||||
|
||||
def lprint(x: str, log: bool):
|
||||
"""
|
||||
Prints a string if logging is enabled.
|
||||
@ -271,7 +281,10 @@ def nprint(grid, cur: set = None, sign: str = None, positions:list = None):
|
||||
for idx, i in enumerate(grid):
|
||||
for jdx, j in enumerate(i):
|
||||
if jdx == 0:
|
||||
prepend = str(idx)+' '
|
||||
if idx < 10:
|
||||
prepend = ' '+str(idx)+' '
|
||||
else:
|
||||
prepend = ''+str(idx)+' '
|
||||
else:
|
||||
prepend = ''
|
||||
|
||||
@ -286,7 +299,7 @@ def nprint(grid, cur: set = None, sign: str = None, positions:list = None):
|
||||
else:
|
||||
if positions is not None:
|
||||
if (idx,jdx) in positions:
|
||||
print(prepend+colored(grid[idx][jdx],'red'),end=' ')
|
||||
print(prepend+colored(grid[idx][jdx],'green'),end=' ')
|
||||
else:
|
||||
print(prepend+grid[idx][jdx], end=' ')
|
||||
else:
|
||||
@ -296,8 +309,26 @@ def nprint(grid, cur: set = None, sign: str = None, positions:list = None):
|
||||
|
||||
for r in range(len(grid[0])):
|
||||
if r == 0:
|
||||
print(' ',end='')
|
||||
print(r,end=' ')
|
||||
print(' 0',end='')
|
||||
|
||||
else:
|
||||
if r%2 == 0:
|
||||
print(r,end='')
|
||||
else:
|
||||
print(' ',end='')
|
||||
print()
|
||||
for r in range(len(grid[0])):
|
||||
if r == 0:
|
||||
print(' ',end='')
|
||||
|
||||
else:
|
||||
if r%2 != 0:
|
||||
print(r,end='')
|
||||
else:
|
||||
if r != 9:
|
||||
print(' ',end='')
|
||||
else:
|
||||
print('',end='')
|
||||
print()
|
||||
|
||||
def list2int(x):
|
||||
@ -647,6 +678,9 @@ def dfs(grid:list, pos:set) -> list:
|
||||
dfs(pos[0], pos[1])
|
||||
return list(visited)
|
||||
|
||||
def manhattan_distance(node, goal):
|
||||
return abs(node[0] - goal[0]) + abs(node[1] - goal[1])
|
||||
|
||||
# Should probably be added to the regular dfs.
|
||||
def flood_fill(cells, pos):
|
||||
"""
|
||||
@ -677,4 +711,83 @@ def flood_fill(cells, pos):
|
||||
dfs(neighbor)
|
||||
|
||||
dfs(pos)
|
||||
return list(visited)
|
||||
return list(visited)
|
||||
|
||||
def create_graph_from_grid(grid, start, end,wall):
|
||||
"""
|
||||
Converts a grid into a graph representation for use in pathfinding algorithms.
|
||||
|
||||
Parameters:
|
||||
- grid: A 2D list representing the grid, where '#' is a wall, and '.' is a path.
|
||||
- start: The coordinates of the start node (row, col).
|
||||
- end: The coordinates of the end node (row, col).
|
||||
- wall: The string of the wall node.
|
||||
|
||||
Returns:
|
||||
- graph: A dictionary where each key is a node (row, col), and the value is a list of tuples.
|
||||
Each tuple represents (neighbor, weight).
|
||||
"""
|
||||
rows, cols = len(grid), len(grid[0])
|
||||
graph = {}
|
||||
|
||||
# Helper to get neighbors
|
||||
def neighbors(r, c):
|
||||
for dr, dc in [(-1, 0), (1, 0), (0, -1), (0, 1)]: # Up, Down, Left, Right
|
||||
nr, nc = r + dr, c + dc
|
||||
if 0 <= nr < rows and 0 <= nc < cols and grid[nr][nc] != '#':
|
||||
yield (nr, nc)
|
||||
|
||||
for r in range(rows):
|
||||
for c in range(cols):
|
||||
if grid[r][c] != '#': # Only process valid cells
|
||||
graph[(r, c)] = [(neighbor, 1) for neighbor in neighbors(r, c)] # Weight is 1
|
||||
|
||||
return graph
|
||||
|
||||
def a_star(graph, start, end, heuristic):
|
||||
"""
|
||||
A* Algorithm to find the shortest path between two nodes in a graph.
|
||||
|
||||
Parameters:
|
||||
- graph: A dictionary where each key is a node, and the value is a list of tuples.
|
||||
Each tuple represents (neighbor, distance).
|
||||
- start: The starting node (row, col).
|
||||
- end: The destination node (row, col).
|
||||
- heuristic: A function that estimates the cost from a node to the end.
|
||||
|
||||
Returns:
|
||||
- path: A list of nodes that represent the shortest path from start to end.
|
||||
- total_cost: The total cost of the shortest path.
|
||||
"""
|
||||
priority_queue = [(0, start)] # (f_score, current_node)
|
||||
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} # Estimated total cost (g + h)
|
||||
f_scores[start] = heuristic(start, end)
|
||||
|
||||
previous_nodes = {node: None for node in graph}
|
||||
|
||||
while priority_queue:
|
||||
current_f_score, current_node = heapq.heappop(priority_queue)
|
||||
|
||||
if current_node == end:
|
||||
break
|
||||
|
||||
for neighbor, weight in graph[current_node]:
|
||||
tentative_g_score = g_scores[current_node] + weight
|
||||
|
||||
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))
|
||||
|
||||
# Reconstruct the path
|
||||
path = []
|
||||
while end is not None:
|
||||
path.append(end)
|
||||
end = previous_nodes[end]
|
||||
path.reverse()
|
||||
|
||||
return path, g_scores[path[-1]]
|
Loading…
Reference in New Issue
Block a user