diff --git a/columbus/entities.py b/columbus/entities.py index 520ebd0..edf0ee5 100644 --- a/columbus/entities.py +++ b/columbus/entities.py @@ -4,14 +4,13 @@ import math class Entity(object): def __init__(self, env): + self.shape = None 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' self.solid = False self.movable = False # False = Non movable, True = Movable, x>1: lighter movable self.elasticity = 1 @@ -48,9 +47,8 @@ class Entity(object): self._crash_list = [] 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) + raise Exception( + '[!] draw not implemented for shape "'+str(self.shape)+'"') def on_collision(self, other, depth): if self.solid and other.solid: @@ -61,7 +59,8 @@ class Entity(object): if other in self._crash_list: return self._crash_list.append(other) - force_dir = self.pos[0] - other.pos[0], self.pos[1] - other.pos[1] + force_dir = self._get_crash_force_dir(other) + print(force_dir, depth) force_dir_len = math.sqrt(force_dir[0]**2+force_dir[1]**2) if force_dir_len == 0: return @@ -82,9 +81,19 @@ class Entity(object): self.env.speed_fac, self.pos[1] - self.env.inp[1] * \ self._coll_add_pushback*self.env.speed_fac if self.collision_changes_speed: + oldspeed = math.sqrt(self.speed[0]**2+self.speed[1]**2) self.speed = self.speed[0] + \ force_vec[0]*self.collision_elasticity/self.env.speed_fac, self.speed[1] + \ force_vec[1]*self.collision_elasticity/self.env.speed_fac + newspeed = math.sqrt(self.speed[0]**2+self.speed[1]**2) + if newspeed > oldspeed*1.1: + self.speed = self.speed[0]/newspeed*1.1 * \ + oldspeed, self.speed[1]/newspeed*oldspeed*1.1 + + def _get_crash_force_dir(self, other): + if 1 == 1: # linter hack + raise Exception( + '[!] No collision-logic implemented for shape"'+str(self.shape)+'"') def on_collect(self, other): pass @@ -105,7 +114,94 @@ class Entity(object): self.env.kill_entity(self) -class Agent(Entity): +class CircularEntity(Entity): + def __init__(self, env): + super().__init__(env) + self.shape = 'circle' + self.radius = 10 + + 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) + + def _get_crash_force_dir(self, other): + if other.shape == 'circle': + return self.pos[0] - other.pos[0], self.pos[1] - other.pos[1] + elif other.shape == 'rect': + pad = 0 + edge_size = min(self.radius, min( + other.width/3, other.height/3)) + 1 + + x, y = self.pos + x, y = x*self.env.height, y*self.env.width + left, top = x - self.radius + pad, y - self.radius + pad + right, bottom = x + self.radius - pad, y + self.radius - pad + lrcenter, tbcenter = x, y + + ox, oy = other.pos + ox, oy = ox*self.env.height, oy*self.env.width + oleft, otop = ox + pad, oy + pad + oright, obottom = ox + other.width - pad, oy + other.height - pad + olrcenter, otbcenter = ox + other.width/2, oy + other.height/2 + + lr, tb = 0, 0 + + if otop < bottom and obottom > bottom: + # col from top + tb = otop - bottom + #print('t', tb) + elif top < obottom and top > otop: + # col from bottom + tb = - top + obottom + #print('b', tb) + + if right > oleft and right < oright: + # col from left + lr = oleft - right + #print('l', lr) + elif left < oright and left > oleft: + # col from right + lr = - left + oright + #print('r', lr) + + if lr != 0 and tb != 0: + if abs(abs(tb) - abs(lr)) < edge_size: + if abs(tb) < abs(lr): + return lr/5, tb + else: + return lr, tb/5 + if abs(tb) < abs(lr): + return 0, tb + else: + return lr, 0 + + return 0, 0 + else: + raise Exception( + '[!] Shape "circle" does not know how to collide with shape "'+str(other.shape)+'"') + + +class RectangularEntity(Entity): + def __init__(self, env): + super().__init__(env) + self.shape = 'rect' + self.width = 10 + self.height = 10 + + def draw(self): + x, y = self.pos + rect = pygame.Rect(x*self.env.width, y * + self.env.width, self.width, self.height) + pygame.draw.rect(self.env.surface, self.col, + rect, width=0) + + def _get_crash_force_dir(self, other): + raise Exception( + '[!] Collisions in this direction not implemented for shape "rectangle"') + + +class Agent(CircularEntity): def __init__(self, env): super(Agent, self).__init__(env) self.pos = (0.5, 0.5) @@ -148,12 +244,17 @@ class Barrier(Enemy): self.movable = False -class CircleBarrier(Barrier): +class CircleBarrier(Barrier, CircularEntity): def __init__(self, env): super(CircleBarrier, self).__init__(env) -class Chaser(Enemy): +class RectBarrier(Barrier, RectangularEntity): + def __init__(self, env): + super().__init__(env) + + +class Chaser(Enemy, CircularEntity): def __init__(self, env): super(Chaser, self).__init__(env) self.target = self.env.agent @@ -193,7 +294,7 @@ class FlyingChaser(Chaser): self.acc = arrow[0] * self.chase_acc, arrow[1] * self.chase_acc -class Collectable(Entity): +class Collectable(CircularEntity): def __init__(self, env): super(Collectable, self).__init__(env) self.avaible = True @@ -271,7 +372,7 @@ class TimeoutReward(OnceReward): self.env.timers.append((self.timeout, self.set_avaible, True)) -class Ball(Entity): +class Ball(CircularEntity): def __init__(self, env): super(Ball, self).__init__(env) self.col = (255, 128, 0) diff --git a/columbus/env.py b/columbus/env.py index ccd5259..c70bbea 100644 --- a/columbus/env.py +++ b/columbus/env.py @@ -45,7 +45,7 @@ def parseObs(obsConf): class ColumbusEnv(gym.Env): metadata = {'render.modes': ['human']} - def __init__(self, observable=observables.Observable(), fps=60, env_seed=3.1, master_seed=None, start_pos=(0.5, 0.5), start_score=0, speed_fac=0.01, acc_fac=0.02, die_on_zero=False, return_on_score=-1, reward_mult=1, agent_drag=0, controll_type='SPEED', aux_reward_max=1, aux_penalty_max=0, aux_reward_discretize=0, void_is_type_barrier=True, void_damage=1, torus_topology=False, default_collision_elasticity=1): + def __init__(self, observable=observables.Observable(), fps=60, env_seed=3.1, master_seed=None, start_pos=(0.5, 0.5), start_score=0, speed_fac=0.01, acc_fac=0.04, die_on_zero=False, return_on_score=-1, reward_mult=1, agent_drag=0, controll_type='SPEED', aux_reward_max=1, aux_penalty_max=0, aux_reward_discretize=0, void_is_type_barrier=True, void_damage=1, torus_topology=False, default_collision_elasticity=1): super(ColumbusEnv, self).__init__() self.action_space = spaces.Box( low=-1, high=1, shape=(2,), dtype=np.float32) @@ -79,6 +79,7 @@ class ColumbusEnv(gym.Env): self.aux_reward_discretize = aux_reward_discretize # 0 = dont discretize; how many steps (along diagonal) self.aux_reward_discretize = 0 + self.penalty_from_edges = False self.draw_observable = True self.draw_joystick = True self.draw_entities = True @@ -169,8 +170,13 @@ class ColumbusEnv(gym.Env): aux_reward += reward elif isinstance(entity, entities.Enemy): if entity.radiateDamage: - penalty = self.aux_penalty_max / \ - (1 + self.sq_dist(entity.pos, self.agent.pos)) + if self.penalty_from_edges: + penalty = self.aux_penalty_max / \ + (1 + self.sq_dist(entity.pos, + self.agent.pos) - entity.radius - self.agent.redius) + else: + penalty = self.aux_penalty_max / \ + (1 + self.sq_dist(entity.pos, self.agent.pos)) if self.aux_reward_discretize: penalty = int(penalty*self.aux_reward_discretize*2) / \ @@ -220,12 +226,16 @@ class ColumbusEnv(gym.Env): other.on_collision(entity, depth) def _check_collision_between(self, e1, e2): + e = [e1, e2] + e.sort(key=lambda x: x.shape) + e1, e2 = e shapes = [e1.shape, e2.shape] - shapes.sort() if shapes == ['circle', 'circle']: dist = math.sqrt(((e1.pos[0]-e2.pos[0])*self.width) ** 2 + ((e1.pos[1]-e2.pos[1])*self.height)**2) return max(0, e1.radius + e2.radius - dist) + elif shapes == ['circle', 'rect']: + return sum([abs(d) for d in e1._get_crash_force_dir(e2)]) else: raise Exception( 'Checking for collision between unsupported shapes: '+str(shapes)) @@ -406,6 +416,29 @@ class ColumbusTest3_1(ColumbusEnv): self.entities.append(reward) +class ColumbusTestRect(ColumbusEnv): + def __init__(self, observable=observables.Observable(), fps=30, aux_reward_max=1, **kw): + super().__init__( + observable=observable, fps=fps, env_seed=3.3, aux_reward_max=aux_reward_max, controll_type='ACC', **kw) + self.start_pos = [0.5, 0.5] + self.score = 0 + + def setup(self): + self.agent.pos = self.start_pos + for i in range(1): + enemy = entities.RectBarrier(self) + enemy.width = self.random()*40+50 + enemy.height = self.random()*40+50 + self.entities.append(enemy) + for i in range(1): + enemy = entities.CircleBarrier(self) + enemy.radius = self.random()*40+50 + self.entities.append(enemy) + for i in range(1): + reward = entities.TeleportingReward(self) + self.entities.append(reward) + + class ColumbusTestRay(ColumbusTest3_1): def __init__(self, observable=observables.RayObservable(), hide_map=False, fps=30, **kw): super(ColumbusTestRay, self).__init__( @@ -421,6 +454,74 @@ class ColumbusRayDrone(ColumbusTestRay): self.agent_drag = 0.02 +class ColumbusDemoEnv3_1(ColumbusEnv): + def __init__(self, observable=observables.Observable(), fps=30, aux_reward_max=1, **kw): + super().__init__( + observable=observable, fps=fps, env_seed=3.1, aux_reward_max=aux_reward_max, controll_type='ACC', agent_drag=0.05, **kw) + self.start_pos = [0.6, 0.3] + self.score = 0 + + def setup(self): + self.agent.pos = self.start_pos + for i in range(18): + enemy = entities.CircleBarrier(self) + enemy.radius = self.random()*40+50 + self.entities.append(enemy) + for i in range(0): + enemy = entities.FlyingChaser(self) + enemy.chase_acc = self.random()*0.4*0.3 # *0.6+0.5 + self.entities.append(enemy) + for i in range(1): + reward = entities.TeleportingReward(self) + self.entities.append(reward) + + +class ColumbusDemoEnv2_7(ColumbusEnv): + def __init__(self, observable=observables.Observable(), fps=30, aux_reward_max=1, **kw): + super().__init__( + observable=observable, fps=fps, env_seed=2.7, aux_reward_max=aux_reward_max, controll_type='ACC', agent_drag=0.05, **kw) + self.start_pos = [0.6, 0.3] + self.score = 0 + + def setup(self): + self.agent.pos = self.start_pos + for i in range(12): + enemy = entities.CircleBarrier(self) + enemy.radius = self.random()*30+40 + self.entities.append(enemy) + for i in range(3): + enemy = entities.FlyingChaser(self) + enemy.chase_acc = self.random()*0.4*0.3 # *0.6+0.5 + self.entities.append(enemy) + for i in range(1): + reward = entities.TeleportingReward(self) + self.entities.append(reward) + + +class ColumbusDemoEnvFootball(ColumbusEnv): + def __init__(self, observable=observables.Observable(), fps=30, walkingOpponent=0, flyingOpponent=0, **kw): + super().__init__( + observable=observable, fps=fps, env_seed=1.23, **kw) + self.start_pos = [0.5, 0.5] + self.score = 0 + self.walkingOpponents = walkingOpponent + self.flyingOpponents = flyingOpponent + + def setup(self): + self.agent.pos = self.start_pos + for i in range(8): + enemy = entities.CircleBarrier(self) + enemy.radius = self.random()*40+50 + self.entities.append(enemy) + ball = entities.Ball(self) + self.entities.append(ball) + self.entities.append(entities.TeleportingGoal(self)) + for i in range(self.walkingOpponents): + self.entities.append(entities.WalkingFootballPlayer(self, ball)) + for i in range(self.flyingOpponents): + self.entities.append(entities.FlyingFootballPlayer(self, ball)) + + class ColumbusCandyland(ColumbusEnv): def __init__(self, observable=observables.RayObservable(chans=[entities.Reward, entities.Void], num_rays=16, include_rand=True), hide_map=False, fps=30, env_seed=None, **kw): super(ColumbusCandyland, self).__init__( @@ -664,7 +765,7 @@ class ColumbusConfigDefined(ColumbusEnv): class ColumbusBlub(ColumbusEnv): - def __init__(self, observable=observables.Observable(), env_seed=None, entities=[], fps=30, **kw): + def __init__(self, observable=observables.CompositionalObservable([observables.StateObservable(), observables.RayObservable(num_rays=6, chans=[entities.Enemy])]), env_seed=None, entities=[], fps=30, **kw): super().__init__( observable=observable, fps=fps, env_seed=env_seed, default_collision_elasticity=0.8, speed_fac=0.01, acc_fac=0.1, agent_drag=0.06, controll_type='ACC') @@ -682,11 +783,11 @@ class ColumbusBlub(ColumbusEnv): ### -register( - id='ColumbusBlub-v0', - entry_point=ColumbusBlub, - max_episode_steps=30*60*2, -) +# register( +# id='ColumbusBlub-v0', +# entry_point=ColumbusBlub, +# max_episode_steps=30*60*2, +# ) register( @@ -701,41 +802,41 @@ register( max_episode_steps=30*60*2, ) -register( - id='ColumbusRayDrone-v0', - entry_point=ColumbusRayDrone, - max_episode_steps=30*60*2, -) +# register( +# id='ColumbusRayDrone-v0', +# entry_point=ColumbusRayDrone, +# max_episode_steps=30*60*2, +# ) -register( - id='ColumbusCandyland-v0', - entry_point=ColumbusCandyland, - max_episode_steps=30*60*2, -) +# register( +# id='ColumbusCandyland-v0', +# entry_point=ColumbusCandyland, +# max_episode_steps=30*60*2, +# ) -register( - id='ColumbusCandyland_Aux10-v0', - entry_point=ColumbusCandyland_Aux10, - max_episode_steps=30*60*2, -) +# register( +# id='ColumbusCandyland_Aux10-v0', +# entry_point=ColumbusCandyland_Aux10, +# max_episode_steps=30*60*2, +# ) -register( - id='ColumbusEasyObstacles-v0', - entry_point=ColumbusEasyObstacles, - max_episode_steps=30*60*2, -) +# register( +# id='ColumbusEasyObstacles-v0', +# entry_point=ColumbusEasyObstacles, +# max_episode_steps=30*60*2, +# ) -register( - id='ColumbusEasierObstacles-v0', - entry_point=ColumbusEasyObstacles, - max_episode_steps=30*60*2, -) +# register( +# id='ColumbusEasierObstacles-v0', +# entry_point=ColumbusEasyObstacles, +# max_episode_steps=30*60*2, +# ) -register( - id='ColumbusJustState-v0', - entry_point=ColumbusJustState, - max_episode_steps=30*60*2, -) +# register( +# id='ColumbusJustState-v0', +# entry_point=ColumbusJustState, +# max_episode_steps=30*60*2, +# ) register( id='ColumbusStateWithBarriers-v0', @@ -743,38 +844,54 @@ register( max_episode_steps=30*60*2, ) -register( - id='ColumbusCompassWithBarriers-v0', - entry_point=ColumbusCompassWithBarriers, - max_episode_steps=30*60*2, -) +# register( +# id='ColumbusCompassWithBarriers-v0', +# entry_point=ColumbusCompassWithBarriers, +# max_episode_steps=30*60*2, +# ) -register( - id='ColumbusTrivialRay-v0', - entry_point=ColumbusTrivialRay, - max_episode_steps=30*60*2, -) +# register( +# id='ColumbusTrivialRay-v0', +# entry_point=ColumbusTrivialRay, +# max_episode_steps=30*60*2, +# ) -register( - id='ColumbusFootball-v0', - entry_point=ColumbusFootball, - max_episode_steps=30*60*2, -) +# register( +# id='ColumbusFootball-v0', +# entry_point=ColumbusFootball, +# max_episode_steps=30*60*2, +# ) -register( - id='ColumbusComb-v0', - entry_point=ColumbusComp, - max_episode_steps=30*60*2, -) +# register( +# id='ColumbusComb-v0', +# entry_point=ColumbusComp, +# max_episode_steps=30*60*2, +# ) -register( - id='ColumbusSingle-v0', - entry_point=ColumbusSingle, - max_episode_steps=30*60*2, -) +# register( +# id='ColumbusSingle-v0', +# entry_point=ColumbusSingle, +# max_episode_steps=30*60*2, +# ) register( id='ColumbusConfigDefined-v0', entry_point=ColumbusConfigDefined, max_episode_steps=30*60*2, ) + +register( + id='ColumbusDemoEnvFootball-v0', + entry_point=ColumbusDemoEnvFootball, + max_episode_steps=30*60*2, +) +register( + id='ColumbusDemoEnv3_1-v0', + entry_point=ColumbusDemoEnv3_1, + max_episode_steps=30*60*2, +) +register( + id='ColumbusDemoEnv2_7-v0', + entry_point=ColumbusDemoEnv2_7, + max_episode_steps=30*60*2, +) diff --git a/columbus/observables.py b/columbus/observables.py index 9d8348b..8224487 100644 --- a/columbus/observables.py +++ b/columbus/observables.py @@ -48,7 +48,7 @@ class CnnObservable(Observable): def get_observation(self): if not self.env._rendered: - self.env.render(dont_show=True) + self.env.render(mode='internal', dont_show=False) self.env._ensure_surface() x, y = self.env.agent.pos[0]*self.env.width - self.in_width / \ 2, self.env.agent.pos[1]*self.env.height - self.in_height/2