def goal_test(self, state): """Return True if the state is a goal. The default method compares the state to self.goal, as specified in the constructor. Override this method if checking against a single self.goal is not enough.""" new_wh1 = sokoban.Warehouse() new_wh1.extract_locations(state.split(sep="\n")) new_wh2 = sokoban.Warehouse() new_wh2.extract_locations(self.goal.split(sep="\n")) return set(new_wh1.boxes) == set(new_wh2.targets)
def h(n): # Perform a manhattan distance heuristic state = n.state[1] wh = sokoban.Warehouse() wh.extract_locations(state.split('\n')) num_targets = len(wh.targets) heuristic = 0 test = 1 for box in wh.boxes: # dist = 0 # for target in wh.targets: # dist+= manhattan_distance(box, target) # heuristic += (dist/num_targets) if test == 1: dist = 0 for target in wh.targets: dist += manhattan_distance(box, target) heuristic += 0.8 * (dist / num_targets) + 0.5 * manhattan_distance( warehouse.worker, box) else: dist1 = [] for target in wh.targets: dist1.append(manhattan_distance(box, target)) heuristic += 0.8 * min(dist1) + 0.5 * manhattan_distance( warehouse.worker, box) return heuristic
def heuristic(node): # Using Manhattan Distance for heuristics nodeState = node.state warehouseFromNode = sokoban.Warehouse() warehouseFromNode.extract_locations(nodeState) warehouseTargets = warehouseFromNode.targets warehouseTargetsCount = len(warehouseTargets) warehouseBoxes = warehouseFromNode.boxes warehouseWorker = warehouseFromNode.worker heuristicValue = 0 for warehouseBox in warehouseBoxes: totalBoxDistance = 0 aSquared = abs(warehouseBox[0] - warehouseWorker[0]) bSquared = abs(warehouseBox[1] - warehouseWorker[1]) workerDistanceToBox = (aSquared + bSquared)**0.5 for warehouseTarget in warehouseTargets: aSquared = abs(warehouseBox[0] - warehouseTarget[0]) bSquared = abs(warehouseBox[1] - warehouseTarget[1]) manhattanDistanceSingleBox = (aSquared + bSquared)**0.5 totalBoxDistance += manhattanDistanceSingleBox heuristicValue += (totalBoxDistance / warehouseTargetsCount) * workerDistanceToBox return heuristicValue
def result(self, state, action): warehouse = sokoban.Warehouse() warehouse.extract_locations(state.splitlines()) box_position = action[0] action = action[1] box_id = warehouse.boxes.index(box_position) warehouse.boxes.remove(box_position) if action == 'Right': move = movement[0] new_box_position = (box_position[0] + move[0], box_position[1] + move[1]) if action == "Left": move = movement[1] new_box_position = (box_position[0] + move[0], box_position[1] + move[1]) if action == 'Down': move = movement[2] new_box_position = (box_position[0] + move[0], box_position[1] + move[1]) if action == 'Up': move = movement[3] new_box_position = (box_position[0] + move[0], box_position[1] + move[1]) warehouse.worker = box_position warehouse.boxes.insert(box_id, new_box_position) # print(str(warehouse)) return str(warehouse)
def actions(self, state): """ Return the list of actions that can be executed in the given state. As specified in the header comment of this class, the attributes 'self.allow_taboo_push' and 'self.macro' should be tested to determine what type of list of actions is to be returned. """ warehouse = sokoban.Warehouse() warehouse.extract_locations(state.splitlines()) # print(warehouse) # print(state) taboo = set( sokoban.find_2D_iterator( taboo_cells(str(warehouse)).splitlines(), "X")) for box in warehouse.boxes: for move in movement: worker_position = (box[0] - move[0], box[1] - move[1]) box_after_pushed = (box[0] + move[0], box[1] + move[1]) # print(taboo) if (can_go_there(warehouse, worker_position) and box_after_pushed not in taboo and box_after_pushed not in warehouse.walls and box_after_pushed not in warehouse.boxes): yield (box, direction(move))
def result(self, state, action): # NO TESTING - YOU ALREADY KNOW THAT THIS IS A VALID MOVE!!! stateArray = state.split('\n') warehouseObject = sokoban.Warehouse() warehouseObject.extract_locations(stateArray) workerPos = warehouseObject.worker # These are in (y, x) format moveLeft = ((-1, 0), 'Left') moveRight = ((1, 0), 'Right') moveUp = ((0, -1), 'Up') moveDown = ((0, 1), 'Down') possibleMoves = [moveLeft, moveRight, moveUp, moveDown] if type(action) == tuple: actionString = action[1] moveFrom = action[0][1], action[0][0] else: actionString = action for movePair in possibleMoves: if movePair[1] == actionString: move = movePair[0] break # if marco true - action = [ ((3,4), 'Left'), ((5,2), 'Right')] if self.macro: boxArrayPosition = warehouseObject.boxes.index(moveFrom) newBoxPosition = moveFrom[0] + move[0], moveFrom[1] + move[1] warehouseObject.boxes[boxArrayPosition] = newBoxPosition warehouseObject.worker = moveFrom return str(warehouseObject) # if marco false - action = [ 'Left', 'Right' ] else: newWorkerPosition = workerPos[0] + move[0], workerPos[1] + move[1] testPos = stateArray[newWorkerPosition[1]][newWorkerPosition[0]] if testPos == ' ' or testPos == '.': # nothing more to do, just update state with worker = testPos warehouseObject.worker = newWorkerPosition return str(warehouseObject) elif testPos == '$' or testPos == '*': # need to shift box before moving worker to this location assert newWorkerPosition in warehouseObject.boxes boxArrayPosition = warehouseObject.boxes.index( newWorkerPosition) newBoxPosition = newWorkerPosition[0] + move[ 0], newWorkerPosition[1] + move[1] warehouseObject.boxes[boxArrayPosition] = newBoxPosition warehouseObject.worker = newWorkerPosition[ 0], newWorkerPosition[1] return str(warehouseObject) # The code should never EVER get here! print("STOP!!!!!!, you should never see this...") print( "The possible actions failed to correctly find the actions available" ) assert False
def warehouse_deepcopy(warehouse): result = sokoban.Warehouse() result.worker = (warehouse.worker[0], warehouse.worker[1]) boxesInternal = [] for i in warehouse.boxes: boxesInternal.append((i[0], i[1])) result.boxes = boxesInternal result.targets = warehouse.targets result.walls = warehouse.walls return result
def h(self, n): heur = 0 new_wh = sokoban.Warehouse() new_wh.extract_locations(n.state.split(sep="\n")) for box in new_wh.boxes: temp_target = new_wh.targets[0] for target in new_wh.targets: if (manhattan_distance(target, box) < manhattan_distance( temp_target, box)): temp_target = target heur = heur + manhattan_distance(temp_target, box) return heur
def h(self, n): h = 0 wh = sokoban.Warehouse() self.initial_box # print(wh.boxes) for target in self.targets: for box in self.initial_box: # print(box) ith_box = self.initial_box.index(box) h = (manhattan_distance(box, target) * push_cost[ith_box]) + h return h
def actions(self, state): warehouse = sokoban.Warehouse() warehouse.extract_locations(state.splitlines()) taboo = set(sokoban.find_2D_iterator(taboo_cells(str(warehouse)), "X")) for box in warehouse.boxes: for move in movement: worker_position = (box[0] - move[0], box[1] - move[1]) box_after_pushed = (box[0] + move[0], box[1] + move[1]) if (can_go_there(warehouse, worker_position) and box_after_pushed not in taboo and box_after_pushed not in warehouse.walls and box_after_pushed not in warehouse.boxes): yield (box, direction(move))
def h(self, n): # print(node) # print(' ') # print(self.initial) h = 0 wh = sokoban.Warehouse() wh.extract_locations(n.state.splitlines()) # print(wh.boxes) # print(wh.boxes) # print(n.state) for target in wh.targets: for box in wh.boxes: # print(box) h = manhattan_distance(box, target) + h return h
def __init__(self, initial, goal, push_cost): self.initial = initial self.goal = goal.replace('@', ' ') self.push_cost = push_cost wh = sokoban.Warehouse() wh.extract_locations(initial.splitlines()) self.initial_box = wh.boxes self.targets = wh.targets self.coll = { 'Left': (-1, 0), 'Right': (1, 0), 'Up': (0, -1), 'Down': (0, 1) } self.box_push_cost = 0
def result(self, state, action): # Extract the warehouse from the current state. current_warehouse = sokoban.Warehouse() current_warehouse.extract_locations(state[1].split(sep="\n")) box = action[0] if box in current_warehouse.boxes: # Remove the old box, set the worker to the position, and add the # new box position. This moves the box with the worker. offset = direction_to_offset(action[1]) current_warehouse.worker = box current_warehouse.boxes.remove(box) current_warehouse.boxes.append(add_tuples(box, offset)) return action, str(current_warehouse) else: # If the box isn't in the warehouse, something went very wrong. print(str(current_warehouse)) print(box) print(current_warehouse.boxes) raise ValueError("Box not in warehouse!")
def path_cost(self, c, state1, action, state2): """Return the cost of a solution path that arrives at state2 from state1 via action, assuming cost c to get up to state1. If the problem is such that the path doesn't matter, this function will only look at state2. If the path does matter, it will consider c and maybe state1 and action. The default method costs 1 for every step in the path.""" new_wh2 = sokoban.Warehouse() new_wh2.extract_locations(state2.split(sep="\n")) if self.push_costs == None: return c + 1 else: for i in range(len(self.ListofLocation)): if self.ListofLocation[i] not in new_wh2.boxes: push_cost = self.push_costs[i] for box in new_wh2.boxes: if box not in self.ListofLocation: self.ListofLocation[i] = box return c + push_cost return c + 1
def actions(self, state): """ Returns the list of actions that can be executed in the given state. """ #Warehouse current state the_warehouse = sokoban.Warehouse() #Positional information of boxes, worker, targets and walls extracted the_warehouse.extract_locations(state[1].split(sep="\n")) #If allow_taboo_push is True, return all legal moves including the box on a taboo cell if self.allow_taboo_push: #Find bad cell spots is_cell_taboo = set( find_2D_iterator(taboo_cells(the_warehouse), "X")) #find directions for the box for box in the_warehouse.boxes: for offset in worker_offsets: offset = offset_of_direction(offset) b_position = add_coordinates(box, offset) ppos1 = (box[0] + offset[0] * -1) ppos0 = (box[1] + offset[1] * -1) p_position = (ppos1, ppos0) p_position = flip_coordinates(p_position) if can_go_there(the_warehouse, p_position): if b_position not in is_cell_taboo: if b_position not in the_warehouse.boxes: if b_position not in the_warehouse.walls: yield (box, direction_of_offset(offset)) #if allow_taboo_push is False, taboo and shouldn't be included in list of moves. elif not self.allow_taboo_push: #find directions for the box for box in the_warehouse.boxes: for offset in worker_offsets: b_position = add_coordinates(box, offset) p_position = flip_coordinates((box[0] + offset[0] * -1), (box[1] + offset[1] * -1)) if can_go_there(the_warehouse, p_position): if b_position not in the_warehouse.boxes: if b_position not in the_warehouse.walls: yield (box, direction_of_offset(offset))
def result(self, state, move): ''' Move is the direction of the object moved by the worker ''' #Warehouse current state the_warehouse = sokoban.Warehouse() #Positional information of boxes, worker, targets and walls extracted the_warehouse.extract_locations(state[1].split(sep="\n")) #remove the box from its old position, set it to the character's offset direction #set the boxes' old position to the worker position = move[0] if position in the_warehouse.boxes: the_warehouse.worker = position the_warehouse.boxes.remove(position) offset_position = offset_of_direction(move[1]) the_warehouse.boxes.append( add_coordinates(position, offset_position)) return move, str(the_warehouse) else: raise ValueError("Box is outside the Warehouse")
def actions(self, state): # Extract the warehouse from the current state. current_warehouse = sokoban.Warehouse() current_warehouse.extract_locations(state[1].split(sep="\n")) global bad_cells if bad_cells is None: # If taboo cells haven't been computed, compute them. # This is done once for optimisation purposes. bad_cells = set( find_2D_iterator( taboo_cells(current_warehouse).split("\n"), "X")) # Find every box, and every direction it can be pushed. for box in current_warehouse.boxes: for offset in offset_states: player_position = flip_tuple( (box[0] + (offset[0] * -1), box[1] + (offset[1] * -1))) new_box_position = add_tuples(box, offset) if can_go_there(current_warehouse, player_position) \ and new_box_position not in bad_cells \ and new_box_position not in current_warehouse.walls \ and new_box_position not in current_warehouse.boxes: yield (box, offset_to_direction(offset))
def main(): wh = sokoban.Warehouse() wh.load_warehouse("./warehouses/warehouse_57.txt") solve_sokoban_elem(wh) solve_sokoban_macro(wh)
def actions(self, state): """ Return the list of actions that can be executed in the given state. As specified in the header comment of this class, the attributes 'self.allow_taboo_push' and 'self.macro' should be tested to determine what type of list of actions is to be returned. """ # These are in (y, x) format moveLeft = (-1, 0) moveRight = (1, 0) moveUp = (0, -1) moveDown = (0, 1) possibleMoves = [moveLeft, moveRight, moveUp, moveDown] nodeAsString = state.split('\n') warehouseObject = sokoban.Warehouse() warehouseObject.extract_locations(nodeAsString) justTabooCells = taboo_cells(warehouseObject) tabooCells = list(sokoban.find_2D_iterator(justTabooCells.split('\n'), 'X')) \ if not self.allow_taboo_push else [] currentWalls = warehouseObject.walls currentBoxes = warehouseObject.boxes currentWorker = warehouseObject.worker if self.macro: for box in currentBoxes: for move in possibleMoves: testNewBoxPosition = box[0] + move[0], box[1] + move[1] # Need to reverse the tuple to be in a (x,y) format testNewPlayerPosition = box[1] + (move[1] * -1), box[0] + ( move[0] * -1) canIGetThere = can_go_there(warehouseObject, testNewPlayerPosition) if canIGetThere and testNewBoxPosition not in currentWalls \ and testNewBoxPosition not in currentBoxes \ and testNewBoxPosition not in tabooCells: revBox = box[1], box[0] if move == moveLeft: yield (revBox, "Left") elif move == moveRight: yield (revBox, "Right") elif move == moveUp: yield (revBox, "Up") elif move == moveDown: yield (revBox, "Down") else: for move in possibleMoves: # Need to reverse the tuple to be in a (x,y) format testNewPlayerPosition = currentWorker[0] + move[ 0], currentWorker[1] + move[1] if testNewPlayerPosition not in currentWalls: if testNewPlayerPosition not in currentBoxes: if move == moveLeft: yield ("Left") elif move == moveRight: yield ("Right") elif move == moveUp: yield ("Up") elif move == moveDown: yield ("Down") else: # If there is a box in the way, make sure the box can legally be moved newBoxPosition = testNewPlayerPosition[0] + move[ 0], testNewPlayerPosition[1] + move[1] if newBoxPosition not in currentWalls and \ newBoxPosition not in currentBoxes and \ newBoxPosition not in tabooCells: if move == moveLeft: yield ("Left") elif move == moveRight: yield ("Right") elif move == moveUp: yield ("Up") elif move == moveDown: yield ("Down")
wh = sokoban.Warehouse() self.initial_box # print(wh.boxes) for target in self.targets: for box in self.initial_box: # print(box) ith_box = self.initial_box.index(box) h = (manhattan_distance(box, target) * push_cost[ith_box]) + h return h def value(self, state): return 1 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if __name__ == '__main__': puzzle_t3 = ''' ####### #@ $ .# #. $ # #######''' problem_file = "./warehouses/warehouse_03_impossible.txt" wh = sokoban.Warehouse() wh.load_warehouse(problem_file) push_cost = [1, 10] # wh.extract_locations(puzzle_t1.split(sep='\n')) print('\nElementary solution') answer = solve_sokoban_elem(wh) print(answer)