import sys from collections import deque from os import path import queue import numpy from utils import * from settings import * class Problem: """The abstract class for a formal problem. You should subclass this and implement the methods actions and result, and possibly __init__, goal_test, and path_cost. Then you will create instances of your subclass and solve them with the various search functions.""" def __init__(self, initial, goal): """The constructor specifies the initial state, and possibly a goal state, if there is a unique goal. Your subclass's constructor can add other arguments.""" self.initial = initial self.goal = goal def actions(self, state): """Return the actions that can be executed in the given state. The result would typically be a list, but if there are many actions, consider yielding them one at a time in an iterator, rather than building them all at once.""" moves = [] if self.turn_left(state): moves.append('Left') if self.turn_right(state): moves.append('Right') if self.move_forward(state): moves.append('Forward') #print(moves) return moves def turn_left(self, state): return True def turn_right(self, state): return True def move_forward(self, state): a_row = 0 a_column = 0 for row in range(MAP_SIZE): for column, pos in enumerate(state[row]): if pos == ">": a_row = row a_column = column if a_column == MAP_SIZE-1: return False elif state[a_row][a_column+1] == '.': return True return False if pos == "<": a_row = row a_column = column if a_column == 0: return False elif state[a_row][a_column-1] == '.': return True return False if pos == "v": a_row = row a_column = column if a_row == MAP_SIZE-1: return False elif state[a_row+1][a_column] == '.': return True return False if pos == "^": a_row = row a_column = column if row == 0: return False elif state[a_row-1][a_column] == '.': return True return False def turn_me_or_move(self, state, do_it): temp_map = [list(item) for item in state] #print(temp_map) #a_row = 0 #a_column = 0 for row in range(MAP_SIZE): for column, pos in enumerate(temp_map[row]): if pos == ">": a_row = row a_column = column #print("a_row:" + str(a_row)) #print("a_column" + str(a_column)) if(do_it == 'Left'): temp_map[a_row][a_column] = "^" if(do_it == 'Right'): temp_map[a_row][a_column] = 'v' if(do_it == 'Forward'): temp_map[a_row][a_column] = '.' temp_map[a_row][a_column+1] = '>' return temp_map if pos == "<": a_row = row a_column = column if(do_it == 'Left'): temp_map[a_row][a_column] = 'v' if(do_it == 'Right'): temp_map[a_row][a_column] = '^' if(do_it == 'Forward'): temp_map[a_row][a_column] = '.' temp_map[a_row][a_column-1] = '<' return temp_map if pos == "v": a_row = row a_column = column if(do_it == 'Left'): temp_map[a_row][a_column] = '>' if(do_it == 'Right'): temp_map[a_row][a_column] = '<' if(do_it == 'Forward'): temp_map[a_row][a_column] = '.' temp_map[a_row+1][a_column] = 'v' return temp_map if pos == "^": a_row = row a_column = column if(do_it == 'Left'): temp_map[a_row][a_column] = '<' if(do_it == 'Right'): temp_map[a_row][a_column] = '>' if(do_it == 'Forward'): temp_map[a_row][a_column] = '.' temp_map[a_row-1][a_column] = '^' return temp_map return temp_map def result(self, state, action): """Return the state that results from executing the given action in the given state. The action must be one of self.actions(state).""" new_state = [] if action == 'Left': new_state = self.turn_me_or_move(state, 'Left') elif action == 'Right': new_state = self.turn_me_or_move(state, 'Right') elif action == 'Forward': new_state = self.turn_me_or_move(state, 'Forward') super_new_state = tuple(map(tuple,new_state)) return super_new_state def goal_test(self, state): """Return True if the state is a goal. The default method compares the state to self.goal or checks for state in self.goal if it is a list, as specified in the constructor. Override this method if checking against a single self.goal is not enough.""" """if isinstance(self.goal, list): return is_in(state, self.goal) else: return state == self.goal""" if self.goal == state: return True return False class Node: """A node in a search tree. Contains a pointer to the parent (the node that this is a successor of) and to the actual state for this node. Note that if a state is arrived at by two paths, then there are two nodes with the same state. Also includes the action that got us to this state, and the total path_cost (also known as g) to reach the node. Other functions may add an f and h value; see best_first_graph_search and astar_search for an explanation of how the f and h values are handled. You will not need to subclass this class.""" def __init__(self, state, parent=None, action=None): """Create a search tree Node, derived from a parent by an action.""" self.state = state self.parent = parent self.action = action def __repr__(self): return "".format(self.state) def expand(self, problem): """List the nodes reachable in one step from this node.""" return [self.child_node(problem, action) for action in problem.actions(self.state)] def child_node(self, problem, action): next_state = problem.result(self.state, action) next_node = Node(next_state, self, action) return next_node def __eq__(self, other): return isinstance(other, Node) and self.state == other.state def __hash__(self): # We use the hash value of the state # stored in the node instead of the node # object itself to quickly search a node # with the same state in a Hash Table return hash(self.state) class BFS: @staticmethod def breadth_first_graph_search(problem): """[Figure 3.11] Note that this function can be implemented in a single line as below: return graph_search(problem, FIFOQueue()) """ node = Node(problem.initial) if problem.goal_test(node.state): return node frontier = deque([node]) final_child = node history = [] explored = set() while frontier: node = frontier.popleft() explored.add(node.state) for child in node.expand(problem): if child.state not in explored and child not in frontier: if problem.goal_test(child.state): while(child.parent != None): history.append(child.action) child = child.parent #return child history.reverse() #print(history) return history frontier.append(child) #BFS.print_node_state(child.state) return None @staticmethod def loadMap(map_name=''): maze = [] map_folder = path.dirname(__file__) with open(path.join(map_folder, map_name), 'rt') as f: for line in f: maze.append(line.rstrip('\n')) #print(maze) return maze @staticmethod def run(): initial_map = tuple(map(tuple,BFS.loadMap('map.txt'))) goal_map = tuple(map(tuple,BFS.loadMap('goal_map.txt'))) problem = Problem(initial_map, goal_map) #BFS.print_node_state(initial_map) #BFS.print_node_state(goal_map) result = BFS.breadth_first_graph_search(problem) print(result) #return result #print(BFS.print_node_state(result)) @staticmethod def print_node_state(node_state): print(numpy.matrix(node_state))