diff --git a/agent.py b/agent.py index 0e2b576..2fbb46a 100644 --- a/agent.py +++ b/agent.py @@ -3,52 +3,52 @@ import project_constants as const import json_generator as js import json - # Class of our agent, initialization of it - # movment functions (those defiend by the 'go_' prefix are not meant to actually move our agent, they just return some values - # that are later used by another function called 'is_valid_move' (which is defined in Minefield)); - + +# Class of our agent, initialization of it +# movement functions (those defiend by the 'go_' prefix are not meant to actually move our agent, they just return some values +# that are later used by another function called 'is_valid_move' (which is defined in Minefield)); + class Agent: def __init__(self, json_path): with open(json_path) as json_data: data = json.load(json_data) self.row, self.column = data['agent_starting_position'].split(",") self.position = [int(self.row), int(self.column)] - #self.direction = const.Direction() - self.direction = 0 + # self.direction = const.Direction() + self.direction = const.Direction.UP - - def rotate_left (self): + def rotate_left(self): self.direction = self.direction.previous() + def rotate_right(self): - self.direction = self.direction.next() - + self.direction = self.direction.next() + def go(self): - if (self.direction == const.Direction.RIGHT ): - temp = go_right() + if self.direction == const.Direction.RIGHT: + temp = self.go_right() self.position[1] = temp[1] - elif (self.direction == const.Direction.LEFT ): - temp = go_left() - self.position[1] = temp[1] - elif (self.direction == const.Direction.UP): - temp = go_up() + elif self.direction == const.Direction.LEFT: + temp = self.go_left() + self.position[1] = temp[1] + elif self.direction == const.Direction.UP: + temp = self.go_up() self.position[0] = temp[0] - elif (self.direction == const.Direction.DOWN): - temp = temp = go_down() + elif self.direction == const.Direction.DOWN: + temp = self.go_down() self.position[0] = temp[0] def go_right(self): - + return self.position[0], self.position[1] + 1 def go_left(self): - + return self.position[0], self.position[1] - 1 def go_up(self): - + return self.position[0] - 1, self.position[1] def go_down(self): - + return self.position[0] + 1, self.position[1] - \ No newline at end of file diff --git a/display_assets.py b/display_assets.py index 4582089..54b40e4 100644 --- a/display_assets.py +++ b/display_assets.py @@ -29,7 +29,7 @@ def display_sapper(sapper_x, sapper_y, sapper_dir): if sapper_dir == const.Direction.RIGHT: const.SCREEN.blit( - pygame.transform.rotate(const.ASSET_SAPPER, 90), + pygame.transform.rotate(const.ASSET_SAPPER, 270), sapper_screen_coords ) @@ -41,7 +41,7 @@ def display_sapper(sapper_x, sapper_y, sapper_dir): if sapper_dir == const.Direction.LEFT: const.SCREEN.blit( - pygame.transform.rotate(const.ASSET_SAPPER, 270), + pygame.transform.rotate(const.ASSET_SAPPER, 90), sapper_screen_coords ) diff --git a/main.py b/main.py index eb8ce4f..802d310 100644 --- a/main.py +++ b/main.py @@ -1,10 +1,13 @@ # libraries + import pygame +import time from pyglet.gl import * # for blocky textures # other files of this project import project_constants as const import minefield as mf +import searching_algorithms.bfs as bfs from display_assets import display_sapper @@ -22,6 +25,12 @@ def main(): # create an instance of Minefield, pass necessary data minefield = mf.Minefield(const.MAP_RANDOM_10x10) + # get sequence of actions found by BFS algorythm + action_sequence = bfs.graphsearch(initial_state=bfs.State(row=minefield.agent.position[0], + column=minefield.agent.position[1], + direction=const.Direction.UP), + minefield=minefield) + running = True while running: # FPS control @@ -48,20 +57,29 @@ def main(): display_sapper( minefield.agent.position[0], minefield.agent.position[1], - const.Direction.UP + minefield.agent.direction ) # update graphics after blitting pygame.display.update() - # ============== # - # === EVENTS === # - # ============== # + # make the next move from sequence of actions + if any(action_sequence): + action = action_sequence.pop(0) + if action == const.Action.ROTATE_LEFT: + minefield.agent.rotate_left() + elif action == const.Action.ROTATE_RIGHT: + minefield.agent.rotate_right() + elif action == const.Action.GO: + minefield.agent.go() - for event in pygame.event.get(): + time.sleep(const.ACTION_INTERVAL) - if event.type == pygame.QUIT: - running = False + else: + for event in pygame.event.get(): + + if event.type == pygame.QUIT: + running = False # Assigning all input from keyboard as variables into an array diff --git a/minefield.py b/minefield.py index 0883a68..d82c039 100644 --- a/minefield.py +++ b/minefield.py @@ -93,7 +93,7 @@ class Minefield: def is_valid_move(self, target_row: int, target_column: int): if 0 <= target_row < const.V_GRID_VER_TILES \ and 0 <= target_column < const.V_GRID_HOR_TILES \ - and self.matrix[target_row][target_column] is None: + and self.matrix[target_row][target_column].mine is None: return True diff --git a/project_constants.py b/project_constants.py index e3e999d..c9447c8 100644 --- a/project_constants.py +++ b/project_constants.py @@ -17,6 +17,8 @@ V_NAME_OF_WINDOW = "MineFusion TM" ASSETS_DIR = os.path.join("resources", "assets") V_FPS = 60 +ACTION_INTERVAL = 1 # interval between two actions in seconds + V_TILE_SIZE = 60 V_GRID_VER_TILES = V_GRID_HOR_TILES = 10 # vertical (number of rows), horizontal (number of columns) V_SCREEN_PADDING = 10 @@ -46,7 +48,7 @@ class Direction(Enum): v = (self.value + 1) % 4 return Direction(v) - def previous(self): + def previous (self): v = (self.value - 1) % 4 return Direction(v) diff --git a/searching_algorithms/bfs.py b/searching_algorithms/bfs.py index 6186611..309c2be 100644 --- a/searching_algorithms/bfs.py +++ b/searching_algorithms/bfs.py @@ -1,10 +1,12 @@ from __future__ import annotations -from typing import List, Set +from typing import List +import ctypes from project_constants import Direction, Action +from minefield import Minefield # temporary goal for testing -GOAL = (9, 9) +GOAL = (13, 9) class State: @@ -27,38 +29,51 @@ def goal_test(state: State): return False -# TODO: depends od Agent Class (rotate/go implementation) -def get_successors(state: State): +def get_successors(state: State, minefield: Minefield): successors = list() - # state_left = get Agent State after rotate_left() - # successors.add("rotate_left", state_left) + state_left = State(state.row, state.column, state.direction.previous()) + successors.append((Action.ROTATE_LEFT, state_left)) - # state_right = get Agent State after rotate_right() - # successors.add("rotate_right", state_right) + state_right = State(state.row, state.column, state.direction.next()) + successors.append((Action.ROTATE_RIGHT, state_right)) - # target = get Agent position after go() - # if is_valid_move(target): - # state_go = Agent State after go() - # successors.add("go", state_go) + 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(fringe: List[Node], explored: Set[Node], initial_state: State): +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() + explored_states = set() + fringe_states = set() + + # root 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 len(fringe): - return False + if not any(fringe): + ctypes.windll.user32.MessageBoxW(0, "Brak rozwiązania", "GAME OVER", 1) + return [] + # get first element from fringe element = fringe.pop(0) + fringe_states.remove((element.state.row, element.state.column, element.state.direction)) - # solution found, prepare and return actions sequence + # if solution was found, prepare and return actions sequence if goal_test(element.state): - actions_sequence = list() + actions_sequence = [element.action] parent = element.parent while parent is not None: @@ -67,20 +82,39 @@ def graphsearch(fringe: List[Node], explored: Set[Node], initial_state: State): actions_sequence.append(parent.action) parent = parent.parent - return actions_sequence.reverse() + actions_sequence.reverse() + return actions_sequence - explored.add(element) + # add current node to explored (prevents infinite cycles) + explored.append(element) + explored_states.add((element.state.row, element.state.column, element.state.direction)) - for successor in get_successors(element.state): - # 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] + # loop through every possible next action + for successor in get_successors(element.state, minefield): - if successor.state not in fringe_states and \ - successor.state not in explored_states: - - new_node = Node(state=successor.state, + # 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.action) + 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