forge/blade/entity/player.py (140 lines of code) (raw):

import numpy as np from forge.blade.systems import ai from forge.blade.lib.enums import Material, Neon from pdb import set_trace as T class Stat: def __init__(self, val, maxVal): self._val = val self._max = maxVal def increment(self, amt=1): self._val = min(self.max, self.val + amt) def decrement(self, amt=1): self._val = max(0, self.val - amt) def center(self): return (self.val - self.max/2.0) / self.max @property def val(self): return self._val @property def max(self): return self._max def packet(self): return {'val': self.val, 'max': self.max} class Player: public = set( 'pos lastPos R C food water health entID annID name colorInd color timeAlive kill attackMap damage freeze immune'.split()) def __init__(self, entID, color, config): self._config = config self._R, self._C = config.R, config.C self._pos = config.SPAWN() self._lastPos = self.pos self._food = Stat(config.FOOD, config.FOOD) self._water = Stat(config.WATER, config.WATER) self._health = Stat(config.HEALTH, config.HEALTH) self._entID = entID self._name = 'Neural_' + str(self._entID) self._timeAlive = 0 self._damage = None self._freeze = 0 self._immune = True self._kill = False self._annID, self._color = color self._colorInd = self._annID self._attackMap = np.zeros((7, 7, 3)).tolist() self._index = 1 self._immuneTicks = 15 self.move = None self.attack = None def __getattribute__(self, name): if name in Player.public: return getattr(self, '_' + name) return super().__getattribute__(name) def __setattr__(self, name, value): if name in Player.public: raise AttributeError('Property \"' + name + '\" is read only: agents cannot modify their server-side data') return super().__setattr__(name, value) def packet(self): data = {} for key in Player.public: val = getattr(self, key) data[key] = val if hasattr(val, 'packet'): data[key] = val.packet() if self.attack is not None: data['attack'] = { 'style': self.attack.action.__name__, 'target': self.attack.args.entID} return data #PCs interact with the world only through stimuli #to prevent cheating def decide(self, packets): action, args = self.cpu.decide(self, packets) return action, args def forage(self, world): r, c = self._pos isForest = type(world.env.tiles[r, c].mat) in [Material.FOREST.value] if isForest and world.env.harvest(r, c): self.food.increment(5) isWater = Material.WATER.value in ai.adjacentMats(world.env, self._pos) if isWater: self.water.increment(5) def lavaKill(self, world): r, c = self._pos if type(world.env.tiles[r, c].mat) == Material.LAVA.value: self._kill = True return self._kill def updateStats(self): if (self._food.val > self._food.max//2 and self._water.val > self._water.max//2): self._health.increment() self._water.decrement() self._food.decrement() if self._food.val <= 0: self._health.decrement() if self._water.val <= 0: self._health.decrement() def updateCounts(self, world): r, c = self._pos world.env.tiles[r, c].counts[self._colorInd] += 1 def mapAttack(self): if self.attack is not None: attack = self.attack name = attack.action.__name__ if name == 'Melee': attackInd = 0 elif name == 'Range': attackInd = 1 elif name == 'Mage': attackInd = 2 rt, ct = attack.args.pos rs, cs = self._pos dr = rt - rs dc = ct - cs if abs(dr)<=3 and abs(dc)<=3: self._attackMap[3+dr][3+dc][attackInd] += 1 def step(self, world): if not self.alive: return self._freeze = max(0, self._freeze-1) self.updateCounts(world) if self.lavaKill(world): return self.forage(world) self.updateStats() self._damage = None self._timeAlive += 1 self.updateImmune() def act(self, world, actions, arguments, val): if not self.alive: return self.mapAttack() self.val = val self._lastPos = self._pos for action, args in zip(actions, arguments): action.call(world, self, *args) @property def alive(self): return self._health.val > 0 def updateImmune(self): if self._timeAlive >= self._immuneTicks: self._immune = False #Note: does not stack damage, but still applies to health def applyDamage(self, damage): if self.immune: return self._damage = damage self._health.decrement(damage)