Use vector
authorBen Doumenc <bdoumenc@gmail.com>
Sat, 31 May 2014 11:36:53 +0000 (13:36 +0200)
committerBen Doumenc <bdoumenc@gmail.com>
Sat, 31 May 2014 11:36:53 +0000 (13:36 +0200)
evolution/entities.py
evolution/main.py
evolution/systems.py
evolution/utils.py

index fdd284a..9b2d8b6 100644 (file)
@@ -1,19 +1,10 @@
 
 import copy
 import random
-import math
 
-from utils import SceneLogger
+from utils import SceneLogger, Vector
 
 class Scene:
-    class Directions:
-        NONE = -1
-        UP = 0
-        DOWN = 1
-        LEFT = 2
-        RIGHT = 3
-        ALL = [NONE, UP, DOWN, LEFT, RIGHT]
-
     class Types:
         DRAWABLE = "Drawable"
         PHYSICAL = "Physical"
@@ -47,27 +38,12 @@ class Scene:
     def getByType(self, type):
         return self.byType.get(type, [])
 
-    def getDirection(self, entity, target):
-        dx = target.x - entity.x
-        dy = target.y - entity.y
-        if abs(dx) >= abs(dy):
-            if dx < 0:
-                return Scene.Directions.LEFT
-            if dx >= 0:
-                return Scene.Directions.RIGHT
-        else:
-            if dy < 0:
-                return Scene.Directions.UP
-            if dy >= 0:
-                return Scene.Directions.DOWN
-        return Scene.Directions.NONE
-
     def getAt(self, entity, type):
-        return [e for e in self.getByType(type) if (e.x == entity.x and e.y == entity.y)]
+        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 math.sqrt((e.x - entity.x)**2 + (e.y - entity.y)**2) < distance
+            return (e.pos - entity.pos).magnitude() < distance
         return [e for e in self.getByType(type) if isAround(e)]
 
 
@@ -86,11 +62,10 @@ class Entity(object):
 
 class Physical(Entity):
     TYPES = Entity.TYPES + [Scene.Types.DRAWABLE, Scene.Types.PHYSICAL]
-    def __init__(self, x, y, walkable=True):
+    def __init__(self, pos, walkable=True):
         super(Physical, self).__init__()
         self.scene = None
-        self.x = x
-        self.y = y
+        self.pos = pos
         self.walkable = walkable
 
     def addToScene(self, scene):
@@ -103,21 +78,19 @@ class Physical(Entity):
         self.scene = None
         return self
 
-    def move(self, dx=0, dy=0):
-        self.x += dx
-        self.y += dy
+    def move(self, dv):
+        self.pos += dv
         return self
 
-    def at(self, x=0, y=0):
-        self.x = x
-        self.y = y
+    def at(self, newPos):
+        self.pos = newPos
         return self
 
 
 class Good(Physical):
     TYPES = Physical.TYPES + [Scene.Types.GOODS]
-    def __init__(self, x, y):
-        super(Good, self).__init__(x, y, False)
+    def __init__(self, pos):
+        super(Good, self).__init__(pos, False)
         self.type = Scene.GoodTypes.FOOD
         self.value = 5
 
@@ -131,9 +104,9 @@ class Good(Physical):
 
 class Bot(Physical):
     TYPES = Physical.TYPES + [Scene.Types.ALIVE]
-    def __init__(self, x, y):
-        super(Bot, self).__init__(x, y, True)
-        self.direction = random.choice(Scene.Directions.ALL)
+    def __init__(self, pos):
+        super(Bot, self).__init__(pos, True)
+        self.direction = Vector(0, 0)
         self.genes = []
         self.stats = {"life": 10, "speed": 1}
         self.stamina = 10
index 5d87b41..11c06ed 100644 (file)
@@ -6,7 +6,7 @@ import logging
 
 from entities import *
 from systems import CursesRenderer, Movement, Behavior
-from utils import Clock
+from utils import Clock, Vector
 
 def generateRandomGenes(entity):
     for g in random.sample(GENES.values(), 3):
@@ -26,11 +26,11 @@ if __name__ == "__main__":
     population = 4
     food = 15
     for _ in xrange(0, population):
-        b = Bot(random.randint(0, size[0] - 1), random.randint(0, size[1] - 1)).addToScene(scene)
+        b = Bot(Vector(random.randint(0, size[0] - 1), random.randint(0, size[1] - 1))).addToScene(scene)
         generateRandomGenes(b)
 
     for _ in xrange(0, food):
-        o = Good(random.randint(0, size[0] - 1), random.randint(0, size[1] - 1)).addToScene(scene)
+        o = Good(Vector(random.randint(0, size[0] - 1), random.randint(0, size[1] - 1))).addToScene(scene)
 
     renderer.update(scene)
     while True:
index 5f3a293..7983772 100644 (file)
@@ -2,6 +2,7 @@
 import random
 
 from entities import Scene
+from utils import Vector
 
 class CursesRenderer:
     def __init__(self):
@@ -18,7 +19,7 @@ class CursesRenderer:
         toRender = scene.getByType(Scene.Types.DRAWABLE)
         for e in toRender:
             try:
-                self.screen.addstr(e.y, e.x, e.getGlyph())
+                self.screen.addstr(e.pos[1], e.pos[0], e.getGlyph())
             except self.curses.error:
                 pass
 
@@ -32,28 +33,17 @@ class Movement:
         self.h = h
 
     def update(self, scene):
-        def isValid(e, dx, dy):
-            newX = e.x + dx
-            newY = e.y + dy
-            return (newX < self.w and newX >= 0 and
-                    newY < self.h and newY >= 0)
+        def isValid(e, dv):
+            newPos = e.pos + dv
+            return (newPos[0] < self.w and newPos[0] >= 0 and
+                    newPos[1] < self.h and newPos[1] >= 0)
 
         entities = scene.getByType(Scene.Types.ALIVE)
         for e in entities:
-            dx = 0; dy = 0
-            if e.direction == Scene.Directions.UP:
-                dx = 0; dy = -1
-            elif e.direction == Scene.Directions.DOWN:
-                dx = 0; dy = 1
-            elif e.direction == Scene.Directions.LEFT:
-                dx = -1; dy = 0
-            elif e.direction == Scene.Directions.RIGHT:
-                dx = 1; dy = 0
-            if dx != 0 or dy != 0:
-                if isValid(e, dx, dy):
-                    # speed = e.stats.get("speed", 1)
-                    speed = 1
-                    e.move(dx * speed, dy * speed)
+            if isValid(e, e.direction):
+                # speed = e.stats.get("speed", 1)
+                speed = 1
+                e.move(e.direction)
 
     def close(self):
         pass
@@ -103,7 +93,7 @@ class Behavior:
 
         if (entity.behaviorData.has_key("target")):
             # And move
-            entity.direction = scene.getDirection(entity, entity.behaviorData["target"])
+            entity.direction = Vector(*map(lambda x:int(x), (entity.behaviorData["target"].pos - entity.pos).normalize()))
         else:
             self.setState(entity, Behavior.WANDER)
 
@@ -123,9 +113,7 @@ class Behavior:
     def wander(self, scene, e):
         def reset():
             e.behaviorData["count"] = 5
-            directions = Scene.Directions.ALL[:]
-            directions.remove(e.direction)
-            e.direction = random.choice(directions)
+            e.direction = Vector(random.randint(-1, 1), random.randint(-1, 1))
 
         if not e.behaviorData.has_key("count"): reset()
         e.behaviorData["count"] = e.behaviorData.get("count", 5) - 1
index cf3d91c..69c9076 100644 (file)
@@ -1,6 +1,7 @@
 
 
 import logging
+import math
 import time
 
 class Clock:
@@ -24,3 +25,30 @@ class SceneLogger(logging.LoggerAdapter):
     def process(self, msg, kwargs):
         return '[%8d] %s' % (self.scene.clock.getTick(), msg), kwargs
 
+
+class Vector:
+    def __init__(self, *data):
+        self.data = list(data)
+
+    def __repr__(self):
+        return repr(self.data)  
+
+    def __add__(self, other):
+        return Vector(*map(lambda x, y: x+y, self, other))
+
+    def __sub__(self, other):
+        return Vector(*map(lambda x, y: x-y, self, other))
+
+    def __getitem__(self, index):
+        return self.data[index]
+
+    def __len__(self):
+        return len(self.data)
+
+    def magnitude(self):
+        return math.sqrt(sum(map(lambda x:x**2, self.data)))
+
+    def normalize(self):
+        magnitude = self.magnitude()
+        return Vector(*map(lambda x: x/magnitude, self))
+