diff --git a/Python/8PUZZLE.PY b/Python/8PUZZLE.PY index b43046b..354baba 100755 --- a/Python/8PUZZLE.PY +++ b/Python/8PUZZLE.PY @@ -19,29 +19,26 @@ # board in the following positions: # # ------------- -# | 0 | 3 | 6 | +# | 3 | 7 | 6 | # ------------- -# | 1 | 4 | 7 | +# | 5 | 1 | 2 | # ------------- -# | 2 | 5 | 8 | +# | 4 | 0 | 8 | # ------------- # # The goal is defined as: # # ------------- -# | 1 | 2 | 3 | +# | 5 | 3 | 6 | # ------------- -# | 8 | 0 | 4 | +# | 7 | 0 | 2 | # ------------- -# | 7 | 6 | 5 | +# | 4 | 1 | 8 | # ------------- # # Where 0 denotes the blank tile or space. -goal_state = [1, 8, 7, 2, 0, 6, 3, 4, 5] -# -# The code will read state from a file called "state.txt" where the format is -# as above but space seperated. i.e. the content for the goal state would be -# 1 8 7 2 0 6 3 4 5 +goal_state = [5, 7, 4, 3, 0, 1, 6, 2, 8] +starting_state = [3, 5, 4, 7, 1, 0, 6, 2, 8] ### Code begins. import sys @@ -59,7 +56,7 @@ def move_up( state ): """Moves the blank tile up on the board. Returns a new state as a list.""" # Perform an object copy new_state = state[:] - index = new_state.index( 0 ) + index = new_state.index( 0 ) #this will return index of blank # Sanity check if index not in [0, 3, 6]: # Swap the values. @@ -118,113 +115,226 @@ def move_right( state ): # Can't move, return None return None -def create_node( state, parent, operator, depth, cost ): - return Node( state, parent, operator, depth, cost ) +def create_node( state, parent, operator, depth,cost): + return Node( state, parent, operator, depth,cost) -def expand_node( node, nodes ): +def expand_node( node,open_nodes,close_nodes): """Returns a list of expanded nodes""" expanded_nodes = [] - expanded_nodes.append( create_node( move_up( node.state ), node, "u", node.depth + 1, 0 ) ) - expanded_nodes.append( create_node( move_down( node.state ), node, "d", node.depth + 1, 0 ) ) - expanded_nodes.append( create_node( move_left( node.state ), node, "l", node.depth + 1, 0 ) ) - expanded_nodes.append( create_node( move_right( node.state), node, "r", node.depth + 1, 0 ) ) + + expanded_nodes.append( create_node( move_up( node.state ), node, "u", node.depth + 1,0) ) + expanded_nodes.append( create_node( move_down( node.state ), node, "d", node.depth + 1,0) ) + expanded_nodes.append( create_node( move_left( node.state ), node, "l", node.depth + 1,0) ) + expanded_nodes.append( create_node( move_right( node.state), node, "r", node.depth + 1,0) ) + # Filter the list and remove the nodes that are impossible (move function returned None) expanded_nodes = [node for node in expanded_nodes if node.state != None] #list comprehension! + open_state = [] + for o in open_nodes: + open_state.append(o.state) + + close_state = [] + for c in close_nodes: + close_state.append(c.state) + + #Remove repeated nodes + #Remove the nodes that are in open list + expanded_nodes = [node for node in expanded_nodes if node.state not in open_state] + #Remove the nodes that are in close list + expanded_nodes = [node for node in expanded_nodes if node.state not in close_state] + return expanded_nodes + +def solution_path(node): + moves = [] + states = [] + temp = node + while True: + moves.insert(0, temp.operator) + states.insert(0,temp.state) + if temp.depth <= 1: break + temp = temp.parent + states.insert(0,starting_state) + return moves,states + def bfs( start, goal ): """Performs a breadth first search from the start state to the goal""" # A list (can act as a queue) for the nodes. - nodes = [] + open_nodes = [] + close_nodes = [] # Create the queue with the root node in it. - nodes.append( create_node( start, None, None, 0, 0 ) ) + open_nodes.append( create_node( start, None, None, 0 , 0) ) while True: # We've run out of states, no solution. - if len( nodes ) == 0: return None + if len( open_nodes ) == 0: return None,None # take the node from the front of the queue - node = nodes.pop(0) + node = open_nodes.pop(0) + close_nodes.append(node) + # Append the move we made to moves # if this node is the goal, return the moves it took to get here. if node.state == goal: - moves = [] - temp = node - while True: - moves.insert(0, temp.operator) - if temp.depth == 1: break - temp = temp.parent - return moves - # Expand the node and add all the expansions to the front of the stack - nodes.extend( expand_node( node, nodes ) ) - -def dfs( start, goal, depth=10 ): + return solution_path(node) + + # Expand the node and add all the expansions to end of the queue + open_nodes.extend( expand_node( node, open_nodes,close_nodes) ) + + +def dfs( start, goal,depth = 10): """Performs a depth first search from the start state to the goal. Depth param is optional.""" - # NOTE: This is a limited search or else it keeps repeating moves. This is an infinite search space. - # I'm not sure if I implemented this right, but I implemented an iterative depth search below - # too that uses this function and it works fine. Using this function itself will repeat moves until - # the depth_limit is reached. Iterative depth search solves this problem, though. - # - # An attempt of cutting down on repeat moves was made in the expand_node() function. - depth_limit = depth # A list (can act as a stack too) for the nodes. - nodes = [] - # Create the queue with the root node in it. - nodes.append( create_node( start, None, None, 0, 0 ) ) + open_nodes = [] + close_nodes = [] + + depth_limit = depth + # Create the stack with the root node in it. + open_nodes.append( create_node( start, None, None, 0 ,0) ) while True: # We've run out of states, no solution. - if len( nodes ) == 0: return None + if len( open_nodes ) == 0: return None,None # take the node from the front of the queue - node = nodes.pop(0) + node = open_nodes.pop(0) + close_nodes.append(node) # if this node is the goal, return the moves it took to get here. if node.state == goal: - moves = [] - temp = node - while True: - moves.insert(0, temp.operator) - if temp.depth <= 1: break - temp = temp.parent - return moves - # Add all the expansions to the beginning of the stack if we are under the depth limit + return solution_path(node) + if node.depth < depth_limit: - expanded_nodes = expand_node( node, nodes ) - expanded_nodes.extend( nodes ) - nodes = expanded_nodes + #Expand the nodes and add all the expansion in the front of open list + expanded_nodes = expand_node( node, open_nodes,close_nodes ) + if len(expanded_nodes) != 0: + expanded_nodes.extend(open_nodes ) + open_nodes = expanded_nodes + def ids( start, goal, depth=50 ): """Perfoms an iterative depth first search from the start state to the goal. Depth is optional.""" for i in range( depth ): - result = dfs( start, goal, i ) + result,state = dfs( start, goal, i ) if result != None: - return result + return result,state + return None,None + + +def hill_climbing(start,goal): + """ Perform steepest Hill Climbing Approach. This method involves local minimum search""" + open_nodes = [] + close_nodes = [] #Required to remove the repition + + # Create the stack with the root node in it. + open_nodes.append( create_node( start, None, None, 0,0 ) ) + while True: + # We've run out of states, no solution. + if len( open_nodes ) == 0: return None,None + # take the node from the front of the queue + node = open_nodes.pop(0) + close_nodes.append(node) + # if this node is the goal, return the moves it took to get here. + if node.state == goal: + return solution_path(node) + + h1 = h(node.state,goal) #heuristic value of current node + + #Expand the nodes and add all the expansion in the front of open list + expanded_nodes = expand_node( node, open_nodes,close_nodes ) + successor_nodes = [] + for succ_node in expanded_nodes: + h2 = h(succ_node.state,goal) + if(h2