Used inheritance, fixed metrics bugs and fixed save location

This commit is contained in:
Vasilis Valatsos 2024-04-23 20:43:39 +02:00
parent ef78027ba7
commit 936674419b
25 changed files with 1524 additions and 278 deletions

BIN
assets/.DS_Store vendored

Binary file not shown.

BIN
assets/map/.DS_Store vendored

Binary file not shown.

View file

@ -25,6 +25,15 @@ warrior_stats = {
'speed': 5
}
base_stats = {
'role_id': 0,
'health': 150,
'energy': 70,
'attack': 10,
'magic': 5,
'speed': 5
}
max_stats = {
'health': 300,
'energy': 150,

BIN
effects/.DS_Store vendored

Binary file not shown.

View file

@ -9,31 +9,31 @@ class MagicPlayer:
self.animation_player = animation_player
def heal(self, player, strength, cost, groups):
if player.stats.energy >= cost:
if player.energy >= cost:
self.sounds['heal'].play()
player.stats.health += strength
player.stats.energy -= cost
if player.stats.health >= player.stats.stats['health']:
player.stats.health = player.stats.stats['health']
player.health += strength
player.energy -= cost
if player.health >= player.stats['health']:
player.health = player.stats['health']
self.animation_player.generate_particles(
'aura',
player.animation.rect.center,
player.rect.center,
groups)
self.animation_player.generate_particles(
'heal',
player.animation.rect.center + pygame.math.Vector2(0, -50),
player.rect.center + pygame.math.Vector2(0, -50),
groups)
def flame(self, player, cost, groups):
if player.stats.energy >= cost:
player.stats.energy -= cost
if player.energy >= cost:
player.energy -= cost
if player._input.status.split('_')[0] == 'right':
if player.status.split('_')[0] == 'right':
direction = pygame.math.Vector2(1, 0)
elif player._input.status.split('_')[0] == 'left':
elif player.status.split('_')[0] == 'left':
direction = pygame.math.Vector2(-1, 0)
elif player._input.status.split('_')[0] == 'up':
elif player.status.split('_')[0] == 'up':
direction = pygame.math.Vector2(0, -1)
else:
direction = pygame.math.Vector2(0, 1)
@ -41,17 +41,17 @@ class MagicPlayer:
for i in range(1, 6):
if direction.x:
offset_x = direction.x * i * TILESIZE
x = player.animation.rect.centerx + offset_x + \
x = player.rect.centerx + offset_x + \
randint(-TILESIZE // 3, TILESIZE // 3)
y = player.animation.rect.centery + \
y = player.rect.centery + \
randint(-TILESIZE // 3, TILESIZE // 3)
self.animation_player.generate_particles(
'flame', (x, y), groups)
else:
offset_y = direction.y * i * TILESIZE
x = player.animation.rect.centerx + \
x = player.rect.centerx + \
randint(-TILESIZE // 3, TILESIZE // 3)
y = player.animation.rect.centery + offset_y + \
y = player.rect.centery + offset_y + \
randint(-TILESIZE // 3, TILESIZE // 3)
self.animation_player.generate_particles(
'flame', (x, y), groups)

View file

@ -10,26 +10,26 @@ class Weapon(pygame.sprite.Sprite):
super().__init__(groups)
self.sprite_type = 'weapon'
direction = player._input.status.split('_')[0]
direction = player.status.split('_')[0]
# Graphic
self.image = pygame.image.load(import_assets(os.path.join(
'graphics',
'weapons',
player._input.combat.weapon,
player.weapon,
f"{direction}.png"))
).convert_alpha()
# Sprite Placement
if direction == 'right':
self.rect = self.image.get_rect(
midleft=player.animation.rect.midright + pygame.math.Vector2(0, 16))
midleft=player.rect.midright + pygame.math.Vector2(0, 16))
elif direction == 'left':
self.rect = self.image.get_rect(
midright=player.animation.rect.midleft + pygame.math.Vector2(0, 16))
midright=player.rect.midleft + pygame.math.Vector2(0, 16))
elif direction == 'down':
self.rect = self.image.get_rect(
midtop=player.animation.rect.midbottom + pygame.math.Vector2(-10, 0))
midtop=player.rect.midbottom + pygame.math.Vector2(-10, 0))
else:
self.rect = self.image.get_rect(
midbottom=player.animation.rect.midtop + pygame.math.Vector2(-10, 0))
midbottom=player.rect.midtop + pygame.math.Vector2(-10, 0))

BIN
entities/.DS_Store vendored

Binary file not shown.

0
entities/__init__.py Normal file
View file

View file

@ -3,25 +3,26 @@ from random import randint, choice
from config.game.spell_config import magic_data
from config.game.weapon_config import weapon_data
#
from .movement import MovementHandler
from .combat import CombatHandler
class InputHandler:
class InputHandler(MovementHandler, CombatHandler):
def __init__(self):
MovementHandler.__init__(self)
CombatHandler.__init__(self)
def __init__(self, sprite_type, animation_player, ai_controller=False):
self.status = 'down'
self.sprite_type = sprite_type
# Setup Movement
self.movement = MovementHandler(self.sprite_type)
self.move_cooldown = 15
self.can_move = True
self.move_time = None
# Setup Combat
self.combat = CombatHandler(animation_player)
self.attacking = False
self.attack_cooldown = 400
self.attack_time = None
@ -35,7 +36,7 @@ class InputHandler:
self.magic_swap_time = None
# Setup Action Space
self.possible_actions = [0, 1, 2, 3, 4, 5]
self.possible_actions = [0, 1, 2, 3, 4]
self.action = 10
def check_input(self,
@ -43,95 +44,95 @@ class InputHandler:
speed,
hitbox,
obstacle_sprites,
rect,
player):
rect
):
if not self.attacking and self.can_move:
self.move_time = pygame.time.get_ticks()
# Movement Input
if button == 0: # keys[pygame.K_w]:
self.movement.direction.y = -1
if self.action == 0: # keys[pygame.K_w]:
self.direction.y = -1
self.status = 'up'
self.can_move = False
self.action = 0
elif button == 1: # keys[pygame.K_s]:
self.movement.direction.y = 1
elif self.action == 1: # keys[pygame.K_s]:
self.direction.y = 1
self.status = 'down'
self.can_move = False
self.action = 1
else:
self.movement.direction.y = 0
self.direction.y = 0
if button == 2: # keys[pygame.K_a]:
self.movement.direction.x = -1
if self.action == 2: # keys[pygame.K_a]:
self.direction.x = -1
self.status = 'left'
self.can_move = False
self.action = 2
elif button == 3: # keys[pygame.K_d]:
self.movement.direction.x = 1
elif self.action == 3: # keys[pygame.K_d]:
self.direction.x = 1
self.status = 'right'
self.can_move = False
self.action = 3
else:
self.movement.direction.x = 0
self.direction.x = 0
self.movement.move(speed, hitbox, obstacle_sprites, rect)
self.move(speed, hitbox, obstacle_sprites, rect)
# Combat Input
if button == 4 and not self.attacking: # keys[pygame.K_e]
if self.action == 4 and not self.attacking: # keys[pygame.K_e]
self.attacking = True
self.attack_time = pygame.time.get_ticks()
self.combat.create_attack_sprite(player)
self.create_attack_sprite()
self.action = 4
# Magic Input
if button == 5:
if self.action == 5:
self.attacking = True
self.attack_time = pygame.time.get_ticks()
self.combat.magic = list(magic_data.keys())[
self.combat.magic_index]
self.magic = list(magic_data.keys())[
self.magic_index]
strength = list(magic_data.values())[
self.combat.magic_index]['strength'] + player.stats.magic
self.magic_index]['strength'] + self.stats['magic']
cost = list(magic_data.values())[
self.combat.magic_index]['cost']
self.combat.create_magic_sprite(
player, self.combat.magic, strength, cost)
self.magic_index]['cost']
self.create_magic_sprite(
self.magic, strength, cost)
self.action = 5
# Rotating Weapons
if button == 6 and self.can_rotate_weapon:
if self.action == 6 and self.can_rotate_weapon:
self.can_rotate_weapon = False
self.weapon_rotation_time = pygame.time.get_ticks()
if self.combat.weapon_index\
if self.weapon_index\
< len(list(weapon_data.keys())) - 1:
self.combat.weapon_index += 1
self.weapon_index += 1
else:
self.combat.weapon_index = 0
self.weapon_index = 0
self.combat.weapon = list(weapon_data.keys())[
self.combat.weapon_index]
self.weapon = list(weapon_data.keys())[
self.weapon_index]
self.action = 6
# Swap Spells
if button == 7 and self.can_swap_magic:
if self.action == 7 and self.can_swap_magic:
self.can_swap_magic = False
self.magic_swap_time = pygame.time.get_ticks()
if self.combat.magic_index < len(list(magic_data.keys())) - 1:
self.combat.magic_index += 1
if self.magic_index < len(list(magic_data.keys())) - 1:
self.magic_index += 1
else:
self.combat.magic_index = 0
self.magic_index = 0
self.action = 7
def cooldowns(self, vulnerable):
@ -141,11 +142,11 @@ class InputHandler:
if self.attacking:
if current_time - self.attack_time\
> self.attack_cooldown\
+ weapon_data[self.combat.weapon]['cooldown']:
+ weapon_data[self.weapon]['cooldown']:
self.attacking = False
if self.combat.current_attack:
self.combat.delete_attack_sprite()
if self.current_attack:
self.delete_attack_sprite()
if not self.can_rotate_weapon:
if current_time - self.weapon_rotation_time\
@ -160,10 +161,10 @@ class InputHandler:
self.can_swap_magic = True
if not vulnerable:
if current_time - self.combat.hurt_time\
>= self.combat.invulnerability_duration:
if current_time - self.hurt_time\
>= self.invulnerability_duration:
self.combat.vulnerable = True
self.vulnerable = True
if not self.can_move:
if current_time - self.move_time\

View file

@ -1,25 +1,27 @@
import os
import pygame
from math import sin
from utils.resource_loader import import_folder, import_assets
from math import sin
from config.system.window import HITBOX_OFFSET
from effects.particle_effects import AnimationPlayer
class AnimationHandler:
from .stats import StatsHandler
from utils.resource_loader import import_folder, import_assets
class AnimationHandler(StatsHandler):
def __init__(self):
def __init__(self, sprite_type, name=None):
self.frame_index = 0
self.animation_speed = 0.15
self.sprite_type = sprite_type
self.name = name
def import_assets(self, position):
# Import Graphic Assets
# Import graphic assets
if self.sprite_type == 'player':
self.image = pygame.image.load(
import_assets(os.path.join('graphics',
@ -83,8 +85,8 @@ class AnimationHandler:
else:
self.image.set_alpha(255)
def trigger_death_particles(self, animation_player, position, particle_type, groups):
animation_player.generate_particles(
def trigger_death_particles(self, position, particle_type, groups):
AnimationPlayer().generate_particles(
particle_type, position, groups)
def wave_value(self):

View file

@ -1,18 +1,19 @@
from effects.weapon_effects import Weapon
from effects.magic_effects import MagicPlayer
from config.game.weapon_config import weapon_data
from config.game.spell_config import magic_data
from effects.magic_effects import MagicPlayer
from effects.particle_effects import AnimationPlayer
from effects.weapon_effects import Weapon
class CombatHandler:
def __init__(self, animation_player):
self.animation_player = animation_player
def __init__(self):
# Setup Combat
self.magic_player = MagicPlayer(animation_player)
self.magic_player = MagicPlayer(AnimationPlayer())
self.current_attack = None
# Spell and Weapon Rotation
@ -27,20 +28,20 @@ class CombatHandler:
self.hurt_time = None
self.invulnerability_duration = 300
def create_attack_sprite(self, player):
def create_attack_sprite(self):
self.current_attack = Weapon(
player, [player.visible_sprites, player.attack_sprites])
self, [self.visible_sprites, self.attack_sprites])
def delete_attack_sprite(self):
if self.current_attack:
self.current_attack.kill()
self.current_attack = None
def create_magic_sprite(self, player, style, strength, cost):
def create_magic_sprite(self, style, strength, cost):
if style == 'heal':
self.magic_player.heal(player, strength, cost, [
player.visible_sprites])
self.magic_player.heal(self, strength, cost, [
self.visible_sprites])
if style == 'flame':
self.magic_player.flame(
player, cost, [player.visible_sprites, player.attack_sprites])
self, cost, [self.visible_sprites, self.attack_sprites])

View file

@ -3,8 +3,7 @@ import pygame
class MovementHandler:
def __init__(self, sprite_type):
def __init__(self):
self.direction = pygame.math.Vector2()
def move(self, speed, hitbox, obstacle_sprites, rect):

View file

@ -1,19 +1,19 @@
from config.game.player_config import warrior_stats, mage_stats, tank_stats
from config.game.monster_config import monster_data
class StatsHandler:
def __init__(self, sprite_type, role=None, monster_name=None):
def get_stats(self, sprite_type, role=None, monster_name=None):
if sprite_type == 'player':
if role == 'warrior':
self.stats = warrior_stats
elif role == 'tank':
self.stats = tank_stats
elif role == 'mage':
self.stats = mage_stats
else:
self.stats = base_stats
self.role_id = self.stats['role_id']
self.health = self.stats['health']
@ -24,7 +24,6 @@ class StatsHandler:
self.exp = 0
if sprite_type == 'enemy':
self.monster_info = monster_data[monster_name]
self.monster_id = self.monster_info['id']
self.health = self.monster_info['health']

View file

@ -1,36 +1,34 @@
import pygame
from .components.animation import AnimationHandler
from .components.stats import StatsHandler
from .components._input import InputHandler
from .entity import Entity
from effects.particle_effects import AnimationPlayer
class Enemy(pygame.sprite.Sprite):
class Enemy(Entity):
def __init__(self, name, position, groups, visible_sprites, obstacle_sprites):
super().__init__(groups)
def __init__(self,
name,
position,
groups,
visible_sprites,
obstacle_sprites
):
self.sprite_type = "enemy"
super().__init__(groups=groups,
visible_sprites=visible_sprites,
obstacle_sprites=obstacle_sprites,
attack_sprites=None,
attackable_sprites=None)
# Setup stats
self.sprite_type = 'enemy'
self.name = name
self.get_stats(self.sprite_type, monster_name=self.name)
self.visible_sprites = visible_sprites
# Setup Graphics
# Graphics Setup
self.animation_player = AnimationPlayer()
self.animation = AnimationHandler(self.sprite_type, self.name)
self.animation.import_assets(position)
self.image = self.animation.image
self.rect = self.animation.rect
# Setup Inputs
self._input = InputHandler(
self.sprite_type, self.animation_player)
# Setup Stats
self.stats = StatsHandler(self.sprite_type, monster_name=self.name)
self.obstacle_sprites = obstacle_sprites
self.import_assets(position)
self.distance_direction_from_player = None
@ -38,49 +36,48 @@ class Enemy(pygame.sprite.Sprite):
player_distance = sorted(
self.distance_direction_from_player, key=lambda x: x[0])[0]
if player_distance[0] < self.stats.notice_radius and player_distance[0] >= self.stats.attack_radius:
self._input.movement.direction = player_distance[1]
self.animation.status = "move"
self._input.movement.move(
self.stats.speed, self.animation.hitbox, self.obstacle_sprites, self.animation.rect)
elif player_distance[0] <= self.stats.attack_radius:
self.animation.status = 'attack'
if player_distance[0] < self.notice_radius and player_distance[0] >= self.attack_radius:
self.direction = player_distance[1]
self.status = "move"
self.move(
self.speed, self.hitbox, self.obstacle_sprites, self.rect)
elif player_distance[0] <= self.attack_radius:
self.status = 'attack'
else:
self.animation.status = 'idle'
self.status = 'idle'
def add_exp(self, player):
player.stats.exp += self.stats.exp
player.exp += self.exp
def check_death(self, player):
if self.stats.health <= 0:
if self.health <= 0:
self.add_exp(player)
self.animation.trigger_death_particles(
self.animation_player, self.rect.center, self.name, self.visible_sprites)
self.trigger_death_particles(
self.rect.center, self.name, self.visible_sprites)
self.kill()
def get_damaged(self, player, attack_type):
if self._input.combat.vulnerable:
if self.vulnerable:
for _, direction, attacking_player in self.distance_direction_from_player:
if attacking_player == player:
self._input.movement.direction = -direction
self._input.movement.move(
self.stats.speed * self.stats.knockback, self.animation.hitbox, self.obstacle_sprites, self.animation.rect)
self.direction = -direction
self.move(
self.speed * self.knockback, self.hitbox, self.obstacle_sprites, self.rect)
break
if attack_type == 'weapon':
self.stats.health -= player.get_full_weapon_damage()
self.health -= player.get_full_weapon_damage()
else:
self.stats.health -= player.get_full_magic_damage()
self.health -= player.get_full_magic_damage()
self.check_death(player)
self._input.combat.hurt_time = pygame.time.get_ticks()
self._input.combat.vulnerable = False
self.hurt_time = pygame.time.get_ticks()
self.vulnerable = False
def update(self):
self.get_action()
self.animation.animate(self.animation.status,
self._input.combat.vulnerable)
self.image = self.animation.image
self.rect = self.animation.rect
self.animate(self.status, self.vulnerable)
self.image = self.image
self.rect = self.rect
self._input.cooldowns(self._input.combat.vulnerable)
self.cooldowns(self.vulnerable)

33
entities/entity.py Normal file
View file

@ -0,0 +1,33 @@
import pygame
import numpy as np
from random import randint
from .components._input import InputHandler
from .components.animation import AnimationHandler
from effects.particle_effects import AnimationPlayer
class Entity(pygame.sprite.Sprite, AnimationHandler, InputHandler):
def __init__(
self,
groups,
obstacle_sprites,
visible_sprites,
attack_sprites = None,
attackable_sprites = None
):
super().__init__(groups)
AnimationHandler.__init__(self)
InputHandler.__init__(self)
# Sprite Setup
self.obstacle_sprites = obstacle_sprites
self.visible_sprites = visible_sprites
self.attack_sprites = attack_sprites
self.attackable_sprites = attackable_sprites

View file

@ -5,16 +5,14 @@ from random import randint
from config.game.weapon_config import weapon_data
from config.game.spell_config import magic_data
from .components.stats import StatsHandler
from .components._input import InputHandler
from .components.animation import AnimationHandler
from .entity import Entity
from effects.particle_effects import AnimationPlayer
from ml.ppo.agent import Agent
class Player(pygame.sprite.Sprite):
class Player(Entity):
def __init__(self,
player_id,
role,
@ -26,32 +24,26 @@ class Player(pygame.sprite.Sprite):
attack_sprites,
attackable_sprites
):
super().__init__(groups)
super().__init__(groups=groups,
obstacle_sprites=obstacle_sprites,
visible_sprites=visible_sprites,
attack_sprites=attack_sprites, attackable_sprites=attackable_sprites)
# Setup stats
self.sprite_type = 'player'
self.get_stats(self.sprite_type, role=role)
# Graphics Setup
self.animation_player = AnimationPlayer()
self.import_assets(position)
# Set misc
self.initial_position = position
self.map_edge = map_edge
self.player_id = player_id
self.distance_direction_from_enemy = None
# Sprite Setup
self.sprite_type = "player"
self.obstacle_sprites = obstacle_sprites
self.visible_sprites = visible_sprites
self.attack_sprites = attack_sprites
self.attackable_sprites = attackable_sprites
# Graphics Setup
self.animation_player = AnimationPlayer()
self.animation = AnimationHandler(self.sprite_type)
self.animation.import_assets(position)
# Input Setup
self._input = InputHandler(
self.sprite_type, self.animation_player)
# Setup Stats
self.role = role
self.stats = StatsHandler(self.sprite_type, self.role)
def setup_agent(self,
gamma,
alpha,
@ -69,7 +61,7 @@ class Player(pygame.sprite.Sprite):
self.agent = Agent(
input_dims=self.num_features,
n_actions=len(self._input.possible_actions),
n_actions=len(self.possible_actions),
gamma=gamma,
alpha=alpha,
policy_clip=policy_clip,
@ -97,24 +89,24 @@ class Player(pygame.sprite.Sprite):
\nSkipping loading ...\n")
def get_status(self):
if self._input.movement.direction.x == 0\
and self._input.movement.direction.y == 0:
if self.direction.x == 0\
and self.direction.y == 0:
if 'idle' not in self._input.status and 'attack' not in self._input.status:
self._input.status += '_idle'
if 'idle' not in self.status and 'attack' not in self.status:
self.status += '_idle'
if self._input.attacking:
self._input.movement.direction.x = 0
self._input.movement.direction.y = 0
if 'attack' not in self._input.status:
if 'idle' in self._input.status:
self._input.status = self._input.status.replace(
if self.attacking:
self.direction.x = 0
self.direction.y = 0
if 'attack' not in self.status:
if 'idle' in self.status:
self.status = self.status.replace(
'idle', 'attack')
else:
self._input.status += '_attack'
self.status += '_attack'
else:
if 'attack' in self._input.status:
self._input.status = self._input.status.replace('_attack', '')
if 'attack' in self.status:
self.status = self.status.replace('_attack', '')
def attack_logic(self):
if self.attack_sprites:
@ -137,13 +129,13 @@ class Player(pygame.sprite.Sprite):
self, attack_sprite.sprite_type)
def get_full_weapon_damage(self):
base_damage = self.stats.attack
weapon_damage = weapon_data[self._input.combat.weapon]['damage']
base_damage = self.attack
weapon_damage = weapon_data[self.weapon]['damage']
return (base_damage + weapon_damage)
def get_full_magic_damage(self):
base_damage = self.stats.magic
spell_damage = magic_data[self._input.combat.magic]['strength']
base_damage = self.stats['magic']
spell_damage = magic_data[self.magic]['strength']
return (base_damage + spell_damage)
def get_current_state(self):
@ -156,30 +148,24 @@ class Player(pygame.sprite.Sprite):
nearest_dist, nearest_en_dir, nearest_enemy = sorted_distances[0]
self.action_features = [self._input.action]
self.action_features = [self.action]
if hasattr(self, 'state_features'):
self.old_state_features = self.state_features
self.reward = self.stats.exp\
+ self.stats.health/self.stats.stats['health']
self.reward = self.exp\
+ self.health/self.stats['health']
# - nearest_dist/np.sqrt(np.sum(self.map_edge))
self.state_features = [
self.animation.rect.center[0]/self.map_edge[0],
self.animation.rect.center[1]/self.map_edge[1],
self._input.movement.direction.x,
self._input.movement.direction.y,
self.stats.health/self.stats.stats['health'],
self.stats.energy/self.stats.stats['energy'],
self.rect.center[0]/self.map_edge[0],
self.rect.center[1]/self.map_edge[1],
self.direction.x,
self.direction.y,
self.health/self.stats['health'],
self.energy/self.stats['energy'],
]
# self.state_features.extend([
# nearest_dist/np.sqrt(np.sum(self.map_edge)),
# nearest_en_dir[0],
# nearest_en_dir[1],
# nearest_enemy.stats.exp
# ])
for distance, direction, enemy in sorted_distances[:5]:
self.state_features.extend([
@ -189,10 +175,10 @@ class Player(pygame.sprite.Sprite):
direction[1],
enemy.stats.health /
enemy.stats.monster_info['health'],
enemy.health /
enemy.monster_info['health'],
enemy.stats.exp,
enemy.exp,
])
if hasattr(self, 'num_features'):
@ -202,9 +188,9 @@ class Player(pygame.sprite.Sprite):
self.state_features = np.array(self.state_features)
def is_dead(self):
if self.stats.health <= 0:
self.stats.health = 0
self.animation.import_assets((3264, 448))
if self.health <= 0:
self.health = 0
self.import_assets((3264, 448))
return True
else:
return False
@ -215,18 +201,18 @@ class Player(pygame.sprite.Sprite):
self.get_current_state()
# Choose action based on current state
action, probs, value\
self.action, probs, value\
= self.agent.choose_action(self.state_features)
# Apply chosen action
self._input.check_input(action,
self.stats.speed,
self.animation.hitbox,
self.check_input(self.action,
self.speed,
self.hitbox,
self.obstacle_sprites,
self.animation.rect,
self)
self.rect
)
self.agent.remember(self.state_features, action,
self.agent.remember(self.state_features, self.action,
probs, value, self.reward, self.is_dead())
self.get_current_state()
@ -236,11 +222,11 @@ class Player(pygame.sprite.Sprite):
self.agent_update()
# Cooldowns and Regen
self.stats.health_recovery()
self.stats.energy_recovery()
self.health_recovery()
self.energy_recovery()
# Refresh player based on input and animate
self.get_status()
self.animation.animate(
self._input.status, self._input.combat.vulnerable)
self._input.cooldowns(self._input.combat.vulnerable)
self.animate(
self.status, self.vulnerable)
self.cooldowns(self.vulnerable)

BIN
figures/.DS_Store vendored

Binary file not shown.

View file

@ -50,8 +50,6 @@ class Level:
self.possible_player_locations = []
player_id = 0
self.layouts = {
'boundary': import_csv_layout(os.path.join('map',
'FloorBlocks.csv')),
@ -195,16 +193,16 @@ class Level:
for player in self.player_sprites:
player.animation.import_assets(
player.import_assets(
choice(self.possible_player_locations))
player.stats.health\
= player.stats.stats['health']
player.health\
= player.stats['health']
player.stats.energy\
= player.stats.stats['energy']
player.energy\
= player.stats['energy']
player.stats.exp = 0
player.exp = 0
self.get_entities()
self.get_distance_direction()
@ -235,12 +233,12 @@ class Level:
for player in self.player_sprites:
if not player.is_dead():
player_vector = pygame.math.Vector2(
player.animation.rect.center
player.rect.center
)
for enemy in self.enemy_sprites:
enemy_vector = pygame.math.Vector2(
enemy.animation.rect.center
enemy.rect.center
)
distance\
= (player_vector - enemy_vector).magnitude()
@ -261,12 +259,12 @@ class Level:
for enemy in self.enemy_sprites:
for distance, _, player in enemy.distance_direction_from_player:
if (distance < enemy.stats.attack_radius
and player._input.combat.vulnerable):
if (distance < enemy.attack_radius
and player.vulnerable):
player.stats.health -= enemy.stats.attack
player._input.combat.vulnerable = False
player._input.combat.hurt_time = pygame.time.get_ticks()
player.health -= enemy.attack
player.vulnerable = False
player.hurt_time = pygame.time.get_ticks()
def toggle_pause(self):
self.paused = not self.paused
@ -279,7 +277,7 @@ class Level:
if not self.paused:
# Update the game
for player in self.player_sprites:
if player.stats.health > 0:
if player.health > 0:
player.attack_logic()
self.get_entities()

View file

@ -180,8 +180,9 @@ def main():
# Save models
player.agent.save_models(
f"A{player.player_id}",
f"C{player.player_id}")
os.path.join(chkpt_path, f"A{player.player_id}"),
os.path.join(chkpt_path, f"C{player.player_id}")
)
print(f"Models saved to {chkpt_path}")

1190
poetry.lock generated Normal file

File diff suppressed because it is too large Load diff

45
pyproject.toml Normal file
View file

@ -0,0 +1,45 @@
[tool.poetry]
name = "thesis"
version = "0.8.1"
description = "A training environment for MARL"
authors = ["Vasilis Valatsos <vasilvalat@gmail.com>"]
license = "MPL-2.0"
readme = "README.md"
package-mode = false
[tool.poetry.dependencies]
python = "^3.11"
contourpy = "^1.2.0"
cycler = "^0.12.1"
docstring-to-markdown = "^0.13"
filelock = "^3.13.1"
fonttools = "^4.45.1"
fsspec = "^2023.10.0"
jedi = "^0.19.1"
jinja2 = "^3.1.2"
kiwisolver = "^1.4.5"
markupsafe = "^2.1.3"
matplotlib = "^3.8.2"
mpmath = "^1.3.0"
networkx = "^3.2.1"
numpy = "^1.26.2"
packaging = "^23.2"
parso = "^0.8.3"
pillow = "^10.1.0"
pluggy = "^1.3.0"
pygame = "^2.5.2"
pyparsing = "^3.1.1"
python-dateutil = "^2.8.2"
python-lsp-jsonrpc = "^1.1.2"
python-lsp-server = "^1.9.0"
six = "^1.16.0"
sympy = "^1.12"
torch = "^2.1.0"
tqdm = "^4.66.1"
typing-extensions = "^4.8.0"
ujson = "^5.8.0"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

View file

@ -1,29 +0,0 @@
contourpy==1.2.0
cycler==0.12.1
docstring-to-markdown==0.13
filelock==3.13.1
fonttools==4.45.1
fsspec==2023.10.0
jedi==0.19.1
Jinja2==3.1.2
kiwisolver==1.4.5
MarkupSafe==2.1.3
matplotlib==3.8.2
mpmath==1.3.0
networkx==3.2.1
numpy==1.26.2
packaging==23.2
parso==0.8.3
Pillow==10.1.0
pluggy==1.3.0
pygame==2.5.2
pyparsing==3.1.1
python-dateutil==2.8.2
python-lsp-jsonrpc==1.1.2
python-lsp-server==1.9.0
six==1.16.0
sympy==1.12
torch==2.1.0
tqdm==4.66.1
typing_extensions==4.8.0
ujson==5.8.0

11
shell.nix Normal file
View file

@ -0,0 +1,11 @@
with import <nixpkgs> {};
mkShell {
NIX_LD_LIBRARY_PATH = lib.makeLibraryPath [
stdenv.cc.cc
];
NIX_LD = lib.fileContents "${stdenv.cc}/nix-support/dynamic-linker";
shellHook = ''
export LD_LIBRARY_PATH=$NIX_LD_LIBRARY_PATH
'';
}

View file

@ -13,7 +13,8 @@ def plot_learning_curve(scores, num_players, figure_path, n_episodes):
for score in scores:
running_avg = np.zeros(len(score))
for i in range(len(score)):
running_avg[i] = np.mean(score[max(0, i-int(n_episodes/10)):(i+1)])
if score[i] != 0:
running_avg[i] = np.mean(score[max(0, i-int(n_episodes/10)):i+1])
plt.plot(running_avg)
plt.savefig(os.path.join(figure_path, "avg_score.png"))
plt.close()
@ -50,6 +51,7 @@ def plot_loss(nn_type, losses, num_players, figure_path, n_episodes):
for loss in losses:
running_avg = np.zeros(len(loss))
for i in range(len(loss)):
if loss[i] != 0:
running_avg[i] = np.mean(loss[max(0, i-int(n_episodes/10)):(i+1)])
plt.plot(running_avg)
plt.savefig(os.path.join(figure_path, f"{nn_type}_loss.png"))
@ -66,6 +68,7 @@ def plot_parameter(name, parameter, num_players, figure_path, n_episodes):
for param in parameter:
running_avg = np.zeros(len(param))
for i in range(len(param)):
if param[i] != 0:
running_avg[i] = np.mean(param[max(0, i-int(n_episodes/10)):(i+1)])
plt.plot(running_avg)
plt.savefig(os.path.join(figure_path, f"{name}.png"))