Merge branch 'master' into decision_trees
# Conflicts: # main.py
This commit is contained in:
commit
ad5864c7f8
296
game.py
Normal file
296
game.py
Normal file
@ -0,0 +1,296 @@
|
|||||||
|
from random import randint
|
||||||
|
|
||||||
|
import project_constants as const
|
||||||
|
|
||||||
|
from display_assets import blit_graphics
|
||||||
|
from searching_algorithms import a_star
|
||||||
|
|
||||||
|
from minefield import Minefield
|
||||||
|
|
||||||
|
from mine_models.time_mine import TimeMine
|
||||||
|
|
||||||
|
from ui.ui_components_manager import UiComponentsManager
|
||||||
|
from ui.text_box import TextBox
|
||||||
|
from ui.button import Button
|
||||||
|
from ui.input_box import InputBox
|
||||||
|
|
||||||
|
|
||||||
|
class Game:
|
||||||
|
def __init__(self, turn=0, minefield=None, window=None):
|
||||||
|
# loading game settings
|
||||||
|
self.window = window if window is not None else const.SCREEN
|
||||||
|
|
||||||
|
# setting in game data
|
||||||
|
self.turn = turn
|
||||||
|
self.turn_on_which_last_action_was_taken = turn
|
||||||
|
self.goal = (0, 0)
|
||||||
|
|
||||||
|
# assigning minefield and agent to variables
|
||||||
|
self.minefield = minefield if minefield is not None else Minefield(const.MAP_RANDOM_10x10)
|
||||||
|
self.initial_minefield = self.minefield.__copy__()
|
||||||
|
self.agent = self.minefield.agent
|
||||||
|
self.agent_action = None
|
||||||
|
self.millisecond_timer = 0
|
||||||
|
self.delta_time = 0
|
||||||
|
self.action_timer = 0
|
||||||
|
self.action_delta_time = 0
|
||||||
|
|
||||||
|
# declaring and initializing gui components
|
||||||
|
# ui_component managers
|
||||||
|
self.in_game_gui_components_manager = UiComponentsManager()
|
||||||
|
self.game_over_gui_components_manager = UiComponentsManager()
|
||||||
|
|
||||||
|
# in game gui
|
||||||
|
self.input_box_row = InputBox((0, 0), (0, 0))
|
||||||
|
self.input_box_column = InputBox((0, 0), (0, 0))
|
||||||
|
self.button_auto = Button((0, 0), (0, 0))
|
||||||
|
self.button_random = Button((0, 0), (0, 0))
|
||||||
|
self.button_ok = Button((0, 0), (0, 0))
|
||||||
|
|
||||||
|
# game over screen
|
||||||
|
self.text_box_game_over = TextBox((0, 0), (0, 0))
|
||||||
|
self.button_try_again = Button((0, 0), (0, 0))
|
||||||
|
self.button_close = Button((0, 0), (0, 0))
|
||||||
|
|
||||||
|
# draws minefield and agent instances
|
||||||
|
def draw_minefield(self):
|
||||||
|
blit_graphics(self.minefield)
|
||||||
|
self.agent.update_and_draw(self.window, self.action_delta_time, self.minefield)
|
||||||
|
|
||||||
|
# draws menu
|
||||||
|
def run_in_game_menu_overlay(self, mouse_position, events):
|
||||||
|
# drawing and updating all gui components
|
||||||
|
self.in_game_gui_components_manager.run_all(self.window, mouse_position, events)
|
||||||
|
self.in_game_gui_components_manager.switch_selected_objects_with_arrow_keys(mouse_position, events)
|
||||||
|
|
||||||
|
# setting random goal if random button is clicked
|
||||||
|
if self.button_random.is_clicked(mouse_position, events):
|
||||||
|
self.goal = (randint(0, 9), randint(0, 9))
|
||||||
|
self.input_box_row.set_texts(user_input=str(self.goal[0]))
|
||||||
|
self.input_box_column.set_texts(user_input=str(self.goal[1]))
|
||||||
|
|
||||||
|
# if any input box is empty don't allow player to run game
|
||||||
|
self.button_ok.set_flags(is_active=not (self.input_box_row.empty() or self.input_box_column.empty()))
|
||||||
|
|
||||||
|
# updating goal if input boxes are filled and highlighting chosen tile destination
|
||||||
|
if not (self.input_box_row.empty() or self.input_box_column.empty()):
|
||||||
|
# updating goal
|
||||||
|
self.goal = (min(9, int(self.input_box_row.get_input())), min(9, int(self.input_box_column.get_input())))
|
||||||
|
|
||||||
|
# highlighting chosen tile destination
|
||||||
|
self.window.blit(const.HIGHLIGHT, const.get_tile_coordinates(self.goal))
|
||||||
|
|
||||||
|
# makes agent take next action
|
||||||
|
def agent_take_next_action(self, action_sequence):
|
||||||
|
# give agent an action if there is one
|
||||||
|
self.agent.take_action(self.agent_action)
|
||||||
|
|
||||||
|
# check what the next action is going to be
|
||||||
|
self.agent_action = action_sequence.pop(0)
|
||||||
|
|
||||||
|
# start animating agents next action
|
||||||
|
self.agent.animate(self.agent_action)
|
||||||
|
|
||||||
|
# updates in game time and timers
|
||||||
|
def update_time(self, number_of_seconds_since_last_tick):
|
||||||
|
self.millisecond_timer += number_of_seconds_since_last_tick / 1000
|
||||||
|
self.action_timer = self.millisecond_timer / const.TURN_INTERVAL
|
||||||
|
self.delta_time = number_of_seconds_since_last_tick / 1000
|
||||||
|
self.action_delta_time = self.delta_time / const.TURN_INTERVAL
|
||||||
|
|
||||||
|
# updates number of turns in game
|
||||||
|
def update_turns(self):
|
||||||
|
# if one turns time passed: updating number of turns
|
||||||
|
if self.millisecond_timer >= const.TURN_INTERVAL:
|
||||||
|
self.turn += 1
|
||||||
|
self.minefield.next_turn()
|
||||||
|
|
||||||
|
# resetting timer
|
||||||
|
self.millisecond_timer %= const.TURN_INTERVAL
|
||||||
|
|
||||||
|
# returns turn number
|
||||||
|
def get_turn_number(self):
|
||||||
|
return self.turn
|
||||||
|
|
||||||
|
# gets action sequence for agent
|
||||||
|
def get_action_sequence(self):
|
||||||
|
return a_star.graphsearch(
|
||||||
|
initial_state=a_star.State(
|
||||||
|
row=self.agent.row,
|
||||||
|
column=self.agent.column,
|
||||||
|
direction=self.agent.direction
|
||||||
|
),
|
||||||
|
minefield=self.minefield,
|
||||||
|
tox=self.goal[0],
|
||||||
|
toy=self.goal[1]
|
||||||
|
)
|
||||||
|
|
||||||
|
# initializes attributes before game loop begins
|
||||||
|
def initialize_before_game_loop(self):
|
||||||
|
self.agent_action = None
|
||||||
|
self.millisecond_timer = 0
|
||||||
|
self.action_timer = 0
|
||||||
|
self.delta_time = 0
|
||||||
|
self.action_delta_time = 0
|
||||||
|
|
||||||
|
for component in self.in_game_gui_components_manager.selectable_ui_components:
|
||||||
|
component.set_flags(is_active=False)
|
||||||
|
|
||||||
|
# gives agent last action
|
||||||
|
def agent_take_last_action(self):
|
||||||
|
self.agent.take_action(self.agent_action)
|
||||||
|
|
||||||
|
# cleans up after game loop ends
|
||||||
|
def cleanup_after_game_loop(self):
|
||||||
|
self.agent.update_and_draw(self.window, self.action_delta_time, self.minefield)
|
||||||
|
self.agent.reset_actions()
|
||||||
|
|
||||||
|
for comp in self.in_game_gui_components_manager.selectable_ui_components:
|
||||||
|
comp.set_flags(is_active=True)
|
||||||
|
|
||||||
|
if not any([comp.is_selected for comp in self.in_game_gui_components_manager.selectable_ui_components]):
|
||||||
|
self.input_box_row.set_is_selected(True)
|
||||||
|
|
||||||
|
# returns True if agents should take an action in this turn, or False otherwise
|
||||||
|
def agent_should_take_next_action(self, action_sequence):
|
||||||
|
number_of_turns_since_last_action = self.turn - self.turn_on_which_last_action_was_taken
|
||||||
|
agent_action_cost = 1 if self.agent_action != const.Action.GO else self._get_next_tiles_entering_cost()
|
||||||
|
|
||||||
|
if number_of_turns_since_last_action >= agent_action_cost and any(action_sequence):
|
||||||
|
self.turn_on_which_last_action_was_taken = self.turn
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
# returns True if agent made all actions he was instructed to do, or False otherwise
|
||||||
|
def agent_made_all_actions(self, action_sequence):
|
||||||
|
number_of_turns_since_last_action = self.turn - self.turn_on_which_last_action_was_taken
|
||||||
|
agent_action_cost = 1 if self.agent_action != const.Action.GO else self._get_next_tiles_entering_cost()
|
||||||
|
|
||||||
|
if number_of_turns_since_last_action >= agent_action_cost and not any(action_sequence):
|
||||||
|
self.turn_on_which_last_action_was_taken = self.turn
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
# runs game over screen
|
||||||
|
def run_game_over_screen(self, mouse_position, events):
|
||||||
|
self.window.fill((255, 255, 255))
|
||||||
|
self.game_over_gui_components_manager.run_all(self.window, mouse_position, events)
|
||||||
|
|
||||||
|
# resetting minefield to the original state
|
||||||
|
if self.button_try_again.is_clicked(mouse_position, events):
|
||||||
|
del self.minefield
|
||||||
|
del self.agent
|
||||||
|
self.minefield = self.initial_minefield.__copy__()
|
||||||
|
self.agent = self.initial_minefield.agent
|
||||||
|
|
||||||
|
# initializes ui components and assigns them to attributes
|
||||||
|
def initialize_gui_components(self):
|
||||||
|
# calculating in game gui coordinates
|
||||||
|
gui_width = const.V_SIDE_MENU_WIDTH
|
||||||
|
ib_height, bt_height = const.V_INPUT_BOX_HEIGHT, const.V_BUTTON_HEIGHT
|
||||||
|
gui_x = const.V_TILE_AREA_WIDTH + 2 * const.V_SCREEN_PADDING + const.V_NUMBER_PADDING
|
||||||
|
gui_y = const.SCREEN.get_height() / 2 - (2 * ib_height + 2 * bt_height + 30) / 2
|
||||||
|
|
||||||
|
# creating in game gui components
|
||||||
|
self.input_box_row = InputBox(
|
||||||
|
position=(gui_x, gui_y),
|
||||||
|
dimensions=(gui_width, ib_height),
|
||||||
|
text="row",
|
||||||
|
box_color=(100, 200, 100),
|
||||||
|
bottom_strip_color=(120, 220, 120),
|
||||||
|
inner_box_color=(120, 220, 120),
|
||||||
|
outline_color=(80, 180, 80),
|
||||||
|
outline_additional_pixel=True,
|
||||||
|
valid_input_characters="1234567890",
|
||||||
|
input_centered=True,
|
||||||
|
clear_input_on_click=True
|
||||||
|
)
|
||||||
|
|
||||||
|
self.input_box_column = InputBox(
|
||||||
|
position=(gui_x, gui_y + ib_height + 10),
|
||||||
|
dimensions=(gui_width, ib_height),
|
||||||
|
text="column",
|
||||||
|
box_color=(100, 200, 100),
|
||||||
|
bottom_strip_color=(120, 220, 120),
|
||||||
|
inner_box_color=(120, 220, 120),
|
||||||
|
outline_color=(80, 180, 80),
|
||||||
|
outline_additional_pixel=True,
|
||||||
|
valid_input_characters="1234567890",
|
||||||
|
input_centered=True,
|
||||||
|
clear_input_on_click=True
|
||||||
|
)
|
||||||
|
|
||||||
|
self.button_auto = Button(
|
||||||
|
position=(gui_x, gui_y + 2 * ib_height + 20),
|
||||||
|
dimensions=(gui_width, bt_height),
|
||||||
|
text="auto",
|
||||||
|
box_color=(100, 200, 100),
|
||||||
|
outline_color=(80, 180, 80),
|
||||||
|
outline_additional_pixel=True
|
||||||
|
)
|
||||||
|
|
||||||
|
self.button_random = Button(
|
||||||
|
position=(gui_x, gui_y + 2 * ib_height + bt_height + 30),
|
||||||
|
dimensions=(gui_width, bt_height),
|
||||||
|
text="random",
|
||||||
|
box_color=(100, 200, 100),
|
||||||
|
outline_color=(80, 180, 80),
|
||||||
|
outline_additional_pixel=True
|
||||||
|
)
|
||||||
|
|
||||||
|
self.button_ok = Button(
|
||||||
|
position=(gui_x, gui_y + 2 * ib_height + 2 * bt_height + 40),
|
||||||
|
dimensions=(gui_width, bt_height),
|
||||||
|
text="ok",
|
||||||
|
box_color=(100, 200, 100),
|
||||||
|
outline_color=(80, 180, 80),
|
||||||
|
outline_additional_pixel=True
|
||||||
|
)
|
||||||
|
|
||||||
|
gui_list = [self.input_box_row, self.input_box_column, self.button_auto, self.button_random, self.button_ok]
|
||||||
|
self.in_game_gui_components_manager = UiComponentsManager(gui_list)
|
||||||
|
|
||||||
|
# creating game over gui components
|
||||||
|
self.text_box_game_over = TextBox(position=(150, 200), dimensions=(500, 100), text="Game Over")
|
||||||
|
self.button_try_again = Button(position=(350, 350), dimensions=(100, 50), text="Try again")
|
||||||
|
self.button_close = Button(position=(350, 450), dimensions=(100, 50), text="Close")
|
||||||
|
|
||||||
|
gui_list = [self.text_box_game_over, self.button_try_again, self.button_close]
|
||||||
|
self.game_over_gui_components_manager = UiComponentsManager(gui_list, select_first_item=False)
|
||||||
|
|
||||||
|
# returns true if there is a time mine that went out of time
|
||||||
|
def time_mine_exploded(self):
|
||||||
|
# auxiliary function that checks if a tile contains a time mine which timer went to 0
|
||||||
|
for i in range(const.V_GRID_HOR_TILES):
|
||||||
|
for j in range(const.V_GRID_VER_TILES):
|
||||||
|
mine = self.minefield.matrix[i][j].mine
|
||||||
|
if mine is not None and isinstance(mine, TimeMine) and mine.timer <= 0:
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
# sets all in game components is_active flags to a given value
|
||||||
|
def set_is_active_flag_for_all_in_game_gui_components(self, is_active):
|
||||||
|
for component in self.in_game_gui_components_manager.selectable_ui_components:
|
||||||
|
component.set_flags(is_active=is_active)
|
||||||
|
|
||||||
|
# returns cost of entering tile in front of the agent
|
||||||
|
def _get_next_tiles_entering_cost(self):
|
||||||
|
row, column = self._get_next_agent_position()
|
||||||
|
|
||||||
|
return self.minefield.matrix[row][column].cost.value
|
||||||
|
|
||||||
|
# returns position in front of agent
|
||||||
|
def _get_next_agent_position(self):
|
||||||
|
# heading either up or down
|
||||||
|
if self.agent_action == const.Action.GO and self.agent.direction.value % 2 == 0:
|
||||||
|
return max(0, min(9, self.agent.row + self.agent.direction.value - 1)), self.agent.column
|
||||||
|
|
||||||
|
# heading either left or right
|
||||||
|
else:
|
||||||
|
return self.agent.row, max(0, min(9, self.agent.column - self.agent.direction.value + 2))
|
223
main.py
223
main.py
@ -1,34 +1,10 @@
|
|||||||
# libraries
|
# libraries
|
||||||
import pygame
|
import pygame
|
||||||
from pyglet.gl import * # for blocky textures
|
from pyglet.gl import * # for blocky textures
|
||||||
import random
|
|
||||||
# other files of this project
|
# other files of this project
|
||||||
|
from game import Game
|
||||||
import project_constants as const
|
import project_constants as const
|
||||||
import minefield as mf
|
|
||||||
from mines.mine_models.time_mine import TimeMine
|
|
||||||
|
|
||||||
import algorithms.searching_algorithms.a_star as a_star
|
|
||||||
|
|
||||||
from display_assets import blit_graphics
|
|
||||||
|
|
||||||
|
|
||||||
def _get_next_agent_position(action, agent):
|
|
||||||
# counting next agents terrain tile
|
|
||||||
# heading either up or down
|
|
||||||
if const.Direction(agent.direction).value % 2 == 0 and action == const.Action.GO:
|
|
||||||
next_row = min(9, agent.row + const.Direction(agent.direction).value - 1)
|
|
||||||
next_column = agent.column
|
|
||||||
|
|
||||||
# heading either left or right
|
|
||||||
else:
|
|
||||||
next_row = agent.row
|
|
||||||
next_column = min(9, agent.column - const.Direction(agent.direction).value + 2)
|
|
||||||
|
|
||||||
return next_row, next_column
|
|
||||||
from project_constants import HIGHLIGHT, INPUT_ROW, INPUT_COLUMN, RANDOM_BUTTON, OK_BUTTON
|
|
||||||
from ui.ui_components_list import UiComponentsList
|
|
||||||
from ui.text_box import TextBox
|
|
||||||
from ui.button import Button
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
@ -42,29 +18,18 @@ def main():
|
|||||||
# FPS clock
|
# FPS clock
|
||||||
clock = pygame.time.Clock()
|
clock = pygame.time.Clock()
|
||||||
|
|
||||||
# in-game turn number
|
# creating some auxiliary functions
|
||||||
turn = 0
|
def is_quit_button_pressed(_events):
|
||||||
last_turn = 0
|
return any([_event.type == pygame.QUIT for _event in _events])
|
||||||
|
|
||||||
# create an instance of Minefield, pass necessary data
|
|
||||||
minefield = mf.Minefield(const.MAP_RANDOM_10x10)
|
|
||||||
|
|
||||||
# getting agent's instance
|
|
||||||
agent = minefield.agent
|
|
||||||
|
|
||||||
# setting flags for program
|
# setting flags for program
|
||||||
running = True
|
running = True
|
||||||
in_menu = True
|
in_menu = True
|
||||||
is_game_over = False
|
is_game_over = False
|
||||||
|
|
||||||
# creating list storing all ui components
|
# create and initialize_gui_components game instance
|
||||||
ui_components_list = UiComponentsList([INPUT_ROW, INPUT_COLUMN, RANDOM_BUTTON, OK_BUTTON])
|
game = Game()
|
||||||
|
game.initialize_gui_components()
|
||||||
# initializing goal position
|
|
||||||
row, column = 0, 0
|
|
||||||
|
|
||||||
# drawing map so black screen doesn't appear
|
|
||||||
blit_graphics(minefield)
|
|
||||||
|
|
||||||
while running:
|
while running:
|
||||||
|
|
||||||
@ -75,162 +40,104 @@ def main():
|
|||||||
while running and in_menu:
|
while running and in_menu:
|
||||||
events = pygame.event.get()
|
events = pygame.event.get()
|
||||||
|
|
||||||
for event in events:
|
# checking if game should stop running
|
||||||
if event.type == pygame.QUIT:
|
running = not is_quit_button_pressed(events)
|
||||||
running = False
|
|
||||||
|
|
||||||
# graphics (from display_assets)
|
# drawing minefield and agent instances
|
||||||
blit_graphics(minefield)
|
game.draw_minefield()
|
||||||
|
|
||||||
agent.update_and_draw(const.SCREEN, 0, minefield)
|
# drawing gui overlay
|
||||||
|
game.run_in_game_menu_overlay(pygame.mouse.get_pos(), events)
|
||||||
|
|
||||||
# drawing gui
|
# ticking to maintain good fps number
|
||||||
ui_components_list.run_all(const.SCREEN, pygame.mouse.get_pos(), events)
|
clock.tick(const.V_FPS)
|
||||||
|
|
||||||
# highlighting chosen tile destination (if exists)
|
# updating window graphics
|
||||||
if not(INPUT_ROW.empty() or INPUT_COLUMN.empty()):
|
|
||||||
row = min(9, int(INPUT_ROW.get_input()))
|
|
||||||
column = min(9, int(INPUT_COLUMN.get_input()))
|
|
||||||
const.SCREEN.blit(HIGHLIGHT, const.get_tile_coordinates((row, column)))
|
|
||||||
|
|
||||||
# updating graphics
|
|
||||||
pygame.display.flip()
|
pygame.display.flip()
|
||||||
|
|
||||||
ui_components_list.switch_selected_objects_with_arrow_keys(events, pygame.mouse.get_pos())
|
# if ok button is clicked then leave menu section
|
||||||
|
in_menu = not game.button_ok.is_clicked(pygame.mouse.get_pos(), events)
|
||||||
OK_BUTTON.set_flags(is_active=(not(INPUT_ROW.empty() or INPUT_COLUMN.empty())))
|
|
||||||
|
|
||||||
if OK_BUTTON.is_clicked(pygame.mouse.get_pos(), events) or OK_BUTTON.enter_pressed(events):
|
|
||||||
in_menu = False
|
|
||||||
|
|
||||||
if RANDOM_BUTTON.is_clicked(pygame.mouse.get_pos(), events) or RANDOM_BUTTON.enter_pressed(events):
|
|
||||||
INPUT_ROW.set_texts(user_input=str(random.randint(0, 9)))
|
|
||||||
INPUT_COLUMN.set_texts(user_input=str(random.randint(0, 9)))
|
|
||||||
|
|
||||||
clock.tick(const.V_FPS)
|
|
||||||
|
|
||||||
# ========================== #
|
# ========================== #
|
||||||
# ==== BEFORE GAME LOOP ==== #
|
# ==== BEFORE GAME LOOP ==== #
|
||||||
# ========================== #
|
# ========================== #
|
||||||
|
|
||||||
if running:
|
# getting action sequence for agent
|
||||||
for component in ui_components_list.selectable_ui_components:
|
action_sequence = game.get_action_sequence()
|
||||||
component.set_flags(is_active=False)
|
|
||||||
|
|
||||||
action_sequence = a_star.graphsearch(
|
# initializing game attributes before the game loop
|
||||||
initial_state=a_star.State(
|
game.initialize_before_game_loop()
|
||||||
row=minefield.agent.position[0],
|
|
||||||
column=minefield.agent.position[1],
|
|
||||||
direction=minefield.agent.direction),
|
|
||||||
minefield=minefield, tox=row, toy=column)
|
|
||||||
|
|
||||||
# initializing variables
|
|
||||||
in_game_timer = 0
|
|
||||||
action = None
|
|
||||||
action_cost = 1
|
|
||||||
next_row, next_column = agent.row, agent.column
|
|
||||||
|
|
||||||
# =================== #
|
|
||||||
# ==== GAME LOOP ==== #
|
|
||||||
# =================== #
|
|
||||||
|
|
||||||
# =================== #
|
# =================== #
|
||||||
# ==== GAME LOOP ==== #
|
# ==== GAME LOOP ==== #
|
||||||
# =================== #
|
# =================== #
|
||||||
|
|
||||||
while running and not in_menu and not is_game_over:
|
while running and not in_menu and not is_game_over:
|
||||||
|
events = pygame.event.get()
|
||||||
|
|
||||||
for event in pygame.event.get():
|
# checking if game should stop running
|
||||||
if event.type == pygame.QUIT:
|
running = not is_quit_button_pressed(events)
|
||||||
running = False
|
|
||||||
|
|
||||||
for event in pygame.event.get():
|
|
||||||
if event.type == pygame.QUIT:
|
|
||||||
running = False
|
|
||||||
|
|
||||||
# FPS control
|
# FPS control
|
||||||
clock.tick(const.V_FPS)
|
clock.tick(const.V_FPS)
|
||||||
delta_time = clock.get_time() / 1000
|
|
||||||
action_delta_time = delta_time / const.ACTION_INTERVAL
|
|
||||||
|
|
||||||
# graphics (from display_assets)
|
# getting time since last tick
|
||||||
blit_graphics(minefield)
|
time = clock.get_time()
|
||||||
const.SCREEN.blit(HIGHLIGHT, const.get_tile_coordinates((row, column)))
|
|
||||||
|
|
||||||
agent.update_and_draw(const.SCREEN, action_delta_time, minefield)
|
# drawing minefield and agent instances
|
||||||
|
game.draw_minefield()
|
||||||
|
|
||||||
# drawing ui components so they don't "disappear"
|
# drawing inactive gui components so they don't "disappear"
|
||||||
ui_components_list.draw_all(const.SCREEN, pygame.mouse.get_pos())
|
game.run_in_game_menu_overlay(pygame.mouse.get_pos(), events)
|
||||||
|
game.set_is_active_flag_for_all_in_game_gui_components(False)
|
||||||
|
|
||||||
# updating graphics
|
# updating graphics
|
||||||
pygame.display.flip()
|
pygame.display.flip()
|
||||||
|
|
||||||
# update turn
|
# update turn
|
||||||
if in_game_timer >= const.ACTION_INTERVAL:
|
game.update_time(time)
|
||||||
in_game_timer %= const.ACTION_INTERVAL
|
game.update_turns()
|
||||||
turn += 1
|
|
||||||
minefield.next_turn()
|
|
||||||
action_cost = 1 if action != const.Action.GO else minefield.matrix[next_row][next_column].cost.value
|
|
||||||
|
|
||||||
# make the next move from sequence of actions
|
# make the next move from sequence of actions
|
||||||
if turn - last_turn >= action_cost and any(action_sequence):
|
if game.agent_should_take_next_action(action_sequence):
|
||||||
last_turn = turn
|
# give agent next action
|
||||||
|
game.agent_take_next_action(action_sequence)
|
||||||
|
|
||||||
agent.take_action(action)
|
# reset values after the game loop
|
||||||
|
if game.agent_made_all_actions(action_sequence):
|
||||||
action = action_sequence.pop(0)
|
# clean up after game loop
|
||||||
|
game.agent_take_last_action()
|
||||||
agent.animate(action)
|
game.cleanup_after_game_loop()
|
||||||
|
|
||||||
next_row, next_column = _get_next_agent_position(action, agent)
|
|
||||||
|
|
||||||
elif turn - last_turn >= action_cost:
|
|
||||||
agent.take_action(action)
|
|
||||||
|
|
||||||
agent.update_and_draw(const.SCREEN, delta_time, minefield)
|
|
||||||
agent.reset_actions()
|
|
||||||
|
|
||||||
|
# reset in_menu flag
|
||||||
in_menu = True
|
in_menu = True
|
||||||
|
|
||||||
if not any([x.is_selected for x in ui_components_list.ui_components]):
|
is_game_over = game.time_mine_exploded()
|
||||||
INPUT_ROW.set_is_selected(True)
|
|
||||||
|
|
||||||
for component in ui_components_list.selectable_ui_components:
|
# TODO temporarily disabling game over screen
|
||||||
component.set_flags(is_active=True)
|
# if you want to enable game over comment line below
|
||||||
|
|
||||||
in_game_timer += delta_time
|
|
||||||
for x in minefield.matrix:
|
|
||||||
for y in x:
|
|
||||||
if y.mine is not None:
|
|
||||||
if isinstance(y.mine, TimeMine):
|
|
||||||
if y.mine.timer == 0:
|
|
||||||
is_game_over = True
|
|
||||||
|
|
||||||
# temporarily disabling game over screen
|
|
||||||
is_game_over = False
|
is_game_over = False
|
||||||
|
|
||||||
while running and is_game_over:
|
while running and is_game_over:
|
||||||
clock.tick(const.V_FPS)
|
events = pygame.event.get()
|
||||||
const.SCREEN.fill((255, 255, 255))
|
|
||||||
tb = TextBox(position=(150, 200), dimensions=(500, 100), text="Game Over")
|
|
||||||
cont = Button(position=(350, 350), dimensions=(100, 50), text="Try again")
|
|
||||||
close = Button(position=(350, 450), dimensions=(100, 50), text="Close")
|
|
||||||
ev = pygame.event.get()
|
|
||||||
if close.is_clicked(pygame.mouse.get_pos(), ev):
|
|
||||||
running = False
|
|
||||||
if cont.is_clicked(pygame.mouse.get_pos(), ev):
|
|
||||||
minefield = mf.Minefield(const.MAP_RANDOM_10x10)
|
|
||||||
agent = minefield.agent
|
|
||||||
in_menu = True
|
|
||||||
is_game_over = False
|
|
||||||
for component in ui_components_list.selectable_ui_components:
|
|
||||||
component.set_flags(is_active=True)
|
|
||||||
cont.draw(const.SCREEN, pygame.mouse.get_pos())
|
|
||||||
close.draw(const.SCREEN, pygame.mouse.get_pos())
|
|
||||||
tb.draw(const.SCREEN)
|
|
||||||
pygame.display.flip()
|
|
||||||
|
|
||||||
# is_game_over = False
|
clock.tick(const.V_FPS)
|
||||||
|
|
||||||
|
# runs game over screen
|
||||||
|
game.run_game_over_screen(pygame.mouse.get_pos(), events)
|
||||||
|
|
||||||
|
# checking if game should stop running
|
||||||
|
running = not is_quit_button_pressed(events) \
|
||||||
|
and not game.button_close.is_clicked(pygame.mouse.get_pos(), events)
|
||||||
|
|
||||||
|
# resetting is_game_over variable if player tries again
|
||||||
|
if game.button_try_again.is_clicked(pygame.mouse.get_pos(), events):
|
||||||
|
game.cleanup_after_game_loop()
|
||||||
|
is_game_over = False
|
||||||
|
in_menu = True
|
||||||
|
|
||||||
|
# updating graphics
|
||||||
|
pygame.display.flip()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
21
minefield.py
21
minefield.py
@ -10,6 +10,7 @@ class Minefield:
|
|||||||
self.turn = 0
|
self.turn = 0
|
||||||
|
|
||||||
self.agent = ag.Agent(const.MAP_RANDOM_10x10)
|
self.agent = ag.Agent(const.MAP_RANDOM_10x10)
|
||||||
|
self.json_path = json_path
|
||||||
|
|
||||||
# open JSON with minefield info
|
# open JSON with minefield info
|
||||||
json_gen = jg.JsonGenerator()
|
json_gen = jg.JsonGenerator()
|
||||||
@ -45,16 +46,30 @@ class Minefield:
|
|||||||
if mine is not None and isinstance(mine, TimeMine):
|
if mine is not None and isinstance(mine, TimeMine):
|
||||||
mine.timer = max(0, mine.starting_time - int(self.turn / 4))
|
mine.timer = max(0, mine.starting_time - int(self.turn / 4))
|
||||||
|
|
||||||
|
def disarm_mine(self, x, y):
|
||||||
|
tile = self.matrix[x][y]
|
||||||
|
mine = tile.mine
|
||||||
|
mine.disarm()
|
||||||
|
|
||||||
# ================ #
|
# ================ #
|
||||||
# === MOVEMENT === #
|
# === MOVEMENT === #
|
||||||
# ================ #
|
# ================ #
|
||||||
|
|
||||||
# check if sapper's destination is accessible
|
# check if sapper's destination is accessible
|
||||||
# If Agent comes upon a tile with a mine his starting position shall be reestablished
|
# If Agent comes upon a tile with a mine his starting position shall be reestablished
|
||||||
def is_valid_move(self, target_row: int, target_column: int):
|
@staticmethod
|
||||||
|
def is_valid_move(target_row: int, target_column: int):
|
||||||
if 0 <= target_row < const.V_GRID_VER_TILES \
|
if 0 <= target_row < const.V_GRID_VER_TILES \
|
||||||
and 0 <= target_column < const.V_GRID_HOR_TILES \
|
and 0 <= target_column < const.V_GRID_HOR_TILES:
|
||||||
and self.matrix[target_row][target_column].mine is None:
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
# ============= #
|
||||||
|
# === OTHER === #
|
||||||
|
# ============= #
|
||||||
|
|
||||||
|
# method that allows copying classes instance
|
||||||
|
def __copy__(self):
|
||||||
|
copy = Minefield(self.json_path)
|
||||||
|
return copy
|
||||||
|
@ -11,4 +11,6 @@ class ChainedMine(Mine):
|
|||||||
super().__init__(position, active)
|
super().__init__(position, active)
|
||||||
|
|
||||||
def disarm(self):
|
def disarm(self):
|
||||||
|
if (self.predecessor.active == False):
|
||||||
|
super().disarm()
|
||||||
pass
|
pass
|
||||||
|
@ -16,4 +16,4 @@ class Mine(ABC):
|
|||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def disarm(self):
|
def disarm(self):
|
||||||
pass
|
self.active = False
|
||||||
|
@ -7,4 +7,4 @@ class StandardMine(Mine):
|
|||||||
super().__init__(position, active)
|
super().__init__(position, active)
|
||||||
|
|
||||||
def disarm(self):
|
def disarm(self):
|
||||||
pass
|
super().disarm()
|
||||||
|
@ -9,4 +9,4 @@ class TimeMine(Mine):
|
|||||||
super().__init__(position, active)
|
super().__init__(position, active)
|
||||||
|
|
||||||
def disarm(self):
|
def disarm(self):
|
||||||
pass
|
super().disarm()
|
||||||
|
@ -22,7 +22,7 @@ V_NAME_OF_WINDOW = "MineFusion TM"
|
|||||||
DIR_ASSETS = os.path.join("resources", "assets")
|
DIR_ASSETS = os.path.join("resources", "assets")
|
||||||
V_FPS = 60
|
V_FPS = 60
|
||||||
|
|
||||||
ACTION_INTERVAL = 0.3 # interval between two actions in seconds
|
TURN_INTERVAL = 0.3 # interval between two turns in seconds
|
||||||
|
|
||||||
V_TILE_SIZE = 60
|
V_TILE_SIZE = 60
|
||||||
V_GRID_VER_TILES = 10 # vertical (number of rows)
|
V_GRID_VER_TILES = 10 # vertical (number of rows)
|
||||||
@ -84,6 +84,7 @@ class Terrain(Enum):
|
|||||||
CONCRETE = 2
|
CONCRETE = 2
|
||||||
GRASS = 5
|
GRASS = 5
|
||||||
MUD = 8
|
MUD = 8
|
||||||
|
MINE = 500
|
||||||
|
|
||||||
|
|
||||||
# =============== #
|
# =============== #
|
||||||
@ -164,6 +165,11 @@ INPUT_ROW = InputBox(
|
|||||||
position=(_gui_x, _gui_y),
|
position=(_gui_x, _gui_y),
|
||||||
dimensions=(_gui_width, _ib_height),
|
dimensions=(_gui_width, _ib_height),
|
||||||
text="row",
|
text="row",
|
||||||
|
box_color=(100, 200, 100),
|
||||||
|
bottom_strip_color=(120, 220, 120),
|
||||||
|
inner_box_color=(120, 220, 120),
|
||||||
|
outline_color=(80, 180, 80),
|
||||||
|
outline_additional_pixel=True,
|
||||||
valid_input_characters="1234567890",
|
valid_input_characters="1234567890",
|
||||||
input_centered=True,
|
input_centered=True,
|
||||||
clear_input_on_click=True
|
clear_input_on_click=True
|
||||||
@ -172,6 +178,11 @@ INPUT_COLUMN = InputBox(
|
|||||||
position=(_gui_x, _gui_y + _ib_height + 10),
|
position=(_gui_x, _gui_y + _ib_height + 10),
|
||||||
dimensions=(_gui_width, _ib_height),
|
dimensions=(_gui_width, _ib_height),
|
||||||
text="column",
|
text="column",
|
||||||
|
box_color=(100, 200, 100),
|
||||||
|
bottom_strip_color=(120, 220, 120),
|
||||||
|
inner_box_color=(120, 220, 120),
|
||||||
|
outline_color=(80, 180, 80),
|
||||||
|
outline_additional_pixel=True,
|
||||||
valid_input_characters="1234567890",
|
valid_input_characters="1234567890",
|
||||||
input_centered=True,
|
input_centered=True,
|
||||||
clear_input_on_click=True
|
clear_input_on_click=True
|
||||||
@ -179,12 +190,18 @@ INPUT_COLUMN = InputBox(
|
|||||||
RANDOM_BUTTON = Button(
|
RANDOM_BUTTON = Button(
|
||||||
position=(_gui_x, _gui_y + 2 * _ib_height + 20),
|
position=(_gui_x, _gui_y + 2 * _ib_height + 20),
|
||||||
dimensions=(_gui_width, _bt_height),
|
dimensions=(_gui_width, _bt_height),
|
||||||
text="random"
|
text="random",
|
||||||
|
box_color=(100, 200, 100),
|
||||||
|
outline_color=(80, 180, 80),
|
||||||
|
outline_additional_pixel=True
|
||||||
)
|
)
|
||||||
OK_BUTTON = Button(
|
OK_BUTTON = Button(
|
||||||
position=(_gui_x, _gui_y + 2 * _ib_height + _bt_height + 30),
|
position=(_gui_x, _gui_y + 2 * _ib_height + _bt_height + 30),
|
||||||
dimensions=(_gui_width, _bt_height),
|
dimensions=(_gui_width, _bt_height),
|
||||||
text="ok"
|
text="ok",
|
||||||
|
box_color=(100, 200, 100),
|
||||||
|
outline_color=(80, 180, 80),
|
||||||
|
outline_additional_pixel=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
6
tile.py
6
tile.py
@ -6,7 +6,9 @@ from project_constants import Terrain
|
|||||||
# It is used in Tile.cost (giving the value to the tile)
|
# It is used in Tile.cost (giving the value to the tile)
|
||||||
|
|
||||||
|
|
||||||
def assume_cost(terrain_type):
|
def assume_cost(terrain_type, mine):
|
||||||
|
if mine is not None:
|
||||||
|
return Terrain.MINE
|
||||||
if terrain_type == "CONCRETE":
|
if terrain_type == "CONCRETE":
|
||||||
return Terrain.CONCRETE
|
return Terrain.CONCRETE
|
||||||
elif terrain_type == "GRASS":
|
elif terrain_type == "GRASS":
|
||||||
@ -19,6 +21,6 @@ class Tile:
|
|||||||
def __init__(self, position, terrain_type=None, mine=None):
|
def __init__(self, position, terrain_type=None, mine=None):
|
||||||
self.position = position
|
self.position = position
|
||||||
self.terrain_type = terrain_type
|
self.terrain_type = terrain_type
|
||||||
self.cost = assume_cost(terrain_type)
|
self.cost = assume_cost(terrain_type, mine)
|
||||||
# mine is an instance of Mine class
|
# mine is an instance of Mine class
|
||||||
self.mine = mine
|
self.mine = mine
|
||||||
|
@ -25,10 +25,10 @@ class InputBox(Button):
|
|||||||
input_box_position=None,
|
input_box_position=None,
|
||||||
input_box_dimensions=None,
|
input_box_dimensions=None,
|
||||||
user_input="",
|
user_input="",
|
||||||
valid_input_characters="abcdefghijklmnoprstuwxyz1234567890",
|
valid_input_characters="abcdefghijklmnoprstuwxyz1234567890 ",
|
||||||
box_color=(195, 195, 195),
|
box_color=(195, 195, 195),
|
||||||
input_box_color=(225, 245, 245),
|
input_box_color=(225, 245, 245),
|
||||||
writing_highlight_color=(150, 255, 255),
|
writing_highlight_color=(200, 255, 255),
|
||||||
inner_box_color=(205, 205, 205),
|
inner_box_color=(205, 205, 205),
|
||||||
bottom_strip_color=(180, 180, 180),
|
bottom_strip_color=(180, 180, 180),
|
||||||
font=pygame.font.get_default_font(),
|
font=pygame.font.get_default_font(),
|
||||||
@ -179,6 +179,7 @@ class InputBox(Button):
|
|||||||
# is not active
|
# is not active
|
||||||
else:
|
else:
|
||||||
input_box_color = self.ib_inactive_color
|
input_box_color = self.ib_inactive_color
|
||||||
|
text_background_color = self.ib_inactive_color
|
||||||
|
|
||||||
# drawing inner (upper) box
|
# drawing inner (upper) box
|
||||||
pygame.draw.rect(window,
|
pygame.draw.rect(window,
|
||||||
|
@ -3,13 +3,15 @@ from ui.button import Button
|
|||||||
from ui.input_box import InputBox
|
from ui.input_box import InputBox
|
||||||
|
|
||||||
|
|
||||||
class UiComponentsList:
|
class UiComponentsManager:
|
||||||
selected = 0
|
selected = 0
|
||||||
|
|
||||||
def __init__(self, ui_components=None, select_first_item=True):
|
def __init__(self, ui_components=None, select_first_item=True):
|
||||||
|
self.ui_components = list()
|
||||||
|
self.selectable_ui_components = list()
|
||||||
|
|
||||||
if ui_components is not None:
|
if ui_components is not None:
|
||||||
self.ui_components = ui_components
|
self.ui_components = ui_components
|
||||||
self.selectable_ui_components = list()
|
|
||||||
|
|
||||||
for component in ui_components:
|
for component in ui_components:
|
||||||
self.selectable_ui_components.append(component) if issubclass(component.__class__, Button) else 0
|
self.selectable_ui_components.append(component) if issubclass(component.__class__, Button) else 0
|
||||||
@ -17,7 +19,8 @@ class UiComponentsList:
|
|||||||
else:
|
else:
|
||||||
self.ui_components = list()
|
self.ui_components = list()
|
||||||
|
|
||||||
self.selectable_ui_components[0].set_is_selected(select_first_item)
|
if any(self.selectable_ui_components):
|
||||||
|
self.selectable_ui_components[0].set_is_selected(select_first_item)
|
||||||
|
|
||||||
def run_all(self, window, mouse_position, events):
|
def run_all(self, window, mouse_position, events):
|
||||||
for component in filter(lambda x: x not in self.selectable_ui_components, self.ui_components):
|
for component in filter(lambda x: x not in self.selectable_ui_components, self.ui_components):
|
||||||
@ -36,7 +39,7 @@ class UiComponentsList:
|
|||||||
for component in self.selectable_ui_components:
|
for component in self.selectable_ui_components:
|
||||||
component.draw(window, mouse_position)
|
component.draw(window, mouse_position)
|
||||||
|
|
||||||
def switch_selected_objects_with_arrow_keys(self, events, mouse_position):
|
def switch_selected_objects_with_arrow_keys(self, mouse_position, events):
|
||||||
for event in events:
|
for event in events:
|
||||||
if event.type == pygame.KEYDOWN:
|
if event.type == pygame.KEYDOWN:
|
||||||
if event.key == pygame.K_ESCAPE:
|
if event.key == pygame.K_ESCAPE:
|
Loading…
Reference in New Issue
Block a user