Advertisement
Guest User

Alpha's pixel maze solving algorithm

a guest
Jun 29th, 2018
333
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 8.93 KB | None | 0 0
  1. from PIL import Image
  2. import math
  3. import sys
  4. import time
  5. import pickle
  6. import os
  7.  
  8. class Node():
  9.     def __init__(self, index, pos, paths, distancefromexit):    # Node(index, (x,y), [up,right,down,left])
  10.         self.index = index
  11.         self.pos = pos
  12.         self.paths = paths
  13.         self.distancefromexit = distancefromexit
  14.         self.connections = []
  15.         self.deadend = False
  16.  
  17. ##########
  18.  
  19. whitepix = (255,255,255)
  20. blackpix = (0,0,0)
  21. redpix = (255,0,0)
  22. greenpix = (0,255,0)
  23.  
  24. ##########
  25.        
  26. def getIndexOfNodeInNodelistByItsNodeIndex(nodeindex, nodelist):
  27.     for i in range(len(nodelist)):
  28.         if nodelist[i].index == nodeindex:
  29.             return i
  30.     return None
  31.    
  32. def getDistanceWithCoords(A, B):
  33.     return math.sqrt( math.pow((B[0] - A[0]),2) + math.pow((B[1] - A[1]),2) )
  34.    
  35.  
  36. if __name__ == "__main__":
  37.        
  38.     imagename = input("Enter the maze image name: ")
  39.  
  40.     maze = Image.open(imagename)
  41.     maze = maze.convert('RGB')
  42.     mazepix = maze.load()
  43.     nodelist = []
  44.    
  45.     print(maze.size)
  46.    
  47.     starttime = time.time()
  48.    
  49.     if os.path.isfile(imagename + ".lnodelist"):
  50.         print("Found lnodelist file for current maze. Loading it!")
  51.         with open(imagename + ".lnodelist", 'rb') as handle:
  52.             nodelist = pickle.load(handle)
  53.             handle.close()
  54.     else:
  55.         print("Generating nodes...")
  56.        
  57.         # print("Looking for ending node")
  58.         # Locate the ending node
  59.         for i in range(maze.size[0]):
  60.             # print(mazepix[i, maze.size[1]-1])
  61.             if mazepix[i, maze.size[1]-1] == whitepix:
  62.                 exitnodecoords = (i, maze.size[1]-1)
  63.                 nodelist.append(Node(0, exitnodecoords, [True, False, False, False], 0))
  64.                 # print("New node added")
  65.                 break
  66.                
  67.  
  68.         # Locate the entry node
  69.         # print("Looking for entry node")
  70.         for i in range(maze.size[0]):
  71.             if mazepix[i, 0] == whitepix:
  72.                 entrynodecoords = (i, 0)
  73.                 nodelist.append(Node(1, entrynodecoords, [False, False, True, False], getDistanceWithCoords( entrynodecoords,exitnodecoords ) ))
  74.                 # print("New node added")
  75.                 break
  76.                
  77.                
  78.         #from there, build a list of all nodes/crossroads  
  79.            
  80.         previouspix = blackpix
  81.         nodecounter = 2
  82.         for b in range(1,maze.size[1]):
  83.             for a in range(maze.size[0]):
  84.                 # print("Processing [%d,%d]"%(a,b))
  85.                 if mazepix[a,b] == blackpix:
  86.                     previouspix = blackpix
  87.                 elif mazepix[a,b] == whitepix:
  88.                     if previouspix == blackpix:
  89.                         if mazepix[a+1,b] == whitepix:
  90.                             newnode = Node(nodecounter, (a,b), [False, True, False, False] , getDistanceWithCoords( (a,b),exitnodecoords ) )
  91.                             nodecounter += 1
  92.                             if mazepix[a,b+1] == whitepix:
  93.                                 newnode.paths[2] = True
  94.                             if mazepix[a,b-1] == whitepix:
  95.                                 newnode.paths[0] = True
  96.                             nodelist.append(newnode)
  97.                             # print("New node added")
  98.                            
  99.                     elif previouspix == whitepix:
  100.                         cangoUp = False
  101.                         cangoDown = False
  102.                         cangoRight = False
  103.                         if mazepix[a,b-1] == whitepix:
  104.                             cangoUp = True
  105.                         if mazepix[a,b+1] == whitepix:
  106.                             cangoDown = True
  107.                         if mazepix[a+1,b] == whitepix:
  108.                             cangoRight = True
  109.                         if cangoUp or cangoDown:
  110.                             newnode = Node(nodecounter, (a,b), [cangoUp, cangoRight, cangoDown, True], getDistanceWithCoords( (a,b),exitnodecoords ))
  111.                             nodecounter += 1
  112.                             nodelist.append(newnode)
  113.                             # print("New node added")
  114.                     previouspix = whitepix         
  115.                        
  116.         # for node in nodelist:
  117.             # print("Creating node %d, at %s : %s"%( node.index, str(node.pos), str(node.paths) ) )
  118.             # print("  %f pixels from exit"%node.distancefromexit)
  119.             # mazepix[node.pos[0], node.pos[1]] = redpix
  120.         # maze.save('solved.png')
  121.        
  122.         # ^ Draws the nodes (not useful) (ugly) ^
  123.        
  124.  
  125.        
  126.         nbrofnodes = len(nodelist)
  127.         print("%d nodes generated"%nbrofnodes)
  128.  
  129.         print("")
  130.         print("Linking nodes...")
  131.         # Alright, we got our nodes list... Now it's time to connect them.
  132.  
  133.         counter = 0
  134.         last = 0
  135.         for node in nodelist:
  136.             a = node.pos[0]
  137.             b = node.pos[1]
  138.             if node.paths[0]:
  139.                 for i in range(b-1,0,-1):
  140.                     if mazepix[a,i] == blackpix:
  141.                         break
  142.                     if any(x.pos == (a,i) for x in nodelist):
  143.                         for iteration in nodelist:
  144.                             if iteration.pos == (a,i):
  145.                                 indexofnodetolinkto = iteration.index
  146.                                 break
  147.                         node.connections.append(indexofnodetolinkto)
  148.                         # print("Linking node %d and node %d..."%(node.index, indexofnodetolinkto))
  149.                         break
  150.            
  151.             if node.paths[1]:
  152.                 for i in range(a+1,maze.size[0]):
  153.                     if mazepix[i,b] == blackpix:
  154.                         break
  155.                     if any(x.pos == (i,b) for x in nodelist):
  156.                         for iteration in nodelist:
  157.                             if iteration.pos == (i,b):
  158.                                 indexofnodetolinkto = iteration.index
  159.                                 break
  160.                         node.connections.append(indexofnodetolinkto)
  161.                         # print("Linking node %d and node %d..."%(node.index, indexofnodetolinkto))
  162.                         break
  163.                    
  164.             if node.paths[2]:
  165.                 for i in range(b+1,maze.size[1]):
  166.                     if mazepix[a,i] == blackpix:
  167.                         break
  168.                     if any(x.pos == (a,i) for x in nodelist):
  169.                         for iteration in nodelist:
  170.                             if iteration.pos == (a,i):
  171.                                 indexofnodetolinkto = iteration.index
  172.                                 break
  173.                         node.connections.append(indexofnodetolinkto)
  174.                         # print("Linking node %d and node %d..."%(node.index, indexofnodetolinkto))
  175.                         break
  176.                        
  177.             if node.paths[3]:
  178.                 for i in range(a-1,0,-1):
  179.                     if mazepix[i,b] == blackpix:
  180.                         break
  181.                     if any(x.pos == (i,b) for x in nodelist):
  182.                         for iteration in nodelist:
  183.                             if iteration.pos == (i,b):
  184.                                 indexofnodetolinkto = iteration.index
  185.                                 break
  186.                         node.connections.append(indexofnodetolinkto)
  187.                         # print("Linking node %d and node %d..."%(node.index, indexofnodetolinkto))
  188.                         break
  189.            
  190.             counter += 1
  191.             percent = (counter/nbrofnodes)*100
  192.             if int(percent)%10 == 0 and int(percent) != last:
  193.                 print("Linking %d%% done..."%percent)
  194.                 last = int(percent)
  195.            
  196.         print("All node linked.")
  197.         print("Saving current linked nodelist as %s"%imagename + ".lnodelist")
  198.         with open(imagename + ".lnodelist", 'wb') as handle:
  199.             pickle.dump(nodelist, handle, protocol=pickle.HIGHEST_PROTOCOL)
  200.             handle.close()
  201.         print("Done.")
  202.    
  203.     print("")
  204.     print("Pathfinding...")
  205.  
  206.     # We've got our nodes and our connections.
  207.     # Time to A* .
  208.  
  209.    
  210.     startnode = nodelist[1]
  211.     endnode = nodelist[0]
  212.    
  213.     currentpath = []
  214.     currentpath.append(startnode)
  215.    
  216.     currentnode = startnode
  217.     while True:
  218.         totaldistance = 0
  219.         for i in range(len(currentpath)):
  220.             node1 = currentpath[i]
  221.             try:
  222.                 node2 = currentpath[i+1]
  223.                 totaldistance += getDistanceWithCoords( node1.pos,node2.pos )
  224.             except:
  225.                 pass
  226.         queuelist = []
  227.         for connection in currentnode.connections:
  228.             nodeindex = getIndexOfNodeInNodelistByItsNodeIndex(connection, nodelist)
  229.             if not nodelist[nodeindex].deadend == True:
  230.                 score = nodelist[nodeindex].distancefromexit + getDistanceWithCoords( currentnode.pos,nodelist[nodeindex].pos ) + totaldistance
  231.                 queuelist.append( (connection, score, nodelist[nodeindex].deadend) )
  232.        
  233.         queuelist.sort(key=lambda x: x[1])
  234.         nextnode = nodelist[queuelist[0][0]]
  235.         if nextnode in currentpath:
  236.             try:
  237.                 nextnode = nodelist[queuelist[1][0]]
  238.             except:
  239.                 # print("ARGL! Node %d is a dead end! Restarting calculations..."%currentnode.index)
  240.                
  241.                 currentnode.deadend = True
  242.                 currentnode = currentpath[-2]
  243.                 del currentpath[-1]
  244.                 continue
  245.                
  246.                
  247.         # print(totaldistance)
  248.         # print(str(queuelist))
  249.         # print("Going from node %d to node %d.."%(currentnode.index, nextnode.index))
  250.         currentpath.append(currentnode)
  251.         currentnode = nextnode
  252.         if currentnode.pos == endnode.pos:
  253.             break
  254.    
  255.     print("Calculations completed. Shortest path is:")
  256.     disppath = ""
  257.     for node in currentpath:
  258.         if not node == endnode:
  259.             disppath += str(node.index) + "->"
  260.         else:
  261.             disppath += str(node.index)
  262.     print(disppath)
  263.     totaldistance = 0
  264.     for i in range(len(currentpath)):
  265.         node1 = currentpath[i]
  266.         try:
  267.             node2 = currentpath[i+1]
  268.             totaldistance += getDistanceWithCoords( node1.pos,node2.pos )
  269.         except:
  270.             pass
  271.     print("\n%s nodes used, with a total travel distance of %d pixels."%(len(currentpath),totaldistance))
  272.  
  273.     stoptime = time.time()
  274.     print(stoptime-starttime)
  275.        
  276.        
  277.        
  278.        
  279.  
  280.     # Draw the path
  281.     for i in range(len(currentpath)):
  282.         currentnode = currentpath[i]
  283.         try:
  284.             nextnode = currentpath[i+1]
  285.             totaldistance += getDistanceWithCoords( currentnode.pos,nextnode.pos )
  286.             x = currentnode.pos[0] - nextnode.pos[0]
  287.             y = currentnode.pos[1] - nextnode.pos[1]
  288.            
  289.             if x != 0:
  290.                 direction = "horizontal"
  291.                 if currentnode.pos[0] < nextnode.pos[0]:
  292.                     jump = 1
  293.                 else:
  294.                     jump = -1
  295.             elif y != 0:
  296.                 direction = "vertical"
  297.                 if currentnode.pos[1] < nextnode.pos[1]:
  298.                     jump = 1
  299.                 else:
  300.                     jump = -1
  301.            
  302.             if direction == "horizontal":
  303.                 for i in range(currentnode.pos[0],nextnode.pos[0],jump):
  304.                     mazepix[i, currentnode.pos[1]] = greenpix
  305.             elif direction == "vertical":
  306.                 for i in range(currentnode.pos[1],nextnode.pos[1],jump):
  307.                     mazepix[currentnode.pos[0], i] = greenpix
  308.        
  309.        
  310.        
  311.         except:
  312.             pass
  313.            
  314.     ##
  315.        
  316. maze.save('solved.png')
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement