from __future__ import annotations from typing import List from common.constants import ACTION, Direction, ROWS, COLUMNS class State: def __init__(self, row, column, direction): self.row = row self.column = column self.direction = direction class Node: def __init__(self, state, parent=None, action=None): self.state = state self.parent = parent self.action = action def goal_test(goal_list, state: State): if (state.row, state.column) in goal_list: return True return False def get_successors(state: State, map): successors = list() state_left = State(state.row, state.column, state.direction.left()) successors.append((ACTION.get("rotate_left"), state_left)) state_right = State(state.row, state.column, state.direction.right()) successors.append((ACTION.get("rotate_right"), state_right)) target = go(state.row, state.column, state.direction) if is_valid_move(map, target[0], target[1]): state_go = State(target[0], target[1], state.direction) successors.append((ACTION.get("go"), state_go)) return successors def graphsearch(initial_state: State, map, goal_list, fringe: List[Node] = None, explored: List[Node] = None): # fringe and explored initialization if fringe is None: fringe = list() if explored is None: explored = list() explored_states = set() fringe_states = set() # train Node fringe.append(Node(initial_state)) fringe_states.add((initial_state.row, initial_state.column, initial_state.direction)) while True: # fringe empty -> solution not found if not any(fringe): print("Brak rozwiazania") return [] # get first element from fringe element = fringe.pop(0) fringe_states.remove((element.state.row, element.state.column, element.state.direction)) # if solution was found, prepare and return actions sequence if goal_test(goal_list, element.state): actions_sequence = [element.action] parent = element.parent while parent is not None: # train's action will be None, don't add it if parent.action is not None: actions_sequence.append(parent.action) parent = parent.parent actions_sequence.reverse() return actions_sequence # add current node to explored (prevents infinite cycles) explored.append(element) explored_states.add((element.state.row, element.state.column, element.state.direction)) # loop through every possible next action for successor in get_successors(element.state, map): # make sure not to fall into a cycle successor_state = (successor[1].row, successor[1].column, successor[1].direction) if successor_state not in fringe_states and successor_state not in explored_states: # create new Node and add it at the end of fringe new_node = Node(state=successor[1], parent=element, action=successor[0]) fringe.append(new_node) fringe_states.add((new_node.state.row, new_node.state.column, new_node.state.direction)) # TEMPORARY METHOD def go(row, column, direction): target = tuple() if direction == Direction.RIGHT: target = row, column + 1 elif direction == Direction.LEFT: target = row, column - 1 elif direction == Direction.UP: target = row - 1, column elif direction == Direction.DOWN: target = row + 1, column return target def is_valid_move(map, target_row, target_column): if 0 <= target_row < ROWS and 0 <= target_column < COLUMNS and map[target_row][target_column] in ['g', 's', ' ']: return True return False