Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
/__pycache__
*.pyc

# Text editor temp files
*~
\#*\#
5 changes: 4 additions & 1 deletion FibonacciHeap.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ def removeminimum(self):
if self.minnode == None:
raise AssertionError("Cannot remove from an empty heap")

removed_node = self.minnode
self.count -= 1

# 1: Assign all old root children as new roots
Expand All @@ -113,7 +114,7 @@ def removeminimum(self):
if self.count != 0:
raise AssertionError("Heap error: Expected 0 keys, count is " + str(self.count))
self.minnode = None
return
return removed_node

# 2.2: Merge any roots with the same degree
logsize = 100
Expand Down Expand Up @@ -154,6 +155,8 @@ def removeminimum(self):

maxdegree = newmaxdegree

return removed_node


def decreasekey(self, node, newkey):
if newkey > node.key:
Expand Down
190 changes: 97 additions & 93 deletions astar.py
Original file line number Diff line number Diff line change
@@ -1,99 +1,103 @@
from FibonacciHeap import FibHeap
from priority_queue import FibPQ, HeapPQ, QueuePQ

# This implementatoin of A* is almost identical to the Dijkstra implementation. So for clarity I've removed all comments, and only added those
# Specifically showing the difference between dijkstra and A*

def solve(maze):
width = maze.width
total = maze.width * maze.height

start = maze.start
startpos = start.Position
end = maze.end
endpos = end.Position

visited = [False] * total
prev = [None] * total

infinity = float("inf")
distances = [infinity] * total

unvisited = FibHeap()

nodeindex = [None] * total

distances[start.Position[0] * width + start.Position[1]] = 0
startnode = FibHeap.Node(0, start)
nodeindex[start.Position[0] * width + start.Position[1]] = startnode
unvisited.insert(startnode)

count = 0
completed = False

while unvisited.count > 0:
count += 1

n = unvisited.minimum()
unvisited.removeminimum()

u = n.value
upos = u.Position
uposindex = upos[0] * width + upos[1]

if distances[uposindex] == infinity:
break

if upos == endpos:
completed = True
break

for v in u.Neighbours:
if v != None:
vpos = v.Position
vposindex = vpos[0] * width + vpos[1]

if visited[vposindex] == False:
d = abs(vpos[0] - upos[0]) + abs(vpos[1] - upos[1])

# New path cost to v is distance to u + extra. Some descriptions of A* call this the g cost.
# New distance is the distance of the path from the start, through U, to V.
newdistance = distances[uposindex] + d

# A* includes a remaining cost, the f cost. In this case we use manhattan distance to calculate the distance from
# V to the end. We use manhattan again because A* works well when the g cost and f cost are balanced.
# https://en.wikipedia.org/wiki/Taxicab_geometry
remaining = abs(vpos[0] - endpos[0]) + abs(vpos[1] - endpos[1])

# Notice that we don't include f cost in this first check. We want to know that the path *to* our node V is shortest
if newdistance < distances[vposindex]:
vnode = nodeindex[vposindex]

if vnode == None:
# V goes into the priority queue with a cost of g + f. So if it's moving closer to the end, it'll get higher
# priority than some other nodes. The order we visit nodes is a trade-off between a short path, and moving
# closer to the goal.
vnode = FibHeap.Node(newdistance + remaining, v)
unvisited.insert(vnode)
nodeindex[vposindex] = vnode
# The distance *to* the node remains just g, no f included.
distances[vposindex] = newdistance
prev[vposindex] = u
else:
# As above, we decrease the node since we've found a new path. But we include the f cost, the distance remaining.
unvisited.decreasekey(vnode, newdistance + remaining)
# The distance *to* the node remains just g, no f included.
distances[vposindex] = newdistance
prev[vposindex] = u


visited[uposindex] = True

from collections import deque

path = deque()
current = end
while (current != None):
path.appendleft(current)
current = prev[current.Position[0] * width + current.Position[1]]

return [path, [count, len(path), completed]]
width = maze.width
total = maze.width * maze.height

start = maze.start
startpos = start.Position
end = maze.end
endpos = end.Position

visited = [False] * total
prev = [None] * total

infinity = float("inf")
distances = [infinity] * total

# The priority queue. There are multiple implementations in priority_queue.py
# unvisited = FibHeap()
unvisited = HeapPQ()
# unvisited = FibPQ()
# unvisited = QueuePQ()

nodeindex = [None] * total

distances[start.Position[0] * width + start.Position[1]] = 0
startnode = FibHeap.Node(0, start)
nodeindex[start.Position[0] * width + start.Position[1]] = startnode
unvisited.insert(startnode)

count = 0
completed = False

while len(unvisited) > 0:
count += 1

n = unvisited.removeminimum()

u = n.value
upos = u.Position
uposindex = upos[0] * width + upos[1]

if distances[uposindex] == infinity:
break

if upos == endpos:
completed = True
break

for v in u.Neighbours:
if v != None:
vpos = v.Position
vposindex = vpos[0] * width + vpos[1]

if visited[vposindex] == False:
d = abs(vpos[0] - upos[0]) + abs(vpos[1] - upos[1])

# New path cost to v is distance to u + extra. Some descriptions of A* call this the g cost.
# New distance is the distance of the path from the start, through U, to V.
newdistance = distances[uposindex] + d

# A* includes a remaining cost, the f cost. In this case we use manhattan distance to calculate the distance from
# V to the end. We use manhattan again because A* works well when the g cost and f cost are balanced.
# https://en.wikipedia.org/wiki/Taxicab_geometry
remaining = abs(vpos[0] - endpos[0]) + abs(vpos[1] - endpos[1])

# Notice that we don't include f cost in this first check. We want to know that the path *to* our node V is shortest
if newdistance < distances[vposindex]:
vnode = nodeindex[vposindex]

if vnode == None:
# V goes into the priority queue with a cost of g + f. So if it's moving closer to the end, it'll get higher
# priority than some other nodes. The order we visit nodes is a trade-off between a short path, and moving
# closer to the goal.
vnode = FibHeap.Node(newdistance + remaining, v)
unvisited.insert(vnode)
nodeindex[vposindex] = vnode
# The distance *to* the node remains just g, no f included.
distances[vposindex] = newdistance
prev[vposindex] = u
else:
# As above, we decrease the node since we've found a new path. But we include the f cost, the distance remaining.
unvisited.decreasekey(vnode, newdistance + remaining)
# The distance *to* the node remains just g, no f included.
distances[vposindex] = newdistance
prev[vposindex] = u


visited[uposindex] = True

from collections import deque

path = deque()
current = end
while (current != None):
path.appendleft(current)
current = prev[current.Position[0] * width + current.Position[1]]

return [path, [count, len(path), completed]]
58 changes: 29 additions & 29 deletions breadthfirst.py
Original file line number Diff line number Diff line change
@@ -1,42 +1,42 @@
from collections import deque

def solve(maze):
start = maze.start
end = maze.end
start = maze.start
end = maze.end

width = maze.width
width = maze.width

queue = deque([start])
shape = (maze.height, maze.width)
prev = [None] * (maze.width * maze.height)
visited = [False] * (maze.width * maze.height)
queue = deque([start])
shape = (maze.height, maze.width)
prev = [None] * (maze.width * maze.height)
visited = [False] * (maze.width * maze.height)

count = 0
count = 0

completed = False
completed = False

visited[start.Position[0] * width + start.Position[1]] = True
visited[start.Position[0] * width + start.Position[1]] = True

while queue:
count += 1
current = queue.pop()
while queue:
count += 1
current = queue.pop()

if current == end:
completed = True
break
if current == end:
completed = True
break

for n in current.Neighbours:
if n != None:
npos = n.Position[0] * width + n.Position[1]
if visited[npos] == False:
queue.appendleft(n)
visited[npos] = True
prev[npos] = current
for n in current.Neighbours:
if n != None:
npos = n.Position[0] * width + n.Position[1]
if visited[npos] == False:
queue.appendleft(n)
visited[npos] = True
prev[npos] = current

path = deque()
current = end
while (current != None):
path.appendleft(current)
current = prev[current.Position[0] * width + current.Position[1]]
path = deque()
current = end
while (current != None):
path.appendleft(current)
current = prev[current.Position[0] * width + current.Position[1]]

return [path, [count, len(path), completed]]
return [path, [count, len(path), completed]]
74 changes: 37 additions & 37 deletions depthfirst.py
Original file line number Diff line number Diff line change
@@ -1,40 +1,40 @@
from collections import deque

def solve(maze):
start = maze.start
end = maze.end
width = maze.width
stack = deque([start])
shape = (maze.height, maze.width)
prev = [None] * (maze.width * maze.height)
visited = [False] * (maze.width * maze.height)
count = 0

completed = False
while stack:
count += 1
current = stack.pop()

if current == end:
completed = True
break

visited[current.Position[0] * width + current.Position[1]] = True

#import code
#code.interact(local=locals())

for n in current.Neighbours:
if n != None:
npos = n.Position[0] * width + n.Position[1]
if visited[npos] == False:
stack.append(n)
prev[npos] = current

path = deque()
current = end
while (current != None):
path.appendleft(current)
current = prev[current.Position[0] * width + current.Position[1]]

return [path, [count, len(path), completed]]
start = maze.start
end = maze.end
width = maze.width
stack = deque([start])
shape = (maze.height, maze.width)
prev = [None] * (maze.width * maze.height)
visited = [False] * (maze.width * maze.height)
count = 0

completed = False
while stack:
count += 1
current = stack.pop()

if current == end:
completed = True
break

visited[current.Position[0] * width + current.Position[1]] = True

#import code
#code.interact(local=locals())

for n in current.Neighbours:
if n != None:
npos = n.Position[0] * width + n.Position[1]
if visited[npos] == False:
stack.append(n)
prev[npos] = current

path = deque()
current = end
while (current != None):
path.appendleft(current)
current = prev[current.Position[0] * width + current.Position[1]]

return [path, [count, len(path), completed]]
Loading