Projekt_Sztuczna_Inteligencja/searching_algorithms/bfs.py

128 lines
3.9 KiB
Python
Raw Normal View History

2021-04-14 23:45:34 +02:00
from __future__ import annotations
from typing import List, Set
from project_constants import Direction, Action
from minefield import Minefield
2021-04-14 23:45:34 +02:00
# temporary goal for testing
GOAL = (2, 2)
2021-04-14 23:45:34 +02:00
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
2021-04-14 23:45:34 +02:00
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
2021-04-14 23:45:34 +02:00
def goal_test(state: State):
if (state.row, state.column) == GOAL:
return True
return False
def get_successors(state: State, minefield: Minefield):
2021-04-14 23:45:34 +02:00
successors = list()
state_left = State(state.row, state.column, state.direction.previous())
successors.append((Action.ROTATE_LEFT, state_left))
2021-04-14 23:45:34 +02:00
state_right = State(state.row, state.column, state.direction.next())
successors.append((Action.ROTATE_RIGHT, state_right))
2021-04-14 23:45:34 +02:00
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))
2021-04-14 23:45:34 +02:00
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()
2021-04-14 23:45:34 +02:00
# root Node
2021-04-14 23:45:34 +02:00
fringe.append(Node(initial_state))
while True:
# fringe empty -> solution not found
if not len(fringe):
return False
# get first element from fringe
2021-04-14 23:45:34 +02:00
element = fringe.pop(0)
# if solution was found, prepare and return actions sequence
2021-04-14 23:45:34 +02:00
if goal_test(element.state):
actions_sequence = [element.action]
2021-04-14 23:45:34 +02:00
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
2021-04-14 23:45:34 +02:00
# add current node to explored (prevents infinite cycles)
explored.append(element)
2021-04-14 23:45:34 +02:00
# loop through every possible next action
for successor in get_successors(element.state, minefield):
2021-04-14 23:45:34 +02:00
# 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:
2021-04-14 23:45:34 +02:00
# create new Node and add it at the end of fringe
new_node = Node(state=successor[1],
2021-04-14 23:45:34 +02:00
parent=element,
action=successor[0])
2021-04-14 23:45:34 +02:00
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