refactoring genes
[experiments.git] / evolution / systems.py
1
2 import pygame
3 import random
4
5 from entities import Scene
6 from utils import Vector
7
8 class PygameRenderer:
9 def __init__(self, width, height, name):
10 self.width = width
11 self.height = height
12 self.bg_color = pygame.Color("black")
13 self.screen = pygame.display.set_mode((width, height))
14 pygame.display.set_caption(name)
15 self.pygame = pygame
16
17 def toScreen(self, point):
18 return (int(point[0] * self.width / 100), int(point[1] * self.height / 100))
19
20 def toWorld(self, point):
21 return (float(point[0]) / self.width * 100, float(point[1]) / self.height * 100)
22
23
24 def update(self, scene):
25 self.screen.fill(self.bg_color)
26 toRender = scene.getByType(Scene.Types.DRAWABLE)
27 for e in toRender:
28 self.pygame.draw.circle(self.screen, self.pygame.Color(e.getRenderColor()), self.toScreen(e.pos), 4, 1)
29
30 self.pygame.display.flip()
31
32 def close(self):
33 pass
34
35
36 class Movement:
37 def __init__(self, w, h):
38 self.w = w
39 self.h = h
40
41 def update(self, scene):
42 def isValid(e, dv):
43 newPos = e.pos + dv
44 return (newPos[0] < self.w and newPos[0] >= 0 and
45 newPos[1] < self.h and newPos[1] >= 0)
46
47 entities = scene.getByType(Scene.Types.ALIVE)
48 for e in entities:
49 if isValid(e, e.direction):
50 speed = e.stats.get("speed", 1)
51 e.move(e.direction * speed)
52
53 def close(self):
54 pass
55
56 class Behavior:
57 EXHAUSTED = "Exhausted"
58 WANDER = "Wander"
59 SEEK = "Seek"
60
61 def __init__(self):
62 self.behaviors = {
63 Behavior.WANDER: self.wander,
64 Behavior.EXHAUSTED: self.exhausted,
65 Behavior.SEEK: self.seek,
66 }
67
68 def setState(self, e, state):
69 e.scene.logger.debug("setState: [%s] %s -> %s", e, e.behavior, state)
70 e.behaviorData = {}
71 e.behavior = state
72
73 def update(self, scene):
74 entities = scene.getByType(Scene.Types.ALIVE)
75 for e in entities:
76 if e.behavior == None:
77 self.setState(e, Behavior.WANDER)
78 self.behaviors.get(e.behavior)(scene, e)
79
80 def seek(self, scene, entity):
81 # Check for near food
82 entities = scene.getAround(entity, 1, Scene.Types.GOODS)
83 for e in entities:
84 if e.type == Scene.GoodTypes.FOOD:
85 e.use(entity)
86 self.setState(entity, Behavior.WANDER)
87 return
88 # Check for surrounding food
89 perceptionDistance = entity.stats.get("perception", 1) * 4
90 if not entity.behaviorData.get("target", None):
91 scene.logger.debug("Seek: [%s] Looking for food", entity)
92 entities = scene.getAround(entity, perceptionDistance, Scene.Types.GOODS)
93 for e in entities:
94 if e.type == Scene.GoodTypes.FOOD:
95 scene.logger.debug("Seek: [%s] Found target %s", entity, e)
96 entity.behaviorData["target"] = e
97 break
98
99 if (entity.behaviorData.has_key("target")):
100 # And move
101 entity.direction = (entity.behaviorData["target"].pos - entity.pos).normalize()
102 else:
103 self.setState(entity, Behavior.WANDER)
104
105 def exhausted(self, scene, entity):
106 entity.behaviorData["count"] = entity.behaviorData.get("count", 5) - 1
107 if entity.behaviorData["count"] == 0:
108 entity.stamina = 10
109 entity.stats["life"] = entity.stats["life"] - 1
110
111 if entity.stats["life"] == 0:
112 scene.logger.debug("Exhausted: [%s] died. Was %s", entity, entity.getGenes())
113 entity.removeFromScene()
114 return
115 if entity.stamina > 0:
116 self.setState(entity, Behavior.WANDER)
117
118 def wander(self, scene, e):
119 def reset():
120 e.behaviorData["count"] = 5
121 e.direction = Vector(random.randint(-1, 1), random.randint(-1, 1)).normalize()
122
123 if not e.behaviorData.has_key("count"): reset()
124 e.behaviorData["count"] = e.behaviorData.get("count", 5) - 1
125 e.stamina -= 1
126 if e.stamina == 0:
127 self.setState(e, Behavior.EXHAUSTED)
128 elif e.behaviorData["count"] == 0:
129 self.setState(e, Behavior.SEEK)
130
131
132 def close(self):
133 pass
134
135