diff --git a/game.py b/game.py index 929a4cc..96bdf9d 100644 --- a/game.py +++ b/game.py @@ -1,4 +1,4 @@ -from random import randint +from random import choice, randint import project_constants as const @@ -112,8 +112,20 @@ class Game: def get_turn_number(self): return self.turn + # draws a random mine to disarm (in auto mode) + def set_random_mine_as_target(self): + self.goal = choice(self.minefield.get_active_mines()).position + + # display new destination + self.input_box_row.set_texts(user_input=str(self.goal[0])) + self.input_box_column.set_texts(user_input=str(self.goal[1])) + + # prevents highlighting input_box_row, + # couldn't find any better solution w/o major Game class changes + self.input_box_row.set_is_selected(False) + # gets action sequence for agent - def get_action_sequence(self): + def get_action_sequence(self, target_type: str = "tile"): return a_star.graphsearch( initial_state=a_star.State( row=self.agent.row, @@ -121,6 +133,7 @@ class Game: direction=self.agent.direction ), minefield=self.minefield, + target_type=target_type, tox=self.goal[0], toy=self.goal[1] ) diff --git a/main.py b/main.py index 5ef7617..6c19ad1 100644 --- a/main.py +++ b/main.py @@ -1,7 +1,8 @@ # libraries +import time import pygame from pyglet.gl import * # for blocky textures - +import random # other files of this project from game import Game import project_constants as const @@ -25,6 +26,7 @@ def main(): # setting flags for program running = True in_menu = True + auto = False is_game_over = False # create and initialize_gui_components game instance @@ -37,7 +39,7 @@ def main(): # ==== MENU ==== # # ============== # - while running and in_menu: + while running and in_menu and not auto: events = pygame.event.get() # checking if game should stop running @@ -58,12 +60,21 @@ def main(): # if ok button is clicked then leave menu section in_menu = not game.button_ok.is_clicked(pygame.mouse.get_pos(), events) + # if auto button is clicked then leave menu and start auto-mode + auto = game.button_auto.is_clicked(pygame.mouse.get_pos(), events) + # ========================== # # ==== BEFORE GAME LOOP ==== # # ========================== # # getting action sequence for agent - action_sequence = game.get_action_sequence() + if auto: + in_menu = False + game.set_random_mine_as_target() + action_sequence = game.get_action_sequence("mine") + + else: + action_sequence = game.get_action_sequence("tile") # initializing game attributes before the game loop game.initialize_before_game_loop() @@ -87,6 +98,11 @@ def main(): # drawing minefield and agent instances game.draw_minefield() + # handling auto button (clicking quits auto-mode) + if auto: + game.button_auto.set_flags(is_active=True) + auto = not game.button_auto.is_clicked(pygame.mouse.get_pos(), events) + # drawing inactive gui components so they don't "disappear" game.run_in_game_menu_overlay(pygame.mouse.get_pos(), events) game.set_is_active_flag_for_all_in_game_gui_components(False) diff --git a/minefield.py b/minefield.py index 61ecf3a..52de4a3 100644 --- a/minefield.py +++ b/minefield.py @@ -45,6 +45,22 @@ class Minefield: if mine is not None and isinstance(mine, TimeMine): mine.timer = max(0, mine.starting_time - int(self.turn / 4)) + def get_active_mines(self): + mines = list() + + for row in range(const.V_GRID_VER_TILES): + for column in range(const.V_GRID_VER_TILES): + mine = self.matrix[row][column].mine + + if mine is not None and mine.active: + # do not add mines with predecessors + if mine.type == 'chained' and mine.predecessor is not None: + continue + + mines.append(mine) + + return mines + def disarm_mine(self, x, y): tile = self.matrix[x][y] mine = tile.mine @@ -55,7 +71,6 @@ class Minefield: # ================ # # check if sapper's destination is accessible - # If Agent comes upon a tile with a mine his starting position shall be reestablished @staticmethod def is_valid_move(target_row: int, target_column: int): if 0 <= target_row < const.V_GRID_VER_TILES \ diff --git a/objects/mines/standard_mine.py b/objects/mines/standard_mine.py index 36cc869..4686976 100644 --- a/objects/mines/standard_mine.py +++ b/objects/mines/standard_mine.py @@ -3,7 +3,7 @@ from .mine import Mine class StandardMine(Mine): def __init__(self, position, active=True): - self.mine_type = "standard" + self.type = "standard" super().__init__(position, active) def disarm(self): diff --git a/searching_algorithms/a_star.py b/searching_algorithms/a_star.py index c87ed90..227c1ad 100644 --- a/searching_algorithms/a_star.py +++ b/searching_algorithms/a_star.py @@ -45,12 +45,32 @@ def get_estimated_cost(node: Node): return abs(node.state.row - GOAL[0]) + abs(node.state.column - GOAL[1]) -def goal_test(state: State): +def tile_goal_test(state: State): if (state.row, state.column) == GOAL: return True return False +def mine_goal_test(state: State): + if state.row == GOAL[0] and state.column == GOAL[1] - 1: + if state.direction == Direction.RIGHT: + return True + + elif state.row == GOAL[0] and state.column == GOAL[1] + 1: + if state.direction == Direction.LEFT: + return True + + elif state.row == GOAL[0] - 1 and state.column == GOAL[1]: + if state.direction == Direction.DOWN: + return True + + elif state.row == GOAL[0] + 1 and state.column == GOAL[1]: + if state.direction == Direction.UP: + return True + + return False + + def get_successors(state: State, minefield: Minefield): successors = list() @@ -73,6 +93,7 @@ def graphsearch(initial_state: State, minefield: Minefield, fringe: List[Node] = None, explored: List[Node] = None, + target_type: str = "tile", tox: int = None, toy: int = None): @@ -86,6 +107,15 @@ def graphsearch(initial_state: State, if tox is not None and toy is not None: GOAL = (tox, toy) + if target_type == "mine": + goal_test = mine_goal_test + else: + goal_test = tile_goal_test + if minefield.matrix[GOAL[0]][GOAL[1]].mine is not None and minefield.matrix[GOAL[0]][GOAL[1]].mine.active: + # TODO: cross-platform popup, move to separate function + ctypes.windll.user32.MessageBoxW(0, "Brak rozwiązania", "GAME OVER", 1) + return [] + # fringe and explored initialization if fringe is None: fringe = list()