2021-03-26 16:36:21 +01:00
|
|
|
import project_constants as const
|
2021-05-23 05:50:49 +02:00
|
|
|
from assets import asset_constants as asset
|
2021-03-26 16:36:21 +01:00
|
|
|
import json
|
2021-05-23 20:20:02 +02:00
|
|
|
from time import sleep
|
2021-05-18 20:37:31 +02:00
|
|
|
from pygame import transform
|
2021-05-23 20:20:02 +02:00
|
|
|
from algorithms.learn.decision_tree import DecisionTree
|
2021-03-26 16:36:21 +01:00
|
|
|
|
2021-04-17 19:56:34 +02:00
|
|
|
|
|
|
|
# Class of our agent, initialization of it
|
2021-05-07 22:26:58 +02:00
|
|
|
# movement functions (those defined 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));
|
2021-04-17 19:56:34 +02:00
|
|
|
|
2021-03-26 16:36:21 +01:00
|
|
|
class Agent:
|
2021-05-12 14:33:26 +02:00
|
|
|
last_action = None
|
|
|
|
new_action = None
|
|
|
|
|
2021-03-26 16:36:21 +01:00
|
|
|
def __init__(self, json_path):
|
|
|
|
with open(json_path) as json_data:
|
|
|
|
data = json.load(json_data)
|
2021-05-07 22:26:58 +02:00
|
|
|
self.row, self.column = data["agents_initial_state"]["position"].split(",")
|
2021-05-18 20:37:31 +02:00
|
|
|
self.row, self.column = int(self.row), int(self.column)
|
|
|
|
self.position = [self.row, self.column]
|
2021-05-12 14:33:26 +02:00
|
|
|
self.on_screen_coordinates = const.get_tile_coordinates(tuple(self.position))
|
2021-05-23 20:20:02 +02:00
|
|
|
self.decision_tree = DecisionTree(const.ROOT_DIR + "/algorithms/learn/decision_tree.joblib",
|
|
|
|
const.ROOT_DIR + "/algorithms/learn/dict_vectorizer.joblib")
|
2021-05-07 22:26:58 +02:00
|
|
|
self.direction = const.Direction(data["agents_initial_state"]["direction"])
|
2021-05-12 14:33:26 +02:00
|
|
|
self.rotation_angle = -const.Direction(self.direction).value * 90
|
|
|
|
self.going_forward = False
|
|
|
|
self.rotating_left = False
|
|
|
|
self.rotating_right = False
|
|
|
|
|
2021-05-23 20:20:02 +02:00
|
|
|
def defuse_a_mine(self, mine):
|
|
|
|
mine_params = mine.investigate()
|
|
|
|
chosen_wire = self.decision_tree.get_answer(mine_params)
|
|
|
|
# TODO temporarily printing chosen wire
|
|
|
|
print("agent's chosen wire: " + str(chosen_wire[0]))
|
|
|
|
sleep(3)
|
|
|
|
return mine.disarm(chosen_wire)
|
|
|
|
|
2021-05-18 20:37:31 +02:00
|
|
|
def update_and_draw(self, window, delta_time, minefield):
|
|
|
|
self.update(delta_time, minefield)
|
2021-05-12 14:33:26 +02:00
|
|
|
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
|
|
|
|
|
2021-05-23 05:50:49 +02:00
|
|
|
rotating_rect = transform.rotate(asset.ASSET_SAPPER, self.rotation_angle).get_rect()
|
2021-05-12 14:33:26 +02:00
|
|
|
rotating_rect.center = (coord_x + tl_dimension, coord_y + tl_dimension)
|
|
|
|
|
2021-05-23 05:50:49 +02:00
|
|
|
window.blit(transform.rotate(asset.ASSET_SAPPER, self.rotation_angle), rotating_rect)
|
2021-05-12 14:33:26 +02:00
|
|
|
|
2021-05-18 20:37:31 +02:00
|
|
|
def update(self, delta_time, minefield):
|
2021-05-12 14:33:26 +02:00
|
|
|
self.new_action = self.going_forward + self.rotating_left * 2 + self.rotating_right * 4
|
|
|
|
|
2021-05-18 20:37:31 +02:00
|
|
|
# counting next agents terrain tile
|
|
|
|
# heading either up or down
|
|
|
|
if const.Direction(self.direction).value % 2 == 0 and self.going_forward:
|
|
|
|
next_row = min(9, self.row + const.Direction(self.direction).value - 1)
|
|
|
|
next_column = self.column
|
|
|
|
|
|
|
|
# heading either left or right
|
|
|
|
else:
|
|
|
|
next_row = self.row
|
|
|
|
next_column = min(9, self.column - const.Direction(self.direction).value + 2)
|
|
|
|
|
|
|
|
value_modifier = minefield.matrix[next_row][next_column].cost.value
|
|
|
|
|
2021-05-12 14:33:26 +02:00
|
|
|
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:
|
2021-05-18 20:37:31 +02:00
|
|
|
self.on_screen_coordinates = (x, y + (direction - 1) * const.V_TILE_SIZE * delta_time / value_modifier)
|
2021-05-12 14:33:26 +02:00
|
|
|
|
|
|
|
# heading either left or right
|
|
|
|
else:
|
2021-05-18 20:37:31 +02:00
|
|
|
self.on_screen_coordinates = (x - (direction - 2) * const.V_TILE_SIZE * delta_time / value_modifier, y)
|
2021-05-12 14:33:26 +02:00
|
|
|
|
|
|
|
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()
|
2021-04-17 19:56:34 +02:00
|
|
|
|
|
|
|
def rotate_left(self):
|
|
|
|
self.direction = self.direction.previous()
|
|
|
|
|
|
|
|
def rotate_right(self):
|
|
|
|
self.direction = self.direction.next()
|
|
|
|
|
|
|
|
def go(self):
|
|
|
|
if self.direction == const.Direction.RIGHT:
|
|
|
|
temp = self.go_right()
|
|
|
|
self.position[1] = temp[1]
|
2021-05-18 20:37:31 +02:00
|
|
|
self.column += 1
|
2021-04-17 19:56:34 +02:00
|
|
|
elif self.direction == const.Direction.LEFT:
|
|
|
|
temp = self.go_left()
|
|
|
|
self.position[1] = temp[1]
|
2021-05-18 20:37:31 +02:00
|
|
|
self.column -= 1
|
2021-04-17 19:56:34 +02:00
|
|
|
elif self.direction == const.Direction.UP:
|
|
|
|
temp = self.go_up()
|
|
|
|
self.position[0] = temp[0]
|
2021-05-18 20:37:31 +02:00
|
|
|
self.row -= 1
|
2021-04-17 19:56:34 +02:00
|
|
|
elif self.direction == const.Direction.DOWN:
|
|
|
|
temp = self.go_down()
|
|
|
|
self.position[0] = temp[0]
|
2021-05-18 20:37:31 +02:00
|
|
|
self.row += 1
|
2021-04-17 19:56:34 +02:00
|
|
|
|
2021-03-26 16:36:21 +01:00
|
|
|
def go_right(self):
|
2021-04-17 19:56:34 +02:00
|
|
|
|
2021-04-11 19:04:06 +02:00
|
|
|
return self.position[0], self.position[1] + 1
|
2021-03-26 16:36:21 +01:00
|
|
|
|
|
|
|
def go_left(self):
|
2021-04-17 19:56:34 +02:00
|
|
|
|
2021-04-11 19:04:06 +02:00
|
|
|
return self.position[0], self.position[1] - 1
|
2021-03-26 16:36:21 +01:00
|
|
|
|
|
|
|
def go_up(self):
|
2021-04-17 19:56:34 +02:00
|
|
|
|
2021-04-11 19:04:06 +02:00
|
|
|
return self.position[0] - 1, self.position[1]
|
2021-03-26 16:36:21 +01:00
|
|
|
|
|
|
|
def go_down(self):
|
2021-04-17 19:56:34 +02:00
|
|
|
|
|
|
|
return self.position[0] + 1, self.position[1]
|
2021-05-12 14:33:26 +02:00
|
|
|
|
|
|
|
def reset_actions(self):
|
|
|
|
self.going_forward = self.rotating_right = self.rotating_left = False
|