2022-06-19 15:01:30 +02:00
|
|
|
import pygame
|
|
|
|
import math
|
|
|
|
|
|
|
|
|
|
|
|
class Entity(object):
|
|
|
|
def __init__(self, env):
|
|
|
|
self.env = env
|
|
|
|
self.pos = (env.random(), env.random())
|
|
|
|
self.speed = (0, 0)
|
|
|
|
self.acc = (0, 0)
|
|
|
|
self.drag = 0
|
|
|
|
self.radius = 10
|
|
|
|
self.col = (255, 255, 255)
|
|
|
|
self.shape = 'circle'
|
2022-06-29 18:46:59 +02:00
|
|
|
self.solid = False
|
2022-06-30 14:41:32 +02:00
|
|
|
self.movable = False # False = Non movable, True = Movable, x>1: lighter movable
|
2022-06-29 18:46:59 +02:00
|
|
|
self.elasticity = 1
|
2022-08-25 13:39:11 +02:00
|
|
|
#self.collision_changes_speed = True
|
|
|
|
self.collision_changes_speed = self.env.controll_type == 'ACC'
|
|
|
|
self.collision_elasticity = self.env.default_collision_elasticity
|
2022-06-30 14:41:32 +02:00
|
|
|
self._crash_list = []
|
2022-08-20 17:18:03 +02:00
|
|
|
self._coll_add_pushback = 0
|
2022-06-19 15:01:30 +02:00
|
|
|
|
|
|
|
def physics_step(self):
|
|
|
|
x, y = self.pos
|
|
|
|
vx, vy = self.speed
|
|
|
|
ax, ay = self.acc
|
|
|
|
vx, vy = vx+ax*self.env.acc_fac, vy+ay*self.env.acc_fac
|
|
|
|
x, y = x+vx*self.env.speed_fac, y+vy*self.env.speed_fac
|
2022-08-17 19:31:15 +02:00
|
|
|
if not self.env.torus_topology:
|
|
|
|
if x > 1 or x < 0:
|
|
|
|
x, y, vx, vy = self.calc_void_collision(x < 0, x, y, vx, vy)
|
|
|
|
if y > 1 or y < 0:
|
|
|
|
x, y, vx, vy = self.calc_void_collision(
|
|
|
|
2 + (x < 0), x, y, vx, vy)
|
|
|
|
else:
|
|
|
|
x = x % 1
|
|
|
|
y = y % 1
|
2022-06-19 15:01:30 +02:00
|
|
|
self.speed = vx/(1+self.drag), vy/(1+self.drag)
|
|
|
|
self.pos = x, y
|
|
|
|
|
|
|
|
def controll_step(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def step(self):
|
|
|
|
self.controll_step()
|
|
|
|
self.physics_step()
|
2022-06-30 14:41:32 +02:00
|
|
|
self._crash_list = []
|
2022-06-19 15:01:30 +02:00
|
|
|
|
|
|
|
def draw(self):
|
|
|
|
x, y = self.pos
|
|
|
|
pygame.draw.circle(self.env.surface, self.col,
|
|
|
|
(x*self.env.width, y*self.env.height), self.radius, width=0)
|
|
|
|
|
2022-06-29 18:46:59 +02:00
|
|
|
def on_collision(self, other, depth):
|
|
|
|
if self.solid and other.solid:
|
|
|
|
if self.movable:
|
|
|
|
self.on_crash(other, depth)
|
|
|
|
|
|
|
|
def on_crash(self, other, depth):
|
2022-06-30 14:41:32 +02:00
|
|
|
if other in self._crash_list:
|
|
|
|
return
|
|
|
|
self._crash_list.append(other)
|
2022-06-29 18:46:59 +02:00
|
|
|
force_dir = self.pos[0] - other.pos[0], self.pos[1] - other.pos[1]
|
|
|
|
force_dir_len = math.sqrt(force_dir[0]**2+force_dir[1]**2)
|
2022-06-30 14:41:32 +02:00
|
|
|
if force_dir_len == 0:
|
|
|
|
return
|
2022-06-29 18:46:59 +02:00
|
|
|
force_dir = force_dir[0]/force_dir_len, force_dir[1]/force_dir_len
|
2022-08-17 19:31:15 +02:00
|
|
|
if not self.env.torus_topology:
|
|
|
|
if self.env.agent.pos[0] > 0.99 or self.env.agent.pos[0] < 0.01:
|
|
|
|
force_dir = force_dir[0], force_dir[1] * 2
|
|
|
|
if self.env.agent.pos[1] > 0.99 or self.env.agent.pos[1] < 0.01:
|
|
|
|
force_dir = force_dir[0] * 2, force_dir[1]
|
2022-06-30 14:41:32 +02:00
|
|
|
depth *= 1.0*self.movable/(self.movable + other.movable)/2
|
2022-06-29 18:46:59 +02:00
|
|
|
depth /= other.elasticity
|
|
|
|
force_vec = force_dir[0]*depth/self.env.width, \
|
|
|
|
force_dir[1]*depth/self.env.height
|
2022-08-27 12:15:28 +02:00
|
|
|
if sum([abs(f) for f in force_vec]) > 0.005:
|
|
|
|
self.pos = self.pos[0] + force_vec[0], self.pos[1] + force_vec[1]
|
|
|
|
if self._coll_add_pushback:
|
|
|
|
self.pos = self.pos[0] - self.env.inp[0]*self._coll_add_pushback * \
|
|
|
|
self.env.speed_fac, self.pos[1] - self.env.inp[1] * \
|
|
|
|
self._coll_add_pushback*self.env.speed_fac
|
2022-06-30 14:41:32 +02:00
|
|
|
if self.collision_changes_speed:
|
|
|
|
self.speed = self.speed[0] + \
|
2022-08-25 13:39:11 +02:00
|
|
|
force_vec[0]*self.collision_elasticity/self.env.speed_fac, self.speed[1] + \
|
|
|
|
force_vec[1]*self.collision_elasticity/self.env.speed_fac
|
2022-06-30 14:41:32 +02:00
|
|
|
|
|
|
|
def on_collect(self, other):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def on_collected(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def calc_void_collision(self, dir, x, y, vx, vy):
|
|
|
|
if dir < 2:
|
|
|
|
x = min(max(x, 0), 1)
|
2022-08-27 12:03:32 +02:00
|
|
|
vx = -vx*self.collision_elasticity*0.5*self.collision_changes_speed
|
2022-06-30 14:41:32 +02:00
|
|
|
else:
|
|
|
|
y = min(max(y, 0), 1)
|
2022-08-27 12:03:32 +02:00
|
|
|
vy = -vy*self.collision_elasticity*0.5*self.collision_changes_speed
|
2022-06-30 14:41:32 +02:00
|
|
|
return x, y, vx, vy
|
2022-06-19 15:01:30 +02:00
|
|
|
|
|
|
|
def kill(self):
|
|
|
|
self.env.kill_entity(self)
|
|
|
|
|
|
|
|
|
|
|
|
class Agent(Entity):
|
|
|
|
def __init__(self, env):
|
|
|
|
super(Agent, self).__init__(env)
|
|
|
|
self.pos = (0.5, 0.5)
|
|
|
|
self.col = (0, 0, 255)
|
|
|
|
self.drag = self.env.agent_drag
|
|
|
|
self.controll_type = self.env.controll_type
|
2022-06-29 18:46:59 +02:00
|
|
|
self.solid = True
|
|
|
|
self.movable = True
|
2022-06-19 15:01:30 +02:00
|
|
|
|
|
|
|
def controll_step(self):
|
|
|
|
self._read_input()
|
|
|
|
self.env.check_collisions_for(self)
|
|
|
|
|
|
|
|
def _read_input(self):
|
|
|
|
if self.controll_type == 'SPEED':
|
|
|
|
self.speed = self.env.inp[0] - 0.5, self.env.inp[1] - 0.5
|
|
|
|
elif self.controll_type == 'ACC':
|
|
|
|
self.acc = self.env.inp[0] - 0.5, self.env.inp[1] - 0.5
|
|
|
|
else:
|
|
|
|
raise Exception('Unsupported controll_type')
|
|
|
|
|
|
|
|
|
|
|
|
class Enemy(Entity):
|
|
|
|
def __init__(self, env):
|
|
|
|
super(Enemy, self).__init__(env)
|
|
|
|
self.col = (255, 0, 0)
|
2022-06-19 15:48:51 +02:00
|
|
|
self.damage = 100
|
2022-06-29 18:49:28 +02:00
|
|
|
self.radiateDamage = True
|
2022-06-19 15:01:30 +02:00
|
|
|
|
2022-06-29 18:46:59 +02:00
|
|
|
def on_collision(self, other, depth):
|
|
|
|
super().on_collision(other, depth)
|
2022-06-19 15:01:30 +02:00
|
|
|
if isinstance(other, Agent):
|
|
|
|
self.env.new_reward -= self.damage
|
|
|
|
|
|
|
|
|
|
|
|
class Barrier(Enemy):
|
|
|
|
def __init__(self, env):
|
|
|
|
super(Barrier, self).__init__(env)
|
2022-06-29 18:46:59 +02:00
|
|
|
self.solid = True
|
|
|
|
self.movable = False
|
2022-06-19 15:01:30 +02:00
|
|
|
|
|
|
|
|
|
|
|
class CircleBarrier(Barrier):
|
|
|
|
def __init__(self, env):
|
|
|
|
super(CircleBarrier, self).__init__(env)
|
|
|
|
|
|
|
|
|
|
|
|
class Chaser(Enemy):
|
|
|
|
def __init__(self, env):
|
|
|
|
super(Chaser, self).__init__(env)
|
|
|
|
self.target = self.env.agent
|
|
|
|
self.arrow_fak = 100
|
|
|
|
self.lookahead = 0
|
|
|
|
|
|
|
|
def _get_arrow(self):
|
|
|
|
tx, ty = self.target.pos
|
|
|
|
x, y = self.pos
|
|
|
|
fx, fy = x + self.speed[0]*self.lookahead*self.env.speed_fac, y + \
|
|
|
|
self.speed[1]*self.lookahead*self.env.speed_fac
|
|
|
|
dx, dy = (tx-fx)*self.arrow_fak, (ty-fy)*self.arrow_fak
|
|
|
|
return self.env._limit_to_unit_circle((dx, dy))
|
|
|
|
|
|
|
|
|
|
|
|
class WalkingChaser(Chaser):
|
|
|
|
def __init__(self, env):
|
|
|
|
super(WalkingChaser, self).__init__(env)
|
|
|
|
self.col = (255, 0, 0)
|
|
|
|
self.chase_speed = 0.45
|
|
|
|
|
|
|
|
def controll_step(self):
|
|
|
|
arrow = self._get_arrow()
|
|
|
|
self.speed = arrow[0] * self.chase_speed, arrow[1] * self.chase_speed
|
|
|
|
|
|
|
|
|
|
|
|
class FlyingChaser(Chaser):
|
|
|
|
def __init__(self, env):
|
|
|
|
super(FlyingChaser, self).__init__(env)
|
|
|
|
self.col = (255, 0, 0)
|
|
|
|
self.chase_acc = 0.5
|
|
|
|
self.arrow_fak = 5
|
|
|
|
self.lookahead = 8 + env.random()*2
|
|
|
|
|
|
|
|
def controll_step(self):
|
|
|
|
arrow = self._get_arrow()
|
|
|
|
self.acc = arrow[0] * self.chase_acc, arrow[1] * self.chase_acc
|
|
|
|
|
|
|
|
|
2022-06-30 14:41:32 +02:00
|
|
|
class Collectable(Entity):
|
2022-06-19 15:01:30 +02:00
|
|
|
def __init__(self, env):
|
2022-06-30 14:41:32 +02:00
|
|
|
super(Collectable, self).__init__(env)
|
2022-06-19 15:01:30 +02:00
|
|
|
self.avaible = True
|
|
|
|
self.enforce_not_on_barrier = False
|
2022-06-29 12:42:08 +02:00
|
|
|
self.reward = 10
|
2022-06-30 14:41:32 +02:00
|
|
|
self.collectors = []
|
2022-06-19 15:01:30 +02:00
|
|
|
|
2022-06-29 18:46:59 +02:00
|
|
|
def on_collision(self, other, depth):
|
|
|
|
super().on_collision(other, depth)
|
2022-06-30 14:41:32 +02:00
|
|
|
if isinstance(other, Barrier):
|
2022-06-19 15:01:30 +02:00
|
|
|
self.on_barrier_collision()
|
2022-06-30 14:41:32 +02:00
|
|
|
else:
|
|
|
|
for Col in self.collectors:
|
|
|
|
if isinstance(other, Col):
|
|
|
|
other.on_collect(self)
|
|
|
|
self.on_collected()
|
2022-06-19 15:01:30 +02:00
|
|
|
|
2022-06-30 14:41:32 +02:00
|
|
|
def on_collected(self):
|
2022-06-19 15:01:30 +02:00
|
|
|
self.env.new_reward += self.reward
|
|
|
|
|
|
|
|
def on_barrier_collision(self):
|
|
|
|
if self.enforce_not_on_barrier:
|
|
|
|
self.pos = (self.env.random(), self.env.random())
|
|
|
|
self.env.check_collisions_for(self)
|
|
|
|
|
|
|
|
|
2022-06-30 14:41:32 +02:00
|
|
|
class Reward(Collectable):
|
|
|
|
def __init__(self, env):
|
|
|
|
super(Reward, self).__init__(env)
|
|
|
|
self.col = (0, 255, 0)
|
|
|
|
self.reward = 10
|
|
|
|
self.collectors = [Agent]
|
|
|
|
|
|
|
|
|
2022-06-19 15:01:30 +02:00
|
|
|
class OnceReward(Reward):
|
|
|
|
def __init__(self, env):
|
|
|
|
super(OnceReward, self).__init__(env)
|
2022-06-29 12:42:08 +02:00
|
|
|
self.reward = 500
|
2022-06-19 15:01:30 +02:00
|
|
|
|
2022-06-30 14:41:32 +02:00
|
|
|
def on_collected(self):
|
2022-06-19 15:01:30 +02:00
|
|
|
self.env.new_abs_reward += self.reward
|
|
|
|
self.kill()
|
|
|
|
|
|
|
|
|
|
|
|
class TeleportingReward(OnceReward):
|
|
|
|
def __init__(self, env):
|
|
|
|
super(TeleportingReward, self).__init__(env)
|
|
|
|
self.enforce_not_on_barrier = True
|
|
|
|
self.env.check_collisions_for(self)
|
|
|
|
|
2022-06-30 14:41:32 +02:00
|
|
|
def on_collected(self):
|
2022-06-19 15:01:30 +02:00
|
|
|
self.env.new_abs_reward += self.reward
|
|
|
|
self.pos = (self.env.random(), self.env.random())
|
|
|
|
self.env.check_collisions_for(self)
|
|
|
|
|
|
|
|
|
|
|
|
class TimeoutReward(OnceReward):
|
|
|
|
def __init__(self, env):
|
|
|
|
super(TimeoutReward, self).__init__(env)
|
|
|
|
self.enforce_not_on_barrier = True
|
|
|
|
self.env.check_collisions_for(self)
|
|
|
|
self.timeout = 10
|
|
|
|
|
|
|
|
def set_avaible(self, value):
|
|
|
|
self.avaible = value
|
|
|
|
if self.avaible:
|
|
|
|
self.col = (0, 255, 0)
|
|
|
|
else:
|
|
|
|
self.col = (50, 100, 50)
|
|
|
|
|
2022-06-30 14:41:32 +02:00
|
|
|
def on_collected(self):
|
2022-06-19 15:01:30 +02:00
|
|
|
if self.avaible:
|
|
|
|
self.env.new_abs_reward += self.reward
|
|
|
|
self.set_avaible(False)
|
|
|
|
self.env.timers.append((self.timeout, self.set_avaible, True))
|
2022-06-19 23:14:39 +02:00
|
|
|
|
|
|
|
|
2022-06-30 14:41:32 +02:00
|
|
|
class Ball(Entity):
|
|
|
|
def __init__(self, env):
|
|
|
|
super(Ball, self).__init__(env)
|
|
|
|
self.col = (255, 128, 0)
|
|
|
|
self.drag = 0.0025
|
|
|
|
self.solid = True
|
|
|
|
self.movable = 10
|
|
|
|
self.elasticity = 1
|
|
|
|
self.collision_changes_speed = True
|
|
|
|
self.wall_reflect_damping = 0.1
|
|
|
|
|
|
|
|
def calc_void_collision(self, dir, x, y, vx, vy):
|
|
|
|
if dir < 2:
|
|
|
|
x = min(max(x, 0), 1)
|
|
|
|
vx = -(vx/(1+self.wall_reflect_damping))
|
|
|
|
else:
|
|
|
|
y = min(max(y, 0), 1)
|
|
|
|
vy = -(vy/(1+self.wall_reflect_damping))
|
|
|
|
return x, y, vx, vy
|
|
|
|
|
|
|
|
def physics_step(self):
|
|
|
|
self.env.check_collisions_for(self)
|
|
|
|
super().physics_step()
|
|
|
|
|
|
|
|
|
|
|
|
class Goal(Collectable):
|
|
|
|
def __init__(self, env):
|
|
|
|
super(Goal, self).__init__(env)
|
|
|
|
self.col = (0, 200, 0)
|
|
|
|
self.reward = 500
|
|
|
|
self.radius = 20
|
|
|
|
self.collectors = [Ball]
|
|
|
|
|
|
|
|
|
|
|
|
class TeleportingGoal(Goal):
|
|
|
|
def __init__(self, env):
|
|
|
|
super(TeleportingGoal, self).__init__(env)
|
|
|
|
self.enforce_not_on_barrier = True
|
|
|
|
self.env.check_collisions_for(self)
|
|
|
|
|
|
|
|
def on_collected(self):
|
|
|
|
self.env.new_abs_reward += self.reward
|
|
|
|
self.pos = (self.env.random(), self.env.random())
|
|
|
|
self.env.check_collisions_for(self)
|
|
|
|
|
|
|
|
|
|
|
|
class FootballPlayer():
|
|
|
|
def __init__(self, env, target):
|
|
|
|
super(FootballPlayer, self).__init__(env)
|
|
|
|
self.col = (200, 0, 100)
|
|
|
|
self.target = target
|
|
|
|
self.solid = True
|
|
|
|
self.movable = 1
|
|
|
|
self.elasticity = 1
|
|
|
|
|
|
|
|
|
|
|
|
class WalkingFootballPlayer(FootballPlayer, WalkingChaser):
|
|
|
|
def __init__(self, env, target):
|
|
|
|
super(WalkingFootballPlayer, self).__init__(env, target)
|
|
|
|
self.target = target
|
|
|
|
|
|
|
|
|
|
|
|
class FlyingFootballPlayer(FootballPlayer, FlyingChaser):
|
|
|
|
def __init__(self, env, target):
|
|
|
|
super(FlyingFootballPlayer, self).__init__(env, target)
|
|
|
|
|
2022-06-21 21:39:19 +02:00
|
|
|
# Not a real entity. Is used in the config of RayObserver to reference the outer boundary of the environment.
|
2022-06-30 14:41:32 +02:00
|
|
|
|
|
|
|
|
2022-06-19 23:14:39 +02:00
|
|
|
class Void():
|
|
|
|
def __init__(self, env):
|
|
|
|
self.col = (50, 50, 50)
|
|
|
|
pass
|