2022-04-28 01:50:56 +02:00
|
|
|
from dataclasses import dataclass, field
|
|
|
|
from typing import List, Any
|
|
|
|
from typing import Tuple
|
2022-04-27 01:26:25 +02:00
|
|
|
|
|
|
|
from data.Direction import Direction
|
|
|
|
from data.GameConstants import GameConstants
|
|
|
|
from decision.ActionType import ActionType
|
|
|
|
from util.PathDefinitions import GridLocation
|
|
|
|
from util.PriorityQueue import PriorityQueue
|
2022-04-28 01:50:56 +02:00
|
|
|
|
|
|
|
|
|
|
|
@dataclass(order=True)
|
|
|
|
class PrioritizedItem:
|
|
|
|
priority: float
|
|
|
|
item: Any = field(compare=False)
|
2022-04-27 01:26:25 +02:00
|
|
|
|
|
|
|
|
|
|
|
class PathFinderState:
|
|
|
|
|
2022-04-28 00:05:12 +02:00
|
|
|
def __init__(self, agent_position: GridLocation, agent_direction: Direction, cost: float,
|
|
|
|
last_action: ActionType, action_taken: List[ActionType]):
|
2022-04-27 01:26:25 +02:00
|
|
|
super().__init__()
|
|
|
|
self.agent_position = agent_position
|
|
|
|
self.agent_direction = agent_direction
|
|
|
|
self.cost = cost
|
2022-04-28 00:05:12 +02:00
|
|
|
self.last_action = last_action
|
|
|
|
self.action_taken = action_taken
|
2022-04-27 01:26:25 +02:00
|
|
|
|
|
|
|
|
|
|
|
class PathFinderOnStates:
|
|
|
|
def __init__(self, game_constants: GameConstants, goal: GridLocation, root_state: PathFinderState):
|
|
|
|
super().__init__()
|
|
|
|
self.game_constants = game_constants
|
|
|
|
self.goal = goal
|
|
|
|
self.queue = PriorityQueue()
|
2022-04-28 01:50:56 +02:00
|
|
|
self.queue.put(PrioritizedItem(root_state.cost, root_state), root_state.cost)
|
2022-04-27 01:26:25 +02:00
|
|
|
|
2022-04-28 00:05:12 +02:00
|
|
|
def heuristic(self, a: Tuple[int, int], b: Tuple[int, int]) -> float:
|
2022-04-27 01:26:25 +02:00
|
|
|
# tutaj mozna uzyc heury np. manhatan distance (zmodyfikowany bo masz obroty a to zmienia oplacalnosc)
|
2022-04-28 00:05:12 +02:00
|
|
|
(x1, y1) = a
|
|
|
|
(x2, y2) = b
|
|
|
|
return abs(x1 - x2) + abs(y1 - y2)
|
|
|
|
|
|
|
|
def evaluate(self, currState: PathFinderState) -> float:
|
|
|
|
# koszt dojscia do danego stanu+ heura
|
2022-04-28 20:34:03 +02:00
|
|
|
return currState.cost + self.heuristic(currState.agent_position, self.goal)
|
2022-04-28 00:05:12 +02:00
|
|
|
|
|
|
|
def getPositionAfterMove(self, currState: PathFinderState) -> GridLocation:
|
2022-04-28 01:50:56 +02:00
|
|
|
if currState.agent_direction == Direction.top:
|
|
|
|
return currState.agent_position[0], currState.agent_position[1] + 1
|
|
|
|
|
|
|
|
elif currState.agent_direction == Direction.down:
|
|
|
|
return currState.agent_position[0], currState.agent_position[1] - 1
|
|
|
|
|
|
|
|
elif currState.agent_direction == Direction.right:
|
|
|
|
return currState.agent_position[0] + 1, currState.agent_position[1]
|
|
|
|
|
|
|
|
elif currState.agent_direction == Direction.left:
|
|
|
|
return currState.agent_position[0] - 1, currState.agent_position[1]
|
2022-04-28 00:05:12 +02:00
|
|
|
|
2022-04-28 00:29:29 +02:00
|
|
|
def isMovePossible(self, currState: PathFinderState) -> bool:
|
|
|
|
positionAfterMove = self.getPositionAfterMove(currState)
|
2022-04-28 00:05:12 +02:00
|
|
|
if positionAfterMove in self.game_constants.walls:
|
|
|
|
return False
|
2022-04-28 00:29:29 +02:00
|
|
|
elif positionAfterMove[0] < 0 or positionAfterMove[0] > self.game_constants.grid_width:
|
2022-04-28 00:05:12 +02:00
|
|
|
return False
|
2022-04-28 00:29:29 +02:00
|
|
|
elif positionAfterMove[1] < 0 or positionAfterMove[1] > self.game_constants.grid_height:
|
2022-04-28 00:05:12 +02:00
|
|
|
return False
|
|
|
|
else:
|
|
|
|
return True
|
|
|
|
|
|
|
|
def createState(self, currState: PathFinderState, action: ActionType) -> PathFinderState:
|
2022-04-28 20:34:03 +02:00
|
|
|
if currState.agent_position in self.game_constants.diffTerrain:
|
|
|
|
cost = currState.cost + 5
|
|
|
|
else:
|
|
|
|
cost = currState.cost + 1
|
2022-04-28 00:05:12 +02:00
|
|
|
last_action = action
|
2022-04-28 00:29:29 +02:00
|
|
|
action_taken: List[ActionType] = []
|
|
|
|
action_taken.extend(currState.action_taken)
|
2022-04-28 01:50:56 +02:00
|
|
|
action_taken.append(last_action)
|
2022-04-28 00:05:12 +02:00
|
|
|
agent_position = currState.agent_position
|
|
|
|
agent_direction = currState.agent_direction
|
|
|
|
|
|
|
|
if action == ActionType.ROTATE_UP:
|
|
|
|
agent_direction = Direction.top
|
|
|
|
elif action == ActionType.ROTATE_DOWN:
|
|
|
|
agent_direction = Direction.down
|
|
|
|
elif action == ActionType.ROTATE_LEFT:
|
|
|
|
agent_direction = Direction.left
|
|
|
|
elif action == ActionType.ROTATE_RIGHT:
|
|
|
|
agent_direction = Direction.right
|
|
|
|
elif action == ActionType.MOVE:
|
|
|
|
agent_position = self.getPositionAfterMove(currState)
|
|
|
|
|
|
|
|
return PathFinderState(agent_position, agent_direction, cost, last_action, action_taken)
|
|
|
|
|
|
|
|
def expansion(self, currState: PathFinderState) -> List[PathFinderState]:
|
2022-04-27 01:26:25 +02:00
|
|
|
# dla stanu sprawdzamy jakie akcje z tego miejsca mozemy podjac (ActionType)
|
2022-04-28 00:05:12 +02:00
|
|
|
# reprezentacja kazdego stanu co moge podjac z tego miejsca
|
|
|
|
# generowanie stanu
|
|
|
|
# sprawdz w ktorym kierunku obrocony
|
2022-04-28 00:29:29 +02:00
|
|
|
possibleNextStates: List[PathFinderState] = []
|
2022-04-28 20:34:03 +02:00
|
|
|
if self.isMovePossible(currState):
|
|
|
|
possibleNextStates.append(self.createState(currState, ActionType.MOVE))
|
|
|
|
|
2022-04-28 00:05:12 +02:00
|
|
|
if currState.agent_direction == Direction.top:
|
|
|
|
possibleNextStates.append(self.createState(currState, ActionType.ROTATE_RIGHT))
|
|
|
|
possibleNextStates.append(self.createState(currState, ActionType.ROTATE_LEFT))
|
|
|
|
possibleNextStates.append(self.createState(currState, ActionType.ROTATE_DOWN))
|
2022-04-28 20:34:03 +02:00
|
|
|
|
2022-04-28 00:05:12 +02:00
|
|
|
elif currState.agent_direction == Direction.down:
|
|
|
|
possibleNextStates.append(self.createState(currState, ActionType.ROTATE_RIGHT))
|
|
|
|
possibleNextStates.append(self.createState(currState, ActionType.ROTATE_LEFT))
|
|
|
|
possibleNextStates.append(self.createState(currState, ActionType.ROTATE_UP))
|
2022-04-28 20:34:03 +02:00
|
|
|
|
2022-04-28 00:05:12 +02:00
|
|
|
elif currState.agent_direction == Direction.left:
|
|
|
|
possibleNextStates.append(self.createState(currState, ActionType.ROTATE_RIGHT))
|
|
|
|
possibleNextStates.append(self.createState(currState, ActionType.ROTATE_UP))
|
|
|
|
possibleNextStates.append(self.createState(currState, ActionType.ROTATE_DOWN))
|
2022-04-28 20:34:03 +02:00
|
|
|
|
2022-04-28 00:05:12 +02:00
|
|
|
elif currState.agent_direction == Direction.right:
|
2022-04-28 00:29:29 +02:00
|
|
|
possibleNextStates.append(self.createState(currState, ActionType.ROTATE_UP))
|
2022-04-28 00:05:12 +02:00
|
|
|
possibleNextStates.append(self.createState(currState, ActionType.ROTATE_LEFT))
|
|
|
|
possibleNextStates.append(self.createState(currState, ActionType.ROTATE_DOWN))
|
2022-04-28 20:34:03 +02:00
|
|
|
|
2022-04-28 00:05:12 +02:00
|
|
|
return possibleNextStates
|
2022-04-27 01:26:25 +02:00
|
|
|
|
|
|
|
def getActionList(self) -> List[ActionType]:
|
2022-04-28 01:50:56 +02:00
|
|
|
already_visited = {}
|
|
|
|
|
|
|
|
while not self.queue.empty():
|
|
|
|
item: PrioritizedItem = self.queue.get()
|
|
|
|
best_state: PathFinderState = item.item
|
|
|
|
|
2022-04-28 20:34:03 +02:00
|
|
|
if best_state.agent_position == self.goal or (self.heuristic(best_state.agent_position, self.goal) == 1
|
|
|
|
and self.goal in self.game_constants.walls):
|
2022-04-28 01:50:56 +02:00
|
|
|
break
|
2022-04-27 01:26:25 +02:00
|
|
|
|
2022-04-28 00:05:12 +02:00
|
|
|
for state in self.expansion(best_state):
|
2022-04-28 01:50:56 +02:00
|
|
|
s_tuple = (state.agent_position[0], state.agent_position[1], state.agent_direction)
|
|
|
|
|
|
|
|
if s_tuple not in already_visited:
|
|
|
|
priority = self.evaluate(state)
|
|
|
|
self.queue.put(PrioritizedItem(priority, state), priority)
|
|
|
|
already_visited[s_tuple] = state
|
|
|
|
|
|
|
|
return best_state.action_taken
|