def goal_test(self, state): "goal_test(state) - Is state a goal?" testBoard = TileBoard(self.size, False, state) if (testBoard.solved()): return True else: return False
def __init__(self, n, force_state=None, **kwargs): """"__init__(n, force_state, **kwargs) NPuzzle constructor. Creates an initial TileBoard of size n. If force_state is not None, the puzzle is initialized to the specified state instead of being generated randomly. The parent's class constructor is then called with the TileBoard instance any any remaining arguments captured in **kwargs. """ # Note on **kwargs: # **kwargs is Python construct that captures any remaining arguments # into a dictionary. The dictionary can be accessed like any other # dictionary, e.g. kwargs["keyname"], or passed to another function # as if each entry was a keyword argument: # e.g. foobar(arg1, arg2, …, argn, **kwargs). if force_state: temp_tileboard = TileBoard(n, force_state=force_state) super(NPuzzle, self).__init__(temp_tileboard, goals=None, g=kwargs["g"], h=kwargs["h"]) else: temp_tileboard = TileBoard(n) super(NPuzzle, self).__init__(temp_tileboard, goals=None, g=kwargs["g"], h=kwargs["h"])
def h(cls, state: TileBoard): manhattan_sum = 0 # Populates Solved State solved = [[None for c in range(state.cols)] for r in range(state.rows)] index = 1 for row in range(state.boardsize): for col in range(state.boardsize): if row == state.rows // 2 and col == state.cols // 2: solved[row][col] = None else: solved[row][col] = index index += 1 # Calculate Displacements for row in range(state.boardsize): for col in range(state.boardsize): item = state.get(row, col) # Find location for solved state for solved_row in range(len(solved)): if item in solved[solved_row]: # Adds manhattan distance to manhattan_sum # If item is at [1, 2] in state and [0, 0] in solved then distance is 1 + 2 = 3 manhattan_sum += abs(solved_row - row) + abs(solved[solved_row].index(item) - col) return manhattan_sum
def __init__(self, n, force_state=None, **kwargs): """"__init__(n, force_state, **kwargs) NPuzzle constructor. Creates an initial TileBoard of size n. If force_state is not None, the puzzle is initialized to the specified state instead of being generated randomly. The parent's class constructor is then called with the TileBoard instance and any remaining arguments captured in **kwargs. """ # Instantiate Tileboard self.puzzle = TileBoard(n, force_state=force_state) # Initialize parent class super().__init__(self.puzzle.state_tuple(), self.puzzle.goals, kwargs["g"], kwargs["h"])
def h(cls, state: TileBoard): manhattan_distance = 0 goal_state = list(state.goals[0]) size = state.boardsize # calculate misplacement tiles # h = |x1 − x2| + |y1 − y2| # for index above board mod board_size will get cols # for index above board divide board_size will get rows for row in range(size): for col in range(size): # not count the blank for misplacement if state.get(row, col) is not None: val_rc = state.get(row, col) idx = goal_state.index(val_rc) if idx > size - 1: manhattan_distance += abs(idx // size - row) + abs(idx % size - col) else: manhattan_distance += row + abs(idx - col) return manhattan_distance
class NPuzzle(Problem): """ NPuzzle - Problem representation for an N-tile puzzle Provides implementations for Problem actions specific to N tile puzzles. """ def __init__(self, n, force_state=None, **kwargs): """"__init__(n, force_state, **kwargs) NPuzzle constructor. Creates an initial TileBoard of size n. If force_state is not None, the puzzle is initialized to the specified state instead of being generated randomly. The parent's class constructor is then called with the TileBoard instance any any remaining arguments captured in **kwargs. """ # Note on **kwargs: # **kwargs is Python construct that captures any remaining arguments # into a dictionary. The dictionary can be accessed like any other # dictionary, e.g. kwargs["keyname"], or passed to another function # as if each entry was a keyword argument: # e.g. foobar(arg1, arg2, …, argn, **kwargs). self.size = n self.tileBoard = TileBoard(self.size, False, force_state) super().__init__(self.tileBoard, **kwargs) #call parent class with tileboard? return def getInitialBoardState(self): return self.tileBoard.state_tuple() def generateDebugBoard(self, state): return TileBoard(self.size, False, state) def actions(self, state): "actions(state) - find a set of actions applicable to specified state" actionBoard = TileBoard(self.size, False, state) actions = actionBoard.get_actions() return actions def result(self, state, action): "result(state, action)- apply action to state and return new state" actionBoard = TileBoard(self.size, False, state) newboard = actionBoard.move(action) return newboard.state_tuple() def goal_test(self, state): "goal_test(state) - Is state a goal?" testBoard = TileBoard(self.size, False, state) if (testBoard.solved()): return True else: return False
def driver(): start_overall_time = tic() sec_per_min = 60.0 number_of_tileboard_instances = 2 puzzle_size = 8 methods = [BreadthFirst, DepthFirst, Manhattan] list_of_paths = list() list_of_number_of_nodes_explored = list() list_of_times = list() for n in range(number_of_tileboard_instances): board = TileBoard(puzzle_size) state_tuple = board.state_tuple( ) # do this becuase we din't want to modify the original board for method in methods: puzzle = NPuzzle(puzzle_size, forced_state=state_tuple, g=method.g, h=method.h) (list_of_paths[n], list_of_number_of_nodes_explored[n], list_of_times[n]) = solve(puzzle)
def __init__(self, n, force_state=None, **kwargs): """"__init__(n, force_state, **kwargs) NPuzzle constructor. Creates an initial TileBoard of size n. If force_state is not None, the puzzle is initialized to the specified state instead of being generated randomly. The parent's class constructor is then called with the TileBoard instance any any remaining arguments captured in **kwargs. """ super().__init__(TileBoard(n, force_state=force_state), g=kwargs['g'], h=kwargs['h'])
def driver(): p_size = dict() n_size = dict() t = dict() for method in SEARCH_METHODS: t[method] = list() n_size[method] = list() p_size[method] = list() for i in range(TRIAL): board_layout = TileBoard(BOARD_SIZE).state_tuple() for method in SEARCH_METHODS: p = NPuzzle(BOARD_SIZE, g=method.g,h=method.h, force_state=board_layout) start_time = time.perf_counter() path, nodes_explored = graph_search(p) dur = time.perf_counter() - start_time n_size[method].append(nodes_explored) t[method].append(dur) p_size[method].append(len(path)) print('Trial %d via %s - plan %d node %d in %d seconds' %((i+1), method.__name__, len(path), nodes_explored, dur)) for method in SEARCH_METHODS: print('Plan Method %s Mean %f Std %f' %(method.__name__, mean(p_size[method]), stdev(p_size[method]))) print('Nodes Method %s Mean %f Std %f' %(method.__name__, mean(n_size[method]), stdev(n_size[method]))) print('Time Method %s Mean %f Std %f' %(method.__name__, mean(t[method]), stdev(t[method])))
def h(cls, state: TileBoard): manhattan_solution = 0 solved = [[None for c in range(state.cols)] for r in range(state.rows)] index = 1 for row in range(state.boardsize): for col in range(state.boardsize): if row == state.rows // 2 and col == state.cols // 2: solved[row][col] = None else: solved[row][col] = index index += 1 # Calculate distance to solution for row in range(state.boardsize): for col in range(state.boardsize): item = state.get(row, col) for solved_row in range(len(solved)): manhattan_solution += abs(solved_row - row) + \ abs(solved[solved_row].index(item) - col) return manhattan_solution
def __init__(self, n, force_state=None, **kwargs): """"__init__(n, force_state, **kwargs) NPuzzle constructor. Creates an initial TileBoard of size n. If force_state is not None, the puzzle is initialized to the specified state instead of being generated randomly. The parent's class constructor is then called with the TileBoard instance any any remaining arguments captured in **kwargs. """ # Note on **kwargs: # **kwargs is Python construct that captures any remaining arguments # into a dictionary. The dictionary can be accessed like any other # dictionary, e.g. kwargs["keyname"], or passed to another function # as if each entry was a keyword argument: # e.g. foobar(arg1, arg2, …, argn, **kwargs). # initialize parent # def __init__(self, initial, goals=None, # g = lambda oldnode, action, newnode : oldnode.depth+1, # h = lambda newnode : 0): nBoard = TileBoard(n, force_state=force_state) super().__init__(goals=nBoard.goals, g=kwargs['g'], g=kwargs['h'])
def driver(): manTime = [] manLength = [] manNodes = [] depTime = [] depLength = [] depNodes = [] breadTime = [] breadLength = [] breadNodes = [] for i in range(0,31): tb = TileBoard(8) #starting with manhattan manprobs = NPuzzle(8,tb.state_tuple(),g=Manhattan.g,h=Manhattan.h) curTime = tic() #current time manList = graph_search(manprobs) #CONSIDER OTHER PARAMETERS LATER manTime.append(tock(curTime)) manLength.append(len(manList[0])) manNodes.append(manList[1]) #depth_first search depprobs = NPuzzle(8,tb.state_tuple(),g=DepthFirst.g,h=DepthFirst.h) curTime = tic() #current time depList = graph_search(depprobs) #CONSIDER OTHER PARAMETERS LATER depTime.append(tock(curTime)) depLength.append(len(depList[0])) depNodes.append(depList[1]) #breadth_first search breadprobs = NPuzzle(8,tb.state_tuple(),g=BreadthFirst.g,h=BreadthFirst.h) curTime = tic() #current time breadList = graph_search(breadprobs) #CONSIDER OTHER PARAMETERS LATER breadTime.append(tock(curTime)) breadLength.append(len(breadList[0])) breadNodes.append(breadList[1]) #calculate mean and stdevs for each list generated at beginning #manhattan statistics meanmantime = mean(manTime) stdmantime = stdev(manTime,meanmantime) meanmanlength = mean(manLength) stdmanlength = stdev(manLength,meanmanlength) meanmannodes = mean(manNodes) stdmannodes = stdev(manNodes,meanmannodes) #depth-first statistics meandeptime = mean(depTime) stddeptime = stdev(depTime) meandeplength = mean(depLength) stddeplength = stdev(depLength) meandepnodes = mean(depNodes) stddepnodes = stdev(depNodes) #breadth-first statistics meanbreadtime = mean(breadTime) stdbreadtime = stdev(breadTime) meanbreadlength = mean(breadLength) stdbreadlength = stdev(breadLength) meanbreadnodes = mean(breadNodes) stdbreadnodes = stdev(breadNodes) print("Manhattan Statistics - Mean Time: ", meanmantime, "StDev Time: ", stdmantime) print("Manhattan Statistics - Mean Length: ", meanmanlength, "StDev Length: ", stdmanlength) print("Manhattan Statistics - Mean Nodes: ", meanmannodes, "StDev Nodes: ", stdmannodes) print("Depth-First Statistics - Mean Time: ", meandeptime, "StDev Time: ", stddeptime) print("Depth-First Statistics - Mean Length: ", meandeplength, "StDev Length: ", stddeplength) print("Depth-First Statistics - Mean Nodes: ", meandepnodes, "StDev Nodes: ", stddepnodes) print("Breadth-First Statistics - Mean Time: ", meanbreadtime, "StDev Time: ", stdbreadtime) print("Breadth-First Statistics - Mean Length: ", meanbreadlength, "StDev Length: ", stdbreadlength) print("Breadth-First Statistics - Mean Nodes: ", meanbreadnodes, "StDev Nodes: ", stdbreadnodes)
def generateDebugBoard(self, state): return TileBoard(self.size, False, state)
def driver(): # Assign number of trials, size of tileboards, and set verbose / debug flags, and force_states ntrials = 31 n = 8 verbose, debug = False, True f_state = [] # Create puzzle states for ntrials, dummy board to check if solvable for f in range(0, ntrials): dummy = TileBoard(n) done = False while not done: made = False # Create temporary state and check if solvable tmp_state = list(range(1, n + 1)) tmp_state.append(None) random.shuffle(tmp_state) if dummy.solvable(tmp_state): made = done = True else: # Reshuffle temporary state if not solvable random.shuffle(tmp_state) if made: # If solvable, append to f_state list f_state.append(tmp_state) # Initialize empty arrays to hold stat values btime, dtime, atime = [], [], [] bnodes, dnodes, anodes = [], [], [] bsteps, dsteps, asteps = [], [], [] """ Run ntrials: each loop runs a breadth search, depth search, and A* search for each force_state""" for state in f_state: # Initialize timer before each search # Create puzzle with same initial state & costs specific to search type # Run graph search on created puzzle # Add stats to stat arrays bt = Timer() breadthpuzzle = NPuzzle(n, force_state=state, g=BreadthFirst.g, h=BreadthFirst.h) bsearch = graph_search(breadthpuzzle, verbose, debug) bsteps.append(len(bsearch[0])) bnodes.append(bsearch[1]) btime.append(bt.elapsed_s()) dt = Timer() depthpuzzle = NPuzzle(n, force_state=state, g=DepthFirst.h, h=DepthFirst.g) dsearch = graph_search(depthpuzzle, verbose, debug) dsteps.append(len(dsearch[0])) dnodes.append(dsearch[1]) dtime.append(dt.elapsed_s()) at = Timer() astarpuzzle = NPuzzle(n, force_state=state, g=Manhattan.g, h=Manhattan.h) asearch = graph_search(astarpuzzle, verbose, debug) asteps.append(len(asearch[0])) anodes.append(asearch[1]) atime.append(at.elapsed_s()) # Merge each stat array into an array for each search bdata, ddata, adata = [], [], [] """ Compile stats and create table """ if ntrials > 1: # Find mean and standard deviation for each stat of each search bdata = [ mean(bsteps), stdev(bsteps, mean(bsteps)), mean(bnodes), stdev(bnodes, mean(bnodes)), mean(btime), stdev(btime, mean(btime)) ] ddata = [ mean(dsteps), stdev(dsteps, mean(dsteps)), mean(dnodes), stdev(dnodes, mean(dnodes)), mean(dtime), stdev(dtime, mean(dtime)) ] adata = [ mean(asteps), stdev(asteps, mean(asteps)), mean(anodes), stdev(anodes, mean(anodes)), mean(atime), stdev(atime, mean(atime)) ] # Combine all data into one list and round as necessary data = [bdata, ddata, adata] data = [[round(elem, 6) for elem in lis] for lis in data] # Create lists for table labels searches = ["Breadth Search", "Depth Search", "A* Search"] stats = [ "Mean Steps", "Std Steps", "Mean Nodes", "Std Nodes", "Mean Time", "Std Time" ] # Create table using formatted strings and for loop i = 0 row_format = ("{:>14}" * (len(stats) + 1)) print(row_format.format("", *stats)) for stat, row in zip(stats, data): print(row_format.format(searches[i], *row)) i += 1
def goal_test(self, state): "goal_test(state) - Is state a goal?" #create a temporary board and determine if it's solved temporaryboard = TileBoard(self.puzzletype,force_state=state.state_tuple()) return temporaryboard.solved()
def result(self, state, action): "result(state, action)- apply action to state and return new state" #return the state of the board after the action command, #using the board's move command. temporaryboard = TileBoard(self.puzzletype,force_state=state.state_tuple()) return temporaryboard.move(action)
def actions(self, state): "actions(state) - find a set of actions applicable to specified state" #create a temporary board from current state, return its actions temporaryboard = TileBoard(self.puzzletype,force_state=state.state_tuple()) return temporaryboard.get_actions()
def driver(): print("============================= TileBoard Game ==============================") print("CS 550 Artificial Intelligence | Prof. Roch | Assignment 2 | kingofthenorth") print("======================== Going through searches... ========================") plan_size = dict() nodes_size = dict() time = dict() for method in SEARCH_METHODS: plan_size[method] = list() nodes_size[method] = list() time[method] = list() for i in range(TRIAL_SIZE): if INFO: print('Trial #%d commence...' % (i + 1)) # Standard layout for the board board_layout = TileBoard(BOARD_SIZE).state_tuple() # Trying the methods for method in SEARCH_METHODS: if INFO: print('Solving puzzle via %s' % method.__name__) puzzle = NPuzzle(BOARD_SIZE, g=method.g, h=method.h, force_state=board_layout) start_time = tic() path, nodes_explored = graph_search( puzzle, debug=DEBUG, verbose=VERBOSE) duration = tock(start_time) assert path is not None plan_size[method].append(len(path)) nodes_size[method].append(nodes_explored) time[method].append(duration) if INFO: print('Solved puzzle via %s in %d seconds' % (method.__name__, duration)) print('Finished Trial #%d' % (i + 1)) if INFO or DEBUG or VERBOSE: print("\n\n======================== Going through searches... ========================\n\n\n") header = ["Search / Result ", "Plan Size (Mean/STDEV)", "Node Size (Mean/STDEV)", "Time (Mean/STDEV)"] rows = list() # Formatting rows.append(['-' * (len(header[i]) + 1) for i in range(len(header))]) for method in SEARCH_METHODS: rows.append([' '.join(re.sub('(?!^)([A-Z][a-z]+)', r' \1', method.__name__).split()), '{:.3f} / {:.3f}'.format( mean(plan_size[method]), stdev(plan_size[method])), '{:.3f} / {:.3f}'.format( mean(nodes_size[method]), stdev(nodes_size[method])), '{:.3f} / {:.3f}'.format(mean(time[method]), stdev(time[method]))]) print_table(rows, header=header, sep="\t| ")
def driver() : bfsLengthList = [] bfsNodesExpandedList = [] bfsTimeList = [] dfsLengthList = [] dfsNodesExpandedList = [] dfsTimeList = [] manLengthList = [] manNodesExpandedList = [] manTimeList = [] for i in range(2): #change back to 31 tempBoard = TileBoard(8) #force_state=(1,2,3,4,5,6,7,8,None) #Breadth First Search bfs = NPuzzle(8,g=BreadthFirst.g,force_state=tempBoard.state_tuple()) tempTimer = Timer() path,numNodes = graph_search(bfs, verbose=True) bfsTimeList.append(tempTimer.elapsed_s()) bfsNodesExpandedList.append(numNodes) bfsLengthList.append(len(path)) #Depth First Search dfs = NPuzzle(8,g=DepthFirst.g,force_state=tempBoard.state_tuple()) tempTimer = Timer() path,numNodes = graph_search(dfs, verbose=True) dfsTimeList.append(tempTimer.elapsed_s()) dfsNodesExpandedList.append(numNodes) dfsLengthList.append(len(path)) #Manhattan Search manhattan = NPuzzle(8, h=Manhattan.h, force_state=tempBoard.state_tuple()) tempTimer = Timer() path, numNodes = graph_search(manhattan, verbose=True) manTimeList.append(tempTimer.elapsed_s()) manNodesExpandedList.append(numNodes) manLengthList.append(len(path)) print("BFS Average Path Length:",mean(bfsLengthList)) print("BFS Average Nodes Expanded:",mean(bfsNodesExpandedList)) print("BFS Average Time Elapsed:",mean(bfsTimeList)) print("DFS Average Path Length:", mean(dfsLengthList)) print("DFS Average Nodes Expanded:",mean(dfsNodesExpandedList)) print("DFS Average Time Elapsed:",mean(dfsTimeList)) print("Manhattan Average Path Length:", mean(manLengthList)) print("Manhattan Average Nodes Expanded:", mean(manNodesExpandedList)) print("Manhattan Average Time Elapsed:", mean(manTimeList)) print("BFS Standard Deviation Path Length:", stdev(bfsLengthList)) print("BFS Standard Deviation Nodes Expanded:", stdev(bfsNodesExpandedList)) print("BFS Standard Deviation Time Elapsed:", stdev(bfsTimeList)) print("DFS Standard Deviation Path Length:", stdev(dfsLengthList)) print("DFS Standard Deviation Nodes Expanded:", stdev(dfsNodesExpandedList)) print("DFS Standard Deviation Time Elapsed:", stdev(dfsTimeList)) print("Manhattan Standard Deviation Path Length:", stdev(manLengthList)) print("Manhattan Standard Deviation Nodes Expanded:", stdev(manNodesExpandedList)) print("Manhattan Standard Deviation Time Elapsed:", stdev(manTimeList))
def actions(self, state): "actions(state) - find a set of actions applicable to specified state" actionBoard = TileBoard(self.size, False, state) actions = actionBoard.get_actions() return actions
def driver(): length_of_plan = dict() number_of_nodes = dict() elapsed_time = dict() for method in SOLUTION_METHODS: length_of_plan[method] = list() number_of_nodes[method] = list() elapsed_time[method] = list() for i in range(TRIAL_SIZE): if INFO: print('Starting Trial #%d' % (i + 1)) # Standard Config board_layout = TileBoard(TRIAL_BOARD_SIZE).state_tuple() # Random Board - For testing # board_layout = TileBoard(TRIAL_BOARD_SIZE, force_state=[8, None, 6, 5, 4, 7, 2, 3, 1]).state_tuple() # board_layout = TileBoard(TRIAL_BOARD_SIZE, force_state=[7, 3, None, 5, 1, 2, 4, 8, 6]).state_tuple() # Solvable in 1 move # board_layout = TileBoard(TRIAL_BOARD_SIZE, force_state=[1, None, 3, 4, 2, 5, 6, 7, 8]).state_tuple() # Solvable in ~5 moves # board_layout = TileBoard(TRIAL_BOARD_SIZE, force_state=[4, 1, 2, None, 5, 3, 6, 7, 8]).state_tuple() # board_layout = TileBoard(TRIAL_BOARD_SIZE, force_state=[1, None, 3, 7, 2, 5, 4, 6, 8]).state_tuple() # Solvable in ~15 moves # board_layout = TileBoard(TRIAL_BOARD_SIZE, force_state=[5, 3, 7, None, 1, 2, 4, 6, 8]).state_tuple() for method in SOLUTION_METHODS: if INFO: print('Solving puzzle via %s' % method.__name__) puzzle = NPuzzle(TRIAL_BOARD_SIZE, g=method.g, h=method.h, force_state=board_layout) start_time = tic() path, nodes_explored = graph_search(puzzle, debug=DEBUG, verbose=VERBOSE) duration = tock(start_time) assert path is not None length_of_plan[method].append(len(path)) number_of_nodes[method].append(nodes_explored) elapsed_time[method].append(duration) if INFO: print('Solved puzzle via %s in %d seconds' % (method.__name__, duration)) print('Finished Trial #%d' % (i + 1)) if INFO or DEBUG or VERBOSE: print("\n\n==================================================\n\n\n") header = [ "Method / Result ", "Length of Plan (Mean/STDEV)", "Number of Nodes (Mean/STDEV)", "Elapsed Time (Mean/STDEV)" ] rows = list() # Header Separator rows.append(['-' * (len(header[i]) + 1) for i in range(len(header))]) # Table Values for method in SOLUTION_METHODS: rows.append([ ' '.join( re.sub('(?!^)([A-Z][a-z]+)', r' \1', method.__name__).split()), '{:.3f} / {:.3f}'.format(mean(length_of_plan[method]), stdev(length_of_plan[method])), '{:.3f} / {:.3f}'.format(mean(number_of_nodes[method]), stdev(number_of_nodes[method])), '{:.3f} / {:.3f}'.format(mean(elapsed_time[method]), stdev(elapsed_time[method])) ]) print_table(rows, header=header, sep="\t| ")
def driver(): print( "-------------------------- TileBoard searching...-----------------------------------" ) # declarations path_length = dict() num_nodes = dict() elapsed_sec = dict() elapsed_min = dict() for method in search_method: path_length[method] = list() num_nodes[method] = list() elapsed_sec[method] = list() elapsed_min[method] = list() for i in range(num_puzzle): print('\n###Puzzle %d###' % (i + 1)) for method in search_method: if method.__name__ is 'Manhattan': name = 'A*' else: name = method.__name__ print('Solving puzzle using %s' % name) # set the puzzle npuzzle = NPuzzle(board_size, g=method.g, h=method.h, force_state=TileBoard(board_size).state_tuple()) t = Timer() # (1,2,3,None,4,6,7,5,8) solve in 3 moves """--------------------One Path Demo using A---------------------------* temp = False if method.__name__ is 'Manhattan': temp = True path, nodes_explored = graph_search(npuzzle, debug=False, verbose=temp) ----------------------------------------------------------------------""" path, nodes_explored = graph_search(npuzzle, debug=False, verbose=False) if method.__name__ is 'Manhattan': name = "A*" else: name = method.__name__ print('Puzzle Solved in %d sec or %d min' % (t.elapsed_s(), t.elapsed_min())) assert path is not None path_length[method].append(len(path)) num_nodes[method].append(nodes_explored) elapsed_sec[method].append(t.elapsed_s()) elapsed_min[method].append(t.elapsed_min()) # use pandas tabulate and print table pretty data = list() # round 2digit and formatting for method in search_method: if method.__name__ is 'Manhattan': name = 'A*' else: name = method.__name__ # create list according to column names data.append([ ' '.join(re.sub('(?!^)([A-Z][a-z]+)', r' \1', name).split()), '{:.2f} / {:.2f}'.format(mean(path_length[method]), stdev(path_length[method])), '{:.2f} / {:.2f}'.format(mean(num_nodes[method]), stdev(num_nodes[method])), '{:.2f} / {:.2f}'.format(mean(elapsed_sec[method]), stdev(elapsed_sec[method])), '{:.2f} / {:.2f}'.format(mean(elapsed_min[method]), stdev(elapsed_min[method])) ]) df_data = pd.DataFrame(data) df_data.columns = [ 'Search Type', 'Path_Length (Mean/Stdev)', 'Node_Explored (Mean/Stdev)', 'Time in Sec(Mean/Stdev)', 'Time in Min(Mean/Stdev)' ] pd_tabulate = lambda df_data: tabulate( df_data, headers='keys', tablefmt='psql') print(pd_tabulate(df_data))
def result(self, state, action): "result(state, action)- apply action to state and return new state" actionBoard = TileBoard(self.size, False, state) newboard = actionBoard.move(action) return newboard.state_tuple()
class NPuzzle(Problem): """ NPuzzle - Problem representation for an N-tile puzzle Provides implementations for Problem actions specific to N tile puzzles. """ def __init__(self, n, force_state=None, **kwargs): """"__init__(n, force_state, **kwargs) NPuzzle constructor. Creates an initial TileBoard of size n. If force_state is not None, the puzzle is initialized to the specified state instead of being generated randomly. The parent's class constructor is then called with the TileBoard instance and any remaining arguments captured in **kwargs. """ # Instantiate Tileboard self.puzzle = TileBoard(n, force_state=force_state) # Initialize parent class super().__init__(self.puzzle.state_tuple(), self.puzzle.goals, kwargs["g"], kwargs["h"]) # Note on **kwargs: # **kwargs is Python construct that captures any remaining arguments # into a dictionary. The dictionary can be accessed like any other # dictionary, e.g. kwargs["keyname"], or passed to another function # as if each entry was a keyword argument: # e.g. foobar(arg1, arg2, …, argn, **kwargs). def actions(self, state): """actions(state) - find a set of actions applicable to specified state""" # Convert state tuple to 2D list boarddims = [self.puzzle.get_rows(), self.puzzle.get_cols()] state_list = [ state[i:i + boarddims[0]] for i in range(0, len(state), boarddims[0]) ] # Find empty tile coordinates found = False for y in range(len(state_list)): for x in range(len(state_list[y])): if state_list[y][x] is None: empty = [y, x] found = True break if found: break actions = [] # Initialize empty list for actions for dim in [0, 1]: # rows, then columns # Append offsets to the actions list, # e.g. move left --> (0, -1) # move down --> (1, 0) # Note that when we append to the list of actions, # we use list( ) to make a copy of the list, otherwise # we just get a pointer to it and modification of offset # will change copies in the list. offset = [0, 0] # Add if we don't go off the top or left if empty[dim] - 1 >= 0: offset[dim] = -1 actions.append(list(offset)) # Append if we don't go off the bottom or right if empty[dim] + 1 < boarddims[dim]: offset[dim] = 1 actions.append(list(offset)) return actions def result(self, state, action): """result(state, action)- apply action to state and return new state""" # Convert tuple into 2D list n = self.puzzle.get_rows() state_list = [list(state[i:i + n]) for i in range(0, len(state), n)] # Find empty tile coordinates r = c = 0 found = False for y in range(len(state_list)): for x in range(len(state_list[y])): if state_list[y][x] is None: r, c = y, x found = True break if found: break [delta_r, delta_c] = action # Validate actions rprime = r + delta_r cprime = c + delta_c if rprime < 0 or cprime < 0 or \ rprime >= n or cprime >= n: raise ValueError("Illegal move (%d,%d) from (%d,%d)" % (delta_r, delta_c, r, c)) # Apply move offset accordingly if delta_r != 0: # Move up or down state_list[r][c] = state_list[rprime][c] state_list[rprime][c] = None elif delta_c != 0: # Move left or right state_list[r][c] = state_list[r][cprime] state_list[r][cprime] = None # Create new state after moving empty new_state = [item for sublist in state_list for item in sublist] # Convert to tuple (hashable) and return return tuple(new_state) def goal_test(self, state): """goal_test(state) - Is state a goal?""" # If state is in puzzle goals, return True goal = state in self.puzzle.goals return goal def value(self, state): pass