Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added two methods on Maze.Node class and refined the argsparse #26

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
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
17 changes: 17 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Python: Current File",
"type": "python",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal",
"justMyCode": true,
"args": ["-m", "dijkstra", "-i", "small.png"]
}
]
}
29 changes: 21 additions & 8 deletions FibonacciHeap.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,18 @@ def __init__(self, key, value):
self.parent = self.child = None
self.previous = self.next = self

def __lt__(self, other) -> bool:
if self.key < other.key:
return True
else:
return False

def __le__(self, other) -> bool:
if self.key <= other.key:
return True
else:
return False

def issingle(self):
return self == self.next

Expand All @@ -23,7 +35,6 @@ def insert(self, node):
self.next = node
node.previous = self


def remove(self):
self.previous.next = self.next
self.next.previous = self.previous
Expand All @@ -40,11 +51,13 @@ def addchild(self, node):

def removechild(self, node):
if node.parent != self:
raise AssertionError("Cannot remove child from a node that is not its parent")
raise AssertionError(
"Cannot remove child from a node that is not its parent")

if node.issingle():
if self.child != node:
raise AssertionError("Cannot remove a node that is not a child")
raise AssertionError(
"Cannot remove a node that is not a child")
self.child = None
else:
if self.child == node:
Expand All @@ -56,7 +69,7 @@ def removechild(self, node):
self.degree -= 1
#### End of Node Class ####

def __init__ (self):
def __init__(self):
self.minnode = None
self.count = 0
self.maxdegree = 0
Expand Down Expand Up @@ -112,7 +125,8 @@ def removeminimum(self):
# 2.1: If we have removed the last key
if self.minnode.next == self.minnode:
if self.count != 0:
raise AssertionError("Heap error: Expected 0 keys, count is " + str(self.count))
raise AssertionError(
"Heap error: Expected 0 keys, count is " + str(self.count))
self.minnode = None
return removed_node

Expand Down Expand Up @@ -146,7 +160,7 @@ def removeminimum(self):
# 3: Remove current root and find new minnode
self.minnode = None
newmaxdegree = 0
for d in range (0,logsize):
for d in range(0, logsize):
if degreeroots[d] != None:
degreeroots[d].next = degreeroots[d].previous = degreeroots[d]
self._insertnode(degreeroots[d])
Expand All @@ -157,11 +171,10 @@ def removeminimum(self):

return removed_node


def decreasekey(self, node, newkey):
if newkey > node.key:
#import code
#code.interact(local=locals())
# code.interact(local=locals())
raise AssertionError("Cannot decrease a key to a greater value")
elif newkey == node.key:
return
Expand Down
Binary file added examples/small-copy.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 23 additions & 11 deletions mazes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,18 @@ def __init__(self, position):
self.Neighbours = [None, None, None, None]
#self.Weights = [0, 0, 0, 0]

def __lt__(self, other) -> bool:
if self.Position < other.Position:
return True
else:
return False

def __le__(self, other) -> bool:
if self.Position <= other.Position:
return True
else:
return False

def __init__(self, im):

width = im.size[0]
Expand All @@ -19,15 +31,15 @@ def __init__(self, im):
count = 0

# Start row
for x in range (1, width - 1):
for x in range(1, width - 1):
if data[x] > 0:
self.start = Maze.Node((0,x))
self.start = Maze.Node((0, x))
topnodes[x] = self.start
count += 1
break

for y in range (1, height - 1):
#print ("row", str(y)) # Uncomment this line to keep a track of row progress
for y in range(1, height - 1):
# print ("row", str(y)) # Uncomment this line to keep a track of row progress

rowoffset = y * width
rowaboveoffset = rowoffset - width
Expand All @@ -40,7 +52,7 @@ def __init__(self, im):

leftnode = None

for x in range (1, width - 1):
for x in range(1, width - 1):
# Move prev, current and next onwards. This way we read from the image once per pixel, marginal optimisation
prv = cur
cur = nxt
Expand All @@ -57,29 +69,29 @@ def __init__(self, im):
# PATH PATH PATH
# Create node only if paths above or below
if data[rowaboveoffset + x] > 0 or data[rowbelowoffset + x] > 0:
n = Maze.Node((y,x))
n = Maze.Node((y, x))
leftnode.Neighbours[1] = n
n.Neighbours[3] = leftnode
leftnode = n
else:
# PATH PATH WALL
# Create path at end of corridor
n = Maze.Node((y,x))
n = Maze.Node((y, x))
leftnode.Neighbours[1] = n
n.Neighbours[3] = leftnode
leftnode = None
else:
if nxt == True:
# WALL PATH PATH
# Create path at start of corridor
n = Maze.Node((y,x))
n = Maze.Node((y, x))
leftnode = n
else:
# WALL PATH WALL
# Create node only if in dead end
if (data[rowaboveoffset + x] == 0) or (data[rowbelowoffset + x] == 0):
#print ("Create Node in dead end")
n = Maze.Node((y,x))
n = Maze.Node((y, x))

# If node isn't none, we can assume we can connect N-S somewhere
if n != None:
Expand All @@ -99,9 +111,9 @@ def __init__(self, im):

# End row
rowoffset = (height - 1) * width
for x in range (1, width - 1):
for x in range(1, width - 1):
if data[rowoffset + x] > 0:
self.end = Maze.Node((height - 1,x))
self.end = Maze.Node((height - 1, x))
t = topnodes[x]
t.Neighbours[2] = self.end
self.end.Neighbours[0] = t
Expand Down
6 changes: 4 additions & 2 deletions priority_queue.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

from FibonacciHeap import FibHeap
import heapq
import Queue
from queue import Queue


class PriorityQueue():
__metaclass__ = ABCMeta
Expand All @@ -24,6 +25,7 @@ def removeminimum(self): pass
@abstractmethod
def decreasekey(self, node, new_priority): pass


class FibPQ(PriorityQueue):
def __init__(self):
self.heap = FibHeap()
Expand All @@ -43,6 +45,7 @@ def removeminimum(self):
def decreasekey(self, node, new_priority):
self.heap.decreasekey(node, new_priority)


class HeapPQ(PriorityQueue):
def __init__(self):
self.pq = []
Expand Down Expand Up @@ -127,4 +130,3 @@ def decreasekey(self, node, new_priority):
self.remove(node)
node.key = new_priority
self.insert(node)

Binary file added small-copy.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added small.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added smallastarOP.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added smalldepthfirstOP.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added smalldijkstraOP.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
56 changes: 34 additions & 22 deletions solve.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,33 @@
import argparse
from PIL import Image
import time
from mazes import Maze
from factory import SolverFactory
Image.MAX_IMAGE_PIXELS = None

# usage: (method to be used) (ip image) (op image)
#! python3 solve.py -m depthfirst/breadthfirst -i small.png -o small-copy.png

# Read command line arguments - the python argparse class is convenient here.
import argparse

def solve(factory, method, input_file, output_file):

def solve(factory, method: str = "breadthfirst", input_file: str = None, output_file: str = None):
# Load Image
print ("Loading Image")
print("Loading Image")
im = Image.open(input_file)

# Create the maze (and time it) - for many mazes this is more time consuming than solving the maze
print ("Creating Maze")
print("Creating Maze")
t0 = time.time()
maze = Maze(im)
t1 = time.time()
print ("Node Count:", maze.count)
print("Node Count:", maze.count)
total = t1-t0
print ("Time elapsed:", total, "\n")
print("Time elapsed:", total, "\n")

# Create and run solver
[title, solver] = factory.createsolver(method)
print ("Starting Solve:", title)
print("Starting Solve:", title)

t0 = time.time()
[result, stats] = solver(maze)
Expand All @@ -32,12 +36,12 @@ def solve(factory, method, input_file, output_file):
total = t1-t0

# Print solve stats
print ("Nodes explored: ", stats[0])
print("Nodes explored: ", stats[0])
if (stats[2]):
print ("Path found, length", stats[1])
print("Path found, length", stats[1])
else:
print ("No Path Found")
print ("Time elapsed: ", total, "\n")
print("No Path Found")
print("Time elapsed: ", total, "\n")

"""
Create and save the output image.
Expand All @@ -46,7 +50,7 @@ def solve(factory, method, input_file, output_file):
blue and red depending on how far down the path this section is.
"""

print ("Saving Image")
print("Saving Image")
im = im.convert('RGB')
impixels = im.load()

Expand All @@ -64,27 +68,35 @@ def solve(factory, method, input_file, output_file):

if a[0] == b[0]:
# Ys equal - horizontal line
for x in range(min(a[1],b[1]), max(a[1],b[1])):
impixels[x,a[0]] = px
for x in range(min(a[1], b[1]), max(a[1], b[1])):
impixels[x, a[0]] = px
elif a[1] == b[1]:
# Xs equal - vertical line
for y in range(min(a[0],b[0]), max(a[0],b[0]) + 1):
impixels[a[1],y] = px
for y in range(min(a[0], b[0]), max(a[0], b[0]) + 1):
impixels[a[1], y] = px

if not output_file:
output_file = input_file.split(".")
output_file[0] = output_file[0] + method + "OP"
output_file = ".".join(output_file)

im.save(output_file)


def main():
sf = SolverFactory()
parser = argparse.ArgumentParser()
parser = argparse.ArgumentParser(
description="Maze Solving using Image Processing")
parser.add_argument("-m", "--method", nargs='?', const=sf.Default, default=sf.Default,
choices=sf.Choices)
parser.add_argument("input_file")
parser.add_argument("output_file")
choices=sf.Choices, help="Method to be used on solving the maze")
parser.add_argument("-i", "--input", required=True,
help="Image of the maze input file")
parser.add_argument("-o", "--output", help="Solved output maze file")
args = parser.parse_args()

solve(sf, args.method, args.input_file, args.output_file)
# NOTE: while debugging, use F5 key,cause the debug button won't pass the arguments to the program
solve(sf, args.method, args.input, args.output)


if __name__ == "__main__":
main()