forge/blade/core/realm.py (144 lines of code) (raw):

import ray import pickle import numpy as np from pdb import set_trace as T import numpy as np from forge import trinity as Trinity from forge.blade import entity, core from itertools import chain from copy import deepcopy class ActionArgs: def __init__(self, action, args): self.action = action self.args = args class Realm: def __init__(self, config, args, idx): #Random samples if config.SAMPLE: config = deepcopy(config) nent = np.random.randint(0, config.NENT) config.NENT = config.NPOP * (1 + nent // config.NPOP) self.world, self.desciples = core.Env(config, idx), {} self.config, self.args, self.tick = config, args, 0 self.npop = config.NPOP self.env = self.world.env self.values = None def clientData(self): if self.values is None and hasattr(self, 'sword'): self.values = self.sword.anns[0].visVals() ret = { 'environment': self.world.env, 'entities': dict((k, v.packet()) for k, v in self.desciples.items()), 'values': self.values } return pickle.dumps(ret) def spawn(self): if len(self.desciples) >= self.config.NENT: return entID, color = self.god.spawn() ent = entity.Player(entID, color, self.config) self.desciples[ent.entID] = ent r, c = ent.pos self.world.env.tiles[r, c].addEnt(entID, ent) self.world.env.tiles[r, c].counts[ent.colorInd] += 1 def cullDead(self, dead): for entID in dead: ent = self.desciples[entID] r, c = ent.pos self.world.env.tiles[r, c].delEnt(entID) self.god.cull(ent.annID) del self.desciples[entID] def stepWorld(self): ents = list(chain(self.desciples.values())) self.world.step(ents, []) def stepEnv(self): self.world.env.step() self.env = self.world.env.np() def stepEnt(self, ent, action, arguments): move, attack = action moveArgs, attackArgs = arguments ent.move = ActionArgs(move, moveArgs) ent.attack = ActionArgs(attack, attackArgs[0]) def getStim(self, ent): return self.world.env.stim(ent.pos, self.config.STIM) @ray.remote class NativeRealm(Realm): def __init__(self, trinity, config, args, idx): super().__init__(config, args, idx) self.god = trinity.god(config, args) self.sword = trinity.sword(config, args) self.sword.anns[0].world = self.world def stepEnts(self): dead = [] for ent in self.desciples.values(): ent.step(self.world) if self.postmortem(ent, dead): continue stim = self.getStim(ent) action, arguments, val = self.sword.decide(ent, stim) ent.act(self.world, action, arguments, val) self.stepEnt(ent, action, arguments) self.cullDead(dead) def postmortem(self, ent, dead): entID = ent.entID if not ent.alive or ent.kill: dead.append(entID) if not self.config.TEST: self.sword.collectRollout(entID, ent) return True return False def step(self): self.spawn() self.stepEnv() self.stepEnts() self.stepWorld() def run(self, swordUpdate=None): self.recvSwordUpdate(swordUpdate) updates = None while updates is None: self.step() updates, logs = self.sword.sendUpdate() return updates, logs def recvSwordUpdate(self, update): if update is None: return self.sword.recvUpdate(update) def recvGodUpdate(self, update): self.god.recv(update) @ray.remote class VecEnvRealm(Realm): #Use the default God behind the scenes for spawning def __init__(self, config, args, idx): super().__init__(config, args, idx) self.god = Trinity.God(config, args) def stepEnts(self, decisions): dead = [] for tup in decisions: entID, action, arguments, val = tup ent = self.desciples[entID] ent.step(self.world) if self.postmortem(ent, dead): continue ent.act(self.world, action, arguments, val) self.stepEnt(ent, action, arguments) self.cullDead(dead) def postmortem(self, ent, dead): entID = ent.entID if not ent.alive or ent.kill: dead.append(entID) return True return False def step(self, decisions): decisions = pickle.loads(decisions) self.stepEnts(decisions) self.stepWorld() self.spawn() self.stepEnv() stims, rews, dones = [], [], [] for entID, ent in self.desciples.items(): stim = self.getStim(ent) stims.append((ent, self.getStim(ent))) rews.append(1) return pickle.dumps((stims, rews, None, None)) def reset(self): self.spawn() self.stepEnv() return [(e, self.getStim(e)) for e in self.desciples.values()]