from __future__ import annotations from typing import List, Set from project_constants import Direction, Action from minefield import Minefield # temporary goal for testing GOAL = (2, 2) class State: def __init__(self, row, column, direction: Direction): self.row = row self.column = column self.direction = direction # def __eq__(self, other): # if not isinstance(other, State): # # don't attempt to compare against unrelated types # return NotImplemented # return self.row == other.row and self.column == other.column and self.direction == other.direction class Node: def __init__(self, state: State, parent: Node = None, action: Action = None): self.state = state self.parent = parent self.action = action # def __eq__(self, other): # if not isinstance(other, Node): # # don't attempt to compare against unrelated types # return NotImplemented # return self.state == other.state and self.parent == other.parent and self.action == other.action def goal_test(state: State): if (state.row, state.column) == GOAL: return True return False def get_successors(state: State, minefield: Minefield): successors = list() state_left = State(state.row, state.column, state.direction.previous()) successors.append((Action.ROTATE_LEFT, state_left)) state_right = State(state.row, state.column, state.direction.next()) successors.append((Action.ROTATE_RIGHT, state_right)) target = go(state.row, state.column, state.direction) if minefield.is_valid_move(target[0], target[1]): state_go = State(target[0], target[1], state.direction) successors.append((Action.GO, state_go)) return successors def graphsearch(initial_state: State, minefield: Minefield, fringe: List[Node] = None, explored: List[Node] = None): # fringe and explored initialization if fringe is None: fringe = list() if explored is None: explored = list() # root Node fringe.append(Node(initial_state)) while True: # fringe empty -> solution not found if not len(fringe): return False # get first element from fringe element = fringe.pop(0) # if solution was found, prepare and return actions sequence if goal_test(element.state): actions_sequence = [element.action] parent = element.parent while parent is not None: # root'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) # loop through every possible next action for successor in get_successors(element.state, minefield): # either me or the pseudocode is dumb # somebody has to verify it fringe_states = [el.state for el in fringe] explored_states = [el.state for el in explored] # make sure not to fall into a cycle if successor[1] not in fringe_states and \ successor[1] 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) # 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