Beispiel #1
0
def optimal_path(data):
    '''optimal_path takes a single argument data, a dictionary of features in 
    the cave and returns the length of the shortest path that enables Falca 
    to collect all of the treasures and exit from the dungeon.'''
    # Check for valid cave
    if not build_cave(data):
        return None

    # Initialise to an unrealistic distance for comparison
    unrealistic_distance = 4 * data['size']**2
    distance = unrealistic_distance

    # The coordinates to visit, with and without the sword
    locations, locations_sword = get_locations(data)

    # If there is no sword or treasure, go straight to exit.
    if len(locations) == 0 and len(locations_sword) == 0:
        return shortest_path(data, data['entrance'], data['exit'], False)

    # Get the shortest distance, once with the sword and once without
    path1 = search(data, locations)
    path2 = search(data, locations_sword)

    # See if paths were found, if they were, update weight
    if path1:
        distance = path1.weight
    if path2 and path2.weight < distance:
        distance = path2.weight
    return distance if distance < unrealistic_distance else None
Beispiel #2
0
def cost_locations(data, node, locations):
    cost_locs = []
    for loc in locations:
        if loc not in node.visited + [node.location]:
            cost = shortest_path(data, node.location, loc, node.sword)
            if cost:
                cost_locs.append((cost, loc))
    return sorted(cost_locs)
Beispiel #3
0
def optimal_path(data):
    '''Return the length of the optimal path that 'solves' the cave defined by
    data; ie, collects all treasures and reaches the exit.'''
    # locations of interest
    locations = [data['exit']]
    if 'sword' in data:
        locations.append(data['sword'])
    if 'treasure' in data:
        locations += data['treasure']

    # state = (current_location, visited_locations, has_sword, treasure_count)
    initial_state = (data['entrance'], (), False, 0)

    # total number of treasures to collect
    treasure_max = len(data['treasure']) if 'treasure' in data else 0

    # queue: here a dictionary mapping state to path cost
    queue = {initial_state: 0}
    
    # while there are still states to be explored
    while queue:
        # get the lowest cost state, remove from the queue and unpack
        cur_state = min(queue, key=queue.get)
        cur_cost = queue.pop(cur_state)
        cur_loc, visited, has_sword, treasure_count = cur_state

        # if we've satisfied the goal conditions (all treasure collected 
        # and at exit), return
        if cur_loc == data['exit'] and treasure_count == treasure_max:
            return cur_cost

        # otherwise, explore next locations
        for next_loc in locations:
            # don't revisit an already visited location
            if next_loc in visited:
                continue
            # generate the path to the next location
            cost = shortest_path(data, cur_loc, next_loc, has_sword)
            # continue if no path to the next location
            if cost is None:
                continue
            # otherwise, generate the next state, and add it to the queue 
            # if either: (i) it doesn't yet exist; or (ii) it exists, but 
            # this is a shorter path
            next_state = (next_loc, visited + (next_loc,), 
                    has_sword or 
                          ('sword' in data and next_loc == data['sword']), 
                    treasure_count + (next_loc in data['treasure']
                        if 'treasure' in data else 0))
            next_cost = cur_cost + cost
            if next_state not in queue or queue[next_state] > next_cost:
                queue[next_state] = next_cost

    # if queue is empty and we haven't reached goal state
    return None
Beispiel #4
0
def search(data, locations):
    """search returns the shortest path between the entry and exit after
    visiting the required locations

    data:       a dictionary of features in the cave
    locations:  list holding the locations of the treasure and sword"""

    # An object holding info on the path
    class Node:
        def __init__(self, weight, visited, sword, treasures):
            self.visited = visited[:]
            self.weight = weight
            self.sword = sword
            self.treasures = treasures

    # Location of the sword
    sword_location = data['sword'] if 'sword' in data else (-1, -1)
    # Treasure locations
    treasure_locations = data['treasure'][:] if 'treasure' in data else []

    # Entrance node
    node = Node(0, [data['entrance']], False, 0)

    # Add the first node onto the que
    unexplored = [(0, id(node), node)]

    while unexplored:

        # Our current position
        unexplored.sort(reverse=True)
        node = unexplored.pop(-1)[2]

        # Check for the sword
        if node.visited[-1] == sword_location:
            node.sword = True

        # We made it to the end, return the distance
        if node.visited[-1] == data['exit']:
            return node.weight

        for point in locations:
            if point not in node.visited:
                if point == data['exit'] and \
                        node.treasures < len(treasure_locations):
                    continue
                cost = shortest_path(data, node.visited[-1], point, node.sword)
                if cost:
                    treasure = 1 if point in treasure_locations else 0
                    new_node = Node(node.weight + cost, node.visited + [point],
                                    node.sword, node.treasures + treasure)
                    unexplored.append((cost, id(new_node), new_node))

    return data['size']**3
Beispiel #5
0
def get_distance(path, data):
    sword = (-1, -1)
    if 'sword' in data:
        sword = data['sword']

    total = 0
    # for each step in the path through the cave
    has_sword = False
    for i in range(len(path) - 1):
        # if current spot is a sword
        if path[i] == sword:
            has_sword = True
        result = shortest_path(data, path[i], path[i + 1], has_sword)
        if result:
            total += result
        else:
            return None
    return total
Beispiel #6
0
def search(data, locations):
    """search returns the shortest path between the entry and exit after
    visiting the required locations

    data:       a dictionary of features in the cave
    locations:  list holding the locations of the treasure and sword"""

    # An object holding info on the path
    class Node:
        def __init__(self, weight, visited, sword):
            self.visited = visited[:]
            self.weight = weight
            self.sword = sword

    # Location of the sword
    sword_location = data['sword'] if 'sword' in data else (-1, -1)

    # Entrance node
    node = Node(0, [data['entrance']], False)

    # Priority que
    unexplored = []
    # Add the first node onto the que
    heappush(unexplored, (0, id(node), node))

    # If there is no nothing to collect, go to exit
    if len(locations) == 0:
        locations.append(data['exit'])

    while unexplored:

        # Our current position
        node = heappop(unexplored)[2]

        # Check for the sword
        if node.visited[-1] == sword_location:
            node.sword = True

        # We made it to the end, return the distance
        if node.visited[-1] == data['exit']:
            return node.weight

        # We have collected all treasure, find cost to exit
        if len(node.visited) == len(locations) + 1:
            cost = shortest_path(data, node.visited[-1], data['exit'],
                                                                node.sword)
            # If there is a path to the exit, add it to the que for comparison
            if cost:
                node.weight += cost
                node.visited += [data['exit']]
                heappush(unexplored, (cost, id(node), node))
        else:
            # There is still treasure to collect
            for point in locations:
                if point not in node.visited:
                    cost = shortest_path(data, node.visited[-1], point,
                                                                node.sword)
                    if cost:
                        new_node = Node(node.weight + cost, node.visited +
                                                        [point], node.sword)
                        heappush(unexplored, (cost, id(new_node), new_node))

    return data['size'] ** 3
Beispiel #7
0
def search(data, locations):
    '''search takes a data dictionary and a list of locations to visit and 
    returns a Node object containing the route and cost of the shortest path 
    to the exit after visiting every treasure location.'''

    # A node holding a list of each place before it, and the current weight
    class Node:
        '''a node holding all the nodes before it'''
        def __init__(self):
            self.visited = []

        weight = 0

    # Location of sword for comparison
    sword_location = (-1, -1)
    if 'sword' in data:
        sword_location = data['sword']

    sword = False
    node = Node()
    node.visited.append(data['entrance'])
    # Priority que
    unexplored = []
    # priorityque tuple consists of (cost, id for equal cost comparison, node)
    heappush(unexplored, (0, id(node), node))

    while unexplored:

        # Our current position
        node = heappop(unexplored)[2]

        # Check for the sword
        if node.visited[-1] == sword_location:
            sword = True

        # We made it to the end
        if node.visited[-1] == data['exit']:
            return node

        for point in locations:
            # If the spot hasn't yet been visited on this specific path
            if point not in node.visited:
                # Create the next Node
                new_node = Node()
                # Give it a copy of the path that came before it
                new_node.visited += node.visited
                # Give it the weight that it took to get here.
                new_node.weight += node.weight
                # Append the current location along the path
                new_node.visited.append(point)
                # Find the weight to get to this point
                cost = shortest_path(data, node.visited[-1], point, sword)
                # Replace None returns with very high cost
                if not cost:
                    cost = 4 * data['size']**2
                new_node.weight += cost
                if new_node not in unexplored:
                    heappush(unexplored, (cost, id(new_node), new_node))
                elif new_node in unexplored and cost < get_cost(
                        new_node, unexplored):
                    unexplored = replace_cost(new_node, cost, unexplored)
            # Add the exit location into the list of locations
            elif len(node.visited) == len(locations) + 1:
                locations.append(data['exit'])
    return None
Beispiel #8
0
Datei: Q4.py Projekt: wingsuit/P2
def optimal_path(data):
    """optimal_path returns the length of the shortest path that enables Falca
    to collect all of the treasures and exit from the dungeon.

    data: a dictionary of features in the cave"""

    # An object holding info on the path
    class Node:
        def __init__(self, weight, visited, treasures, sword):
            self.cost = weight
            self.visited = visited
            self.treasures = treasures
            self.sword = sword

    # Location of the sword
    sword_location = data['sword'] if 'sword' in data else (-1, -1)

    # Treasure locations
    treasure_locations = data['treasure'] if 'treasure' in data else []

    # All locations
    locations = [data['exit']] + treasure_locations
    locations += [data['sword']] if 'sword' in data else []

    # Entrance node
    node = Node(0, [data['entrance']], 0, False)

    # Add the first node onto the que
    unexplored = [(0, id(node), node)]

    while unexplored:

        # Our current position
        unexplored.sort()
        node = unexplored.pop(0)[2]

        # Check for the sword
        if node.visited[-1] == sword_location:
            node.sword = True

        # We made it to the end, return the distance
        if node.visited[-1] == data['exit']:
            return node.cost

        # Find every location we can go from here
        for spot in locations:
            if spot not in node.visited:

                # Dont exit without all the treasure
                if spot == data['exit'] and \
                        node.treasures < len(treasure_locations):
                    continue

                # If the location is reachable, add it to the que
                cost = shortest_path(data, node.visited[-1], spot, node.sword)
                if cost:
                    treasure = 1 if spot in treasure_locations else 0
                    new_node = Node(node.cost + cost, node.visited + [spot],
                                    node.treasures + treasure, node.sword)
                    unexplored.append((new_node.cost, id(new_node), new_node))

    return None