added agent movement animations; redefined game loop; added turn counting; adjusted classes; fixed a bug; code refactoring
This commit is contained in:
parent
0fc5cedae6
commit
af238a6ed6
69
agent.py
69
agent.py
@ -1,5 +1,6 @@
|
|||||||
import project_constants as const
|
import project_constants as const
|
||||||
import json
|
import json
|
||||||
|
from pygame import transform, Surface, SRCALPHA
|
||||||
|
|
||||||
|
|
||||||
# Class of our agent, initialization of it
|
# Class of our agent, initialization of it
|
||||||
@ -7,13 +8,78 @@ import json
|
|||||||
# values that are later used by another function called 'is_valid_move' (which is defined in Minefield));
|
# values that are later used by another function called 'is_valid_move' (which is defined in Minefield));
|
||||||
|
|
||||||
class Agent:
|
class Agent:
|
||||||
|
last_action = None
|
||||||
|
new_action = None
|
||||||
|
|
||||||
def __init__(self, json_path):
|
def __init__(self, json_path):
|
||||||
with open(json_path) as json_data:
|
with open(json_path) as json_data:
|
||||||
data = json.load(json_data)
|
data = json.load(json_data)
|
||||||
self.row, self.column = data["agents_initial_state"]["position"].split(",")
|
self.row, self.column = data["agents_initial_state"]["position"].split(",")
|
||||||
self.position = [int(self.row), int(self.column)]
|
self.position = [int(self.row), int(self.column)]
|
||||||
|
self.on_screen_coordinates = const.get_tile_coordinates(tuple(self.position))
|
||||||
# self.direction = const.Direction()
|
# self.direction = const.Direction()
|
||||||
self.direction = const.Direction(data["agents_initial_state"]["direction"])
|
self.direction = const.Direction(data["agents_initial_state"]["direction"])
|
||||||
|
self.rotation_angle = -const.Direction(self.direction).value * 90
|
||||||
|
self.going_forward = False
|
||||||
|
self.rotating_left = False
|
||||||
|
self.rotating_right = False
|
||||||
|
|
||||||
|
def update_and_draw(self, window, delta_time):
|
||||||
|
self.update(delta_time)
|
||||||
|
self.draw(window)
|
||||||
|
|
||||||
|
def draw(self, window):
|
||||||
|
coord_x, coord_y = self.on_screen_coordinates[0], self.on_screen_coordinates[1]
|
||||||
|
tl_dimension = const.V_TILE_SIZE / 2
|
||||||
|
|
||||||
|
rotating_rect = transform.rotate(const.ASSET_SAPPER, self.rotation_angle).get_rect()
|
||||||
|
rotating_rect.center = (coord_x + tl_dimension, coord_y + tl_dimension)
|
||||||
|
|
||||||
|
window.blit(transform.rotate(const.ASSET_SAPPER, self.rotation_angle), rotating_rect)
|
||||||
|
|
||||||
|
def update(self, delta_time):
|
||||||
|
self.new_action = self.going_forward + self.rotating_left * 2 + self.rotating_right * 4
|
||||||
|
|
||||||
|
if self.going_forward:
|
||||||
|
direction = const.Direction(self.direction).value
|
||||||
|
x, y = self.on_screen_coordinates
|
||||||
|
|
||||||
|
# heading either up or down
|
||||||
|
if direction % 2 == 0:
|
||||||
|
self.on_screen_coordinates = (x, y + (direction - 1) * const.V_TILE_SIZE * delta_time)
|
||||||
|
|
||||||
|
# heading either left or right
|
||||||
|
else:
|
||||||
|
self.on_screen_coordinates = (x - (direction - 2) * const.V_TILE_SIZE * delta_time, y)
|
||||||
|
|
||||||
|
elif self.rotating_right:
|
||||||
|
self.rotation_angle -= delta_time * 90 % 360
|
||||||
|
|
||||||
|
elif self.rotating_left:
|
||||||
|
self.rotation_angle += delta_time * 90 % 360
|
||||||
|
|
||||||
|
if self.last_action != self.new_action or \
|
||||||
|
not any((self.going_forward, self.rotating_right, self.rotating_left)):
|
||||||
|
self.last_action = self.new_action
|
||||||
|
self.rotation_angle = -const.Direction(self.direction).value * 90
|
||||||
|
self.on_screen_coordinates = const.get_tile_coordinates(tuple(self.position))
|
||||||
|
|
||||||
|
def animate(self, action):
|
||||||
|
self.reset_actions()
|
||||||
|
if action == const.Action.ROTATE_LEFT:
|
||||||
|
self.rotating_left = True
|
||||||
|
elif action == const.Action.ROTATE_RIGHT:
|
||||||
|
self.rotating_right = True
|
||||||
|
elif action == const.Action.GO:
|
||||||
|
self.going_forward = True
|
||||||
|
|
||||||
|
def take_action(self, action):
|
||||||
|
if action == const.Action.ROTATE_LEFT:
|
||||||
|
self.rotate_left()
|
||||||
|
elif action == const.Action.ROTATE_RIGHT:
|
||||||
|
self.rotate_right()
|
||||||
|
elif action == const.Action.GO:
|
||||||
|
self.go()
|
||||||
|
|
||||||
def rotate_left(self):
|
def rotate_left(self):
|
||||||
self.direction = self.direction.previous()
|
self.direction = self.direction.previous()
|
||||||
@ -50,3 +116,6 @@ class Agent:
|
|||||||
def go_down(self):
|
def go_down(self):
|
||||||
|
|
||||||
return self.position[0] + 1, self.position[1]
|
return self.position[0] + 1, self.position[1]
|
||||||
|
|
||||||
|
def reset_actions(self):
|
||||||
|
self.going_forward = self.rotating_right = self.rotating_left = False
|
||||||
|
@ -69,14 +69,15 @@ def blit_graphics(minefield):
|
|||||||
elif isinstance(tile.mine, TimeMine):
|
elif isinstance(tile.mine, TimeMine):
|
||||||
display_time_mine(tile.position, "0" + str(tile.mine.timer))
|
display_time_mine(tile.position, "0" + str(tile.mine.timer))
|
||||||
|
|
||||||
# sapper
|
# changed sapper's movement from jumping to continuous movement (moved everything to Agent's class)
|
||||||
display_sapper(
|
# # sapper
|
||||||
minefield.agent.position,
|
# display_sapper(
|
||||||
minefield.agent.direction
|
# minefield.agent.position,
|
||||||
)
|
# minefield.agent.direction
|
||||||
|
# )
|
||||||
|
|
||||||
# update graphics after blitting
|
# update graphics after blitting
|
||||||
pygame.display.update()
|
# pygame.display.update()
|
||||||
|
|
||||||
|
|
||||||
# moved here from minefield
|
# moved here from minefield
|
||||||
@ -216,12 +217,12 @@ def display_chained_mine(coords, parent_coords=None):
|
|||||||
|
|
||||||
display_mine(coords)
|
display_mine(coords)
|
||||||
|
|
||||||
if parent_coords is not None:
|
const.SCREEN.blit(
|
||||||
const.SCREEN.blit(
|
const.ASSET_CHAINS,
|
||||||
const.ASSET_CHAINS,
|
calculate_screen_position(coords)
|
||||||
calculate_screen_position(coords)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
|
if parent_coords is not None:
|
||||||
display_number(const.Coords.X, parent_coords[0])
|
display_number(const.Coords.X, parent_coords[0])
|
||||||
display_number(const.Coords.Y, parent_coords[1])
|
display_number(const.Coords.Y, parent_coords[1])
|
||||||
|
|
||||||
|
54
main.py
54
main.py
@ -27,10 +27,14 @@ def main():
|
|||||||
# create an instance of Minefield, pass necessary data
|
# create an instance of Minefield, pass necessary data
|
||||||
minefield = mf.Minefield(const.MAP_RANDOM_10x10)
|
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
|
||||||
|
|
||||||
|
# creating list storing all ui components
|
||||||
ui_components_list = UiComponentsList([INPUT_ROW, INPUT_COLUMN, RANDOM_BUTTON, OK_BUTTON])
|
ui_components_list = UiComponentsList([INPUT_ROW, INPUT_COLUMN, RANDOM_BUTTON, OK_BUTTON])
|
||||||
|
|
||||||
# initializing goal position
|
# initializing goal position
|
||||||
@ -41,6 +45,10 @@ def main():
|
|||||||
|
|
||||||
while running:
|
while running:
|
||||||
|
|
||||||
|
# ============== #
|
||||||
|
# ==== MENU ==== #
|
||||||
|
# ============== #
|
||||||
|
|
||||||
while running and in_menu:
|
while running and in_menu:
|
||||||
events = pygame.event.get()
|
events = pygame.event.get()
|
||||||
|
|
||||||
@ -51,11 +59,10 @@ def main():
|
|||||||
# graphics (from display_assets)
|
# graphics (from display_assets)
|
||||||
blit_graphics(minefield)
|
blit_graphics(minefield)
|
||||||
|
|
||||||
|
agent.update_and_draw(const.SCREEN, 0)
|
||||||
|
|
||||||
# drawing gui
|
# drawing gui
|
||||||
INPUT_ROW.run(const.SCREEN, pygame.mouse.get_pos(), events)
|
ui_components_list.run_all(const.SCREEN, pygame.mouse.get_pos(), events)
|
||||||
INPUT_COLUMN.run(const.SCREEN, pygame.mouse.get_pos(), events)
|
|
||||||
OK_BUTTON.draw(const.SCREEN, pygame.mouse.get_pos())
|
|
||||||
RANDOM_BUTTON.draw(const.SCREEN, pygame.mouse.get_pos())
|
|
||||||
|
|
||||||
# highlighting chosen tile destination (if exists)
|
# highlighting chosen tile destination (if exists)
|
||||||
if not(INPUT_ROW.empty() or INPUT_COLUMN.empty()):
|
if not(INPUT_ROW.empty() or INPUT_COLUMN.empty()):
|
||||||
@ -79,6 +86,10 @@ def main():
|
|||||||
|
|
||||||
clock.tick(const.V_FPS)
|
clock.tick(const.V_FPS)
|
||||||
|
|
||||||
|
# ========================== #
|
||||||
|
# ==== BEFORE GAME LOOP ==== #
|
||||||
|
# ========================== #
|
||||||
|
|
||||||
if running:
|
if running:
|
||||||
for component in ui_components_list.selectable_ui_components:
|
for component in ui_components_list.selectable_ui_components:
|
||||||
component.set_flags(is_active=False)
|
component.set_flags(is_active=False)
|
||||||
@ -90,6 +101,13 @@ def main():
|
|||||||
direction=minefield.agent.direction),
|
direction=minefield.agent.direction),
|
||||||
minefield=minefield, tox=row, toy=column)
|
minefield=minefield, tox=row, toy=column)
|
||||||
|
|
||||||
|
action = None
|
||||||
|
in_game_timer = 0
|
||||||
|
|
||||||
|
# =================== #
|
||||||
|
# ==== GAME LOOP ==== #
|
||||||
|
# =================== #
|
||||||
|
|
||||||
while running and not in_menu:
|
while running and not in_menu:
|
||||||
|
|
||||||
for event in pygame.event.get():
|
for event in pygame.event.get():
|
||||||
@ -98,11 +116,15 @@ def main():
|
|||||||
|
|
||||||
# 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)
|
# graphics (from display_assets)
|
||||||
blit_graphics(minefield)
|
blit_graphics(minefield)
|
||||||
const.SCREEN.blit(HIGHLIGHT, const.get_tile_coordinates((row, column)))
|
const.SCREEN.blit(HIGHLIGHT, const.get_tile_coordinates((row, column)))
|
||||||
|
|
||||||
|
agent.update_and_draw(const.SCREEN, action_delta_time)
|
||||||
|
|
||||||
# drawing ui components so they don't "disappear"
|
# drawing ui components so they don't "disappear"
|
||||||
ui_components_list.draw_all(const.SCREEN, pygame.mouse.get_pos())
|
ui_components_list.draw_all(const.SCREEN, pygame.mouse.get_pos())
|
||||||
|
|
||||||
@ -110,27 +132,31 @@ def main():
|
|||||||
pygame.display.flip()
|
pygame.display.flip()
|
||||||
|
|
||||||
# make the next move from sequence of actions
|
# make the next move from sequence of actions
|
||||||
if any(action_sequence):
|
if in_game_timer >= const.ACTION_INTERVAL and any(action_sequence):
|
||||||
|
in_game_timer -= const.ACTION_INTERVAL
|
||||||
|
minefield.next_turn()
|
||||||
|
|
||||||
|
agent.take_action(action)
|
||||||
|
|
||||||
action = action_sequence.pop(0)
|
action = action_sequence.pop(0)
|
||||||
|
|
||||||
if action == const.Action.ROTATE_LEFT:
|
agent.animate(action)
|
||||||
minefield.agent.rotate_left()
|
|
||||||
elif action == const.Action.ROTATE_RIGHT:
|
|
||||||
minefield.agent.rotate_right()
|
|
||||||
elif action == const.Action.GO:
|
|
||||||
minefield.agent.go()
|
|
||||||
|
|
||||||
time.sleep(const.ACTION_INTERVAL)
|
elif in_game_timer >= const.ACTION_INTERVAL:
|
||||||
|
agent.take_action(action)
|
||||||
|
|
||||||
|
agent.update_and_draw(const.SCREEN, delta_time)
|
||||||
|
agent.reset_actions()
|
||||||
|
|
||||||
else:
|
|
||||||
in_menu = True
|
in_menu = True
|
||||||
if not any([x.is_selected for x in ui_components_list.ui_components]):
|
if not any([x.is_selected for x in ui_components_list.ui_components]):
|
||||||
INPUT_ROW.set_is_selected(True)
|
INPUT_ROW.set_is_selected(True)
|
||||||
time.sleep(const.ACTION_INTERVAL)
|
|
||||||
|
|
||||||
for component in ui_components_list.selectable_ui_components:
|
for component in ui_components_list.selectable_ui_components:
|
||||||
component.set_flags(is_active=True)
|
component.set_flags(is_active=True)
|
||||||
|
|
||||||
|
in_game_timer += delta_time
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
@ -5,6 +5,7 @@ class TimeMine(Mine):
|
|||||||
def __init__(self, position, timer, active=True):
|
def __init__(self, position, timer, active=True):
|
||||||
self.type = "time"
|
self.type = "time"
|
||||||
self.timer = timer
|
self.timer = timer
|
||||||
|
self.starting_time = timer
|
||||||
super().__init__(position, active)
|
super().__init__(position, active)
|
||||||
|
|
||||||
def disarm(self):
|
def disarm(self):
|
||||||
|
42
minefield.py
42
minefield.py
@ -1,9 +1,7 @@
|
|||||||
import agent as ag
|
import agent as ag
|
||||||
import project_constants as const
|
import project_constants as const
|
||||||
import tile as tl
|
import tile as tl
|
||||||
from mine_models import standard_mine as sm
|
from mine_models.time_mine import TimeMine
|
||||||
from mine_models import time_mine as tm
|
|
||||||
from mine_models import chained_mine as cm
|
|
||||||
import json_generator as jg
|
import json_generator as jg
|
||||||
|
|
||||||
|
|
||||||
@ -37,6 +35,16 @@ class Minefield:
|
|||||||
predecessor = self.matrix[predecessor_row][predecessor_column]
|
predecessor = self.matrix[predecessor_row][predecessor_column]
|
||||||
self.matrix[successor_row][successor_column].mine.predecessor = predecessor
|
self.matrix[successor_row][successor_column].mine.predecessor = predecessor
|
||||||
|
|
||||||
|
def next_turn(self):
|
||||||
|
self.turn += 1
|
||||||
|
|
||||||
|
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 isinstance(mine, TimeMine):
|
||||||
|
mine.timer = max(0, mine.starting_time - self.turn)
|
||||||
|
|
||||||
# ================ #
|
# ================ #
|
||||||
# === MOVEMENT === #
|
# === MOVEMENT === #
|
||||||
# ================ #
|
# ================ #
|
||||||
@ -50,31 +58,3 @@ class Minefield:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# distinguishes new mine's type and creates appropriate object
|
|
||||||
def _create_mine(self, mine_data, row, column):
|
|
||||||
mine_type = mine_data["mine_type"]
|
|
||||||
|
|
||||||
# TIME MINE
|
|
||||||
if mine_type == "time":
|
|
||||||
timer = mine_data["timer"]
|
|
||||||
mine = tm.TimeMine((row, column), int(timer))
|
|
||||||
|
|
||||||
# CHAINED MINE
|
|
||||||
elif mine_type == "chained":
|
|
||||||
if mine_data["predecessor"] is not None:
|
|
||||||
# locate predecessor
|
|
||||||
row, column = map(int, mine_data["predecessor"].split(','))
|
|
||||||
|
|
||||||
# get predecessor object and assign it to the new mine
|
|
||||||
predecessor = self.matrix[row][column].mine
|
|
||||||
mine = cm.ChainedMine((row, column), predecessor)
|
|
||||||
|
|
||||||
else:
|
|
||||||
mine = cm.ChainedMine((row, column))
|
|
||||||
|
|
||||||
# STANDARD MINE
|
|
||||||
else:
|
|
||||||
mine = sm.StandardMine((row, column))
|
|
||||||
|
|
||||||
return mine
|
|
||||||
|
@ -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.75 # interval between two actions in seconds
|
ACTION_INTERVAL = 0.6 # interval between two actions 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)
|
||||||
@ -164,7 +164,7 @@ 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",
|
||||||
valid_input_characters="123456890",
|
valid_input_characters="1234567890",
|
||||||
input_centered=True,
|
input_centered=True,
|
||||||
clear_input_on_click=True
|
clear_input_on_click=True
|
||||||
)
|
)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import pygame
|
import pygame
|
||||||
from ui.button import Button
|
from ui.button import Button
|
||||||
|
from ui.input_box import InputBox
|
||||||
|
|
||||||
|
|
||||||
class UiComponentsList:
|
class UiComponentsList:
|
||||||
@ -18,6 +19,16 @@ class UiComponentsList:
|
|||||||
|
|
||||||
self.selectable_ui_components[0].set_is_selected(select_first_item)
|
self.selectable_ui_components[0].set_is_selected(select_first_item)
|
||||||
|
|
||||||
|
def run_all(self, window, mouse_position, events):
|
||||||
|
for component in filter(lambda x: x not in self.selectable_ui_components, self.ui_components):
|
||||||
|
component.draw(window)
|
||||||
|
|
||||||
|
for component in filter(lambda x: isinstance(x, Button), self.selectable_ui_components):
|
||||||
|
component.draw(window, mouse_position)
|
||||||
|
|
||||||
|
for component in filter(lambda x: isinstance(x, InputBox), self.selectable_ui_components):
|
||||||
|
component.run(window, mouse_position, events)
|
||||||
|
|
||||||
def draw_all(self, window, mouse_position):
|
def draw_all(self, window, mouse_position):
|
||||||
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):
|
||||||
component.draw(window)
|
component.draw(window)
|
||||||
|
Loading…
Reference in New Issue
Block a user