refactoring
authorBen Doumenc <bdoumenc@gmail.com>
Sat, 31 May 2014 20:40:28 +0000 (22:40 +0200)
committerBen Doumenc <bdoumenc@gmail.com>
Sat, 31 May 2014 20:40:28 +0000 (22:40 +0200)
evolution/entities.py
evolution/systems.py
evolution/utils.py

index 5ef7d95..85201d9 100644 (file)
@@ -48,9 +48,13 @@ class Scene:
         return [e for e in self.getByType(type) if (e.pos[0] == entity.pos[0] and e.pos[1] == entity.pos[1])]
 
     def getAround(self, entity, distance, type):
-        def isAround(e):
-            return (e.pos - entity.pos).magnitude() < distance
-        return [e for e in self.getByType(type) if isAround(e)]
+        result = []
+        for e in self.getByType(type):
+            d = (e.pos - entity.pos).magnitude()
+            if d < distance:
+                result.append((e, d))
+        sorted(result, key=lambda t: t[1])
+        return result
 
 
 _entityCounter = 0
@@ -112,7 +116,8 @@ class Bot(Physical):
     RENDER = Scene.Render.Bot
     def __init__(self, pos):
         super(Bot, self).__init__(pos, True)
-        self.direction = Vector(0, 0)
+        self.forces = Vector(0, 0)
+        self.velocity = Vector(0, 0)
         self.genes = []
         self.stats = {"life": 100, "speed": 1}
         self.behavior = None
index 45186ad..e9e6f4c 100644 (file)
@@ -55,78 +55,71 @@ class Movement:
 
         entities = scene.getByType(Scene.Types.ALIVE)
         for e in entities:
-            if isValid(e, e.direction):
-                speed = e.stats.get("speed", 1)
-                e.move(e.direction * speed)
+            speed = e.stats.get("speed", 1)
+            e.velocity = (e.velocity + e.forces).truncate(speed)
+            if isValid(e, e.velocity):
+                e.move(e.velocity)
+            e.forces = Vector(0, 0)
 
     def close(self):
         pass
 
 class Behavior:
-    WANDER = "Wander"
-    SEEK   = "Seek"
-
     def __init__(self): 
-        self.behaviors = {
-            Behavior.WANDER: self.wander,
-            Behavior.SEEK: self.seek,
-        }
-
-    def setState(self, e, state, **kw):
-        e.scene.logger.debug("setState: [%s] %s -> %s (%s)", e, e.behavior, state, kw)
-        e.behaviorData = kw
-        e.behavior = state
+        pass
 
     def update(self, scene):
         entities = scene.getByType(Scene.Types.ALIVE)
+        actions = [self.checkForFood, self.lookForFood, self.wander]
         for entity in entities:
-            if entity.behavior == None:
-                self.setState(entity, Behavior.WANDER)
+            for action in actions:
+                if action(scene, entity): break
+
             entity.stats["life"] = entity.stats["life"] - 1
             if entity.stats["life"] == 0:
                 scene.logger.info("[%s] died by exhaustion. Was %s", entity, entity.getGenes())
                 entity.removeFromScene()
                 return
-            self.behaviors.get(entity.behavior)(scene, entity)
 
-    def seek(self, scene, entity):
-        # Check for near food
-        entities = scene.getAround(entity, 1, Scene.Types.GOODS)
-        for e in entities:
+    def checkForFood(self, scene, entity):
+        # Check if we can eat food
+        entities = scene.getAround(entity, 4, Scene.Types.GOODS)
+        for e, d in entities:
             if e.type == Scene.GoodTypes.FOOD:
                 e.use(entity)
-                self.setState(entity, Behavior.WANDER)
-                return
-
-        if (entity.behaviorData.has_key("target")):
-            # And move
-            entity.direction = (entity.behaviorData["target"].pos - entity.pos).normalize()
-        else:
-            self.setState(entity, Behavior.WANDER)
+                entity.behaviorData["target"] = None
+                return True
+        return False
+
+    def lookForFood(self, scene, entity):
+        target = entity.behaviorData.get("target", None)
+        if target is None:
+            # Check for surrounding food
+            perceptionDistance = entity.stats.get("perception", 1) * 4
+            entities = scene.getAround(entity, perceptionDistance, Scene.Types.GOODS)
+            for e, d in entities:
+                if e.type == Scene.GoodTypes.FOOD:
+                    scene.logger.info("lookForFood: [%s] Found target %s", entity, e)
+                    target = e
+                    break
+        if target:
+            # And apply behavior
+            entity.forces += (target.pos - entity.pos).normalize()
+            entity.behaviorData["target"] = target
+            return True
+        return False
 
     def wander(self, scene, entity):
-        def reset():
+        target = entity.behaviorData.get("wander", None)
+        count = entity.behaviorData.get("count", 10)
+        count -= 1
+        entity.behaviorData["count"] = count
+        if target is None or count == 0:
+            target = Vector(random.randint(0, scene.w), random.randint(0, scene.h))
+            entity.forces += (target - entity.pos).normalize()
+            entity.behaviorData["wander"] = target
             entity.behaviorData["count"] = 10
-            randomTarget = Vector(random.randint(0, scene.w), random.randint(0, scene.h))
-            entity.direction = (randomTarget - entity.pos).normalize()
-
-        if not entity.behaviorData.has_key("count"): reset()
-        entity.behaviorData["count"] = entity.behaviorData["count"] - 1
-
-        target = None
-        # Check for surrounding food
-        perceptionDistance = entity.stats.get("perception", 1) * 4
-        entities = scene.getAround(entity, perceptionDistance, Scene.Types.GOODS)
-        for e in entities:
-            if e.type == Scene.GoodTypes.FOOD:
-                scene.logger.info("Wander: [%s] Found target %s", entity, e)
-                target = e
-                break
-        if target:
-            self.setState(entity, Behavior.SEEK, target=target)
-        elif entity.behaviorData["count"] == 0:
-            reset()
-        
+        return True
 
     def close(self):
         pass
index 656f424..8a7bf6a 100644 (file)
@@ -30,7 +30,11 @@ class Vector:
     def normalize(self):
         magnitude = self.magnitude()
         if magnitude:
-            return Vector(*map(lambda x: x/magnitude, self))
-        return Vector(*map(lambda x: 0, self))
+            self.data = map(lambda x: x/magnitude, self)
+        return self
+
+    def truncate(self, upper):
+        self.data = map(lambda x: min(x, upper), self)
+        return self