From 36258b11d0a5e6d9c5a4ce07b9e0b4944d239f16 Mon Sep 17 00:00:00 2001 From: s452645 Date: Sun, 9 May 2021 02:06:13 +0200 Subject: [PATCH] Fixed cost and priority calculation --- main.py | 5 ++- project_constants.py | 2 +- searching_algorithms/a_star.py | 62 ++++++++++++++++++++++------------ 3 files changed, 44 insertions(+), 25 deletions(-) diff --git a/main.py b/main.py index 4fba37a..d3ce526 100644 --- a/main.py +++ b/main.py @@ -6,7 +6,6 @@ import random import project_constants as const import minefield as mf import searching_algorithms.a_star as a_star -import searching_algorithms.bfs as bfs from display_assets import blit_graphics from ui.input_box import * from ui.button import * @@ -94,8 +93,8 @@ def main(): to_x = int(input1.get_input()) to_y = int(input2.get_input()) - action_sequence = bfs.graphsearch( - initial_state=bfs.State( + action_sequence = a_star.graphsearch( + initial_state=a_star.State( row=minefield.agent.position[0], column=minefield.agent.position[1], direction=minefield.agent.direction), diff --git a/project_constants.py b/project_constants.py index 35ce325..0d92497 100644 --- a/project_constants.py +++ b/project_constants.py @@ -17,7 +17,7 @@ V_NAME_OF_WINDOW = "MineFusion TM" DIR_ASSETS = os.path.join("resources", "assets") V_FPS = 60 -ACTION_INTERVAL = 1 # interval between two actions in seconds +ACTION_INTERVAL = 0.5 # interval between two actions in seconds V_TILE_SIZE = 60 V_GRID_VER_TILES = 10 # vertical (number of rows) diff --git a/searching_algorithms/a_star.py b/searching_algorithms/a_star.py index a1e36f1..302c2b9 100644 --- a/searching_algorithms/a_star.py +++ b/searching_algorithms/a_star.py @@ -20,25 +20,30 @@ class State: class Node: - def __init__(self, state: State, parent: Node = None, action: Action = None, weight=0): + def __init__(self, state: State, parent: Node = None, action: Action = None, cost=0): self.state = state self.parent = parent self.action = action + self.cost = cost - if not weight: - self.weight = self._get_weight() - else: - self.weight = weight - def _get_weight(self): - weight = 0 - if self.parent is not None: - weight += self.parent.weight +def get_node_cost(node: Node, minefield: Minefield): + row = node.state.row + column = node.state.column - heuristics = abs(self.state.row - GOAL[0]) + abs(self.state.column - GOAL[1]) - weight += heuristics + # Rotation Cost?? + if node.action != Action.GO: + return node.parent.cost + 1 - return weight + # if Tile considered its mine in cost calculation, this code would be priettier + if minefield.matrix[row][column].mine is not None: + return node.parent.cost + 10 + else: + return node.parent.cost + minefield.matrix[row][column].cost.value + + +def get_estimated_cost(node: Node): + return abs(node.state.row - GOAL[0]) + abs(node.state.column - GOAL[1]) def goal_test(state: State): @@ -65,13 +70,23 @@ def get_successors(state: State, minefield: Minefield): return successors -def graphsearch(initial_state: State, minefield: Minefield, fringe: List[Node] = None, explored: List[Node] = None): +def graphsearch(initial_state: State, + minefield: Minefield, + fringe: List[Node] = None, + explored: List[Node] = None, + tox: int = None, + toy: int = None): + # reset global priority queue helpers global entry_finder global counter entry_finder = {} counter = itertools.count() + global GOAL + if tox is not None and toy is not None: + GOAL = (tox, toy) + # fringe and explored initialization if fringe is None: fringe = list() @@ -83,18 +98,20 @@ def graphsearch(initial_state: State, minefield: Minefield, fringe: List[Node] = fringe_states = set() # root Node - add_node(fringe, Node(initial_state)) + add_node(fringe, Node(initial_state), 0) fringe_states.add((initial_state.row, initial_state.column, initial_state.direction)) while True: # fringe empty -> solution not found if not any(fringe): + # TODO: cross-platform popup, move to separate function ctypes.windll.user32.MessageBoxW(0, "Brak rozwiązania", "GAME OVER", 1) return [] # get first element from fringe element = pop_node(fringe) if element is None: + # TODO: cross-platform popup, move to separate function ctypes.windll.user32.MessageBoxW(0, "Brak rozwiązania", "GAME OVER", 1) return [] @@ -124,17 +141,20 @@ def graphsearch(initial_state: State, minefield: Minefield, fringe: List[Node] = new_node = Node(state=successor[1], parent=element, action=successor[0]) + + new_node.cost = get_node_cost(new_node, minefield) + priority = new_node.cost + get_estimated_cost(new_node) 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: - add_node(fringe, new_node) + add_node(fringe, new_node, priority) fringe_states.add((new_node.state.row, new_node.state.column, new_node.state.direction)) # update weight if it's lower - elif successor_state in fringe and entry_finder[successor_state][0] > new_node.weight: - update_priority(fringe, new_node) + elif successor_state in fringe and entry_finder[successor_state][0] > priority: + update_priority(fringe, new_node, priority) else: del new_node @@ -162,9 +182,9 @@ REMOVED = '' # placeholder for a removed nodes counter = itertools.count() # unique sequence count -def add_node(heap, node: Node): +def add_node(heap, node: Node, priority): count = next(counter) - entry = [node.weight, count, node] + entry = [priority, count, node] entry_finder[node.state] = entry heappush(heap, entry) @@ -178,7 +198,7 @@ def pop_node(heap): return None -def update_priority(heap, new_node): +def update_priority(heap, new_node, new_priority): old_entry = entry_finder.pop(new_node.state) old_entry[-1] = REMOVED - add_node(heap, new_node) + add_node(heap, new_node, new_priority)