コード例 #1
0
ファイル: mazeSolve.py プロジェクト: Axel-Jacobsen/MazeSolve
class mazeSolve(object):
    """Takes in a maze, makes a graph, and solves it"""

    def __init__(self, filename, to_crop=False):

        self.maze = Maze(filename, to_crop=to_crop)
        self.nodes = self.maze.node_dict
        self.priority_que = BinaryHeap([list(a) for a in zip(self.nodes.keys(), [float('inf')] * len(self.nodes))])
        # Want to use zip function, but need each item to be mutable. therefore, the `list(a) for a in zip...` notation
        self.visited_nodes = set()
        self.path = []

    def solve(self):
        """Connects the nodes from the starting node to the ending node"""

        # Find the starting node of the maze, add it to self.visited_nodes, and assign the node a distance from the start of 0
        start = self.maze.start_node

        self.add_value(start, 0)
        self.heapify()

        end_node = self.process([start, 0])

        self.make_path(end_node[0])

        self.draw_path()

    def process(self, curr_node):
        """Process the maze by running through the binary heap, until the end node is found"""

        current_node = self.nodes[curr_node[0]]
        current_node_distance = curr_node[1]

        connected_nodes = 0.0
        total_nodes = len(self.nodes)


        while current_node.end == False:

            connected_nodes += 1
            percent = connected_nodes / total_nodes * 100

            replace_print('Investigating {0:18} Number {1} of {2} nodes ({3:3.2f} %)'.format(current_node.prettify, int(connected_nodes), total_nodes, percent))

            # Add min node to visited_nodes and remove it from the priority_que
            self.visited_nodes.add(current_node.name)
            self.priority_que.delete_min()

            # Find the adjacent nodes of curr_node
            adjacent_nodes = current_node.adjacent_nodes

            for direction, node_distance in adjacent_nodes.items():

                if node_distance and not (node_distance[0].name in self.visited_nodes):

                    self.add_value(node_distance[0].name, node_distance[1] + current_node_distance)
                    node_distance[0].set_prev_node(current_node)

            self.heapify()

            current_node = self.nodes[self.priority_que.heap[0][0]]
            current_node_distance = self.priority_que.heap[0][1]

        end_node = self.priority_que.delete_min()
        end_node[0] = self.nodes[end_node[0]]

        # Final increment and print of "Investigating Node..."
        connected_nodes += 1
        percent = connected_nodes / total_nodes * 100
        replace_print('Investigating {0:18} Number {1} of {2} nodes ({3:3.2f} %)'.format(current_node.prettify, int(connected_nodes), total_nodes, percent))

        print('\nEnd node has been found: {}'.format(end_node[0].prettify))
        print '\n'
        print 'The path length from start to end node is {} pixels long'.format(end_node[1])

        return end_node

    def make_path(self, curr_node):
        """Returns the path from the start node to the end node"""

        self.path = [curr_node] + self.path

        while curr_node.prev_node:

            self.path = [curr_node.prev_node] + self.path
            curr_node = curr_node.prev_node

        if len(self.path) < 100:
            self.print_path()

    def print_path(self):
        """Prints the path of the nodes"""

        print '\nHere is the path for the maze:',

        for node in self.path:

            print node.name, '-->',

    def draw_path(self):
        """Creates a new maze image with the solution on it"""

        if not self.path:
            raise IndexError('`self.path` MUST BE POPULATED')

        maze_copy = self.maze.maze.copy()
        draw = ImageDraw.Draw(maze_copy)

        for i in xrange(len(self.path)-1):

            node = self.path[i]
            next_node = self.path[i + 1]
            line_coords = [(node.x_pos, node.y_pos), (next_node.x_pos, next_node.y_pos)]
            draw.line(line_coords, fill=(66, 134, 244))

        filename = self.maze.maze.filename.replace('cropped', 'solution')
        maze_copy.save(filename)

    def add_value(self, node, value):
        """Finds `node` in priority_que and assigns `value` to it's second value. If the node hasn't been discovered yet, assign it's distance `value`. Else add value to it's distance"""
        found = False

        for node_dist in self.priority_que.heap:

            if node == node_dist[0]:
                node_dist[1] = value if node_dist[1] == float('inf') else (node_dist[1] + value)
                found = True
                break

        if not found:
            raise ValueError('Node not in priority que')

    def heapify(self):
        """Shortcut method"""
        self.priority_que.heapify()