1 """
2 Springbot evolution extension.
3 Implements genetic operations like mutation and crossover
4 """
5
6 from springbot import Springbot
7 from gear import Node, Spring
8 from random import choice, uniform, randint
9 from latimname import latimname
10 from math import sqrt, pi
11 from vector import Vector
12 from copy import copy
13
14
15 _bloodline_count = 0
16
17
18 BLOODLINE_SEP = ':'
19
21 """
22 Extends springbot to evolution methods
23 """
24
25 - def __init__(self, parent=None, name="Unnamed", startangle=0, random=False):
40
42 """
43 Return generations number
44 """
45 return len(self['bloodline'].split(BLOODLINE_SEP)) - 1
46
53
55 """
56 Crosses this springbot with another one, returning a new one
57 """
58 global _bloodline_count
59
60
61 breed = EvolveSpringbot()
62
63
64 for key in self:
65 breed[key] = self[key]
66
67 breed['fitness'] = 0
68 breed['name'] = ' '.join(set(self['name'].split() + other['name'].split()))
69 breed['adapted'] = "random"
70
71
72 try:
73 breed['bloodline'] += 'X' + \
74 other['bloodline'].split(BLOODLINE_SEP)[0] + '...' + other['bloodline'].split(BLOODLINE_SEP)[-2] + \
75 BLOODLINE_SEP
76 except KeyError:
77 pass
78
79
80 for springA, springB in zip(self.springs + ([None] * ((len(other.springs)-len(self.springs))/2)),
81 other.springs + ([None] * ((len(self.springs)-len(other.springs))/2))):
82
83 if springA is None: springA = springB
84 elif springB is None: springB = springA
85
86
87 spring = choice([springA,springB])
88
89
90 nodeA = breed.getNode(spring.a.id)
91 if nodeA is None:
92 nodeA = choice([self,other]).getNode(spring.a.id)
93 if nodeA is None: nodeA = spring.a
94
95
96 new_nodeA = Node(nodeA.pos, nodeA.vel, nodeA.acc)
97 new_nodeA.id = nodeA.id
98 nodeA = new_nodeA
99
100
101 breed.add(nodeA)
102
103
104 nodeB = breed.getNode(spring.b.id)
105 if nodeB is None:
106 nodeB = choice([self,other]).getNode(spring.b.id)
107 if nodeB is None: nodeB = spring.b
108
109
110 new_nodeB = Node(nodeB.pos, nodeB.vel, nodeB.acc)
111 new_nodeB.id = nodeB.id
112 nodeB = new_nodeB
113
114
115 breed.add(nodeB)
116
117
118 breed.add(Spring(nodeA, nodeB, spring.amplitude,
119 spring.offset, spring.normal))
120
121
122 breed.removeUnconnected()
123
124
125 return breed
126
127 - def mutate(self, newnodedist=100, nodevariation=10):
128 """
129 Mutates a random structure of the springbot, which may be
130 adding or removing a node, adding or removing a spring,
131 changing any spring's parameter or changing a node position.
132 """
133
134
135
136
137
138
139
140
141 if len(self.nodes) == 0:
142 return
143
144
145 tipo = choice(range(6))
146
147 if tipo == 0:
148 neigh = choice(self.nodes)
149 newnode = Node(pos=(neigh.pos.x + uniform(-newnodedist, newnodedist),
150 neigh.pos.y + uniform(-newnodedist, newnodedist)))
151
152 self.add(newnode)
153 self.add(Spring(neigh, newnode, offset=uniform(0,pi*2)))
154
155 elif tipo == 1 and len(self.nodes) > 1:
156
157
158 copia = Springbot()
159 copia.nodes = copy(self.nodes)
160 copia.springs = copy(self.springs)
161
162
163 toremove = choice(copia.nodes)
164
165
166 copia.remove(toremove)
167 if not copia.unconnected():
168
169 for node in self.nodes:
170 if node.id == toremove.id:
171 self.remove(node)
172 break
173
174 elif tipo == 2 and len(self.nodes) > 1:
175 a = choice(self.nodes)
176 b = choice(self.nodes)
177
178
179 while a is b:
180 b = choice(self.nodes)
181
182 self.add(Spring(a, b, offset=uniform(0,pi*2)))
183 elif tipo == 3 and len(self.springs) > 0:
184
185
186 toremove = choice(self.springs)
187
188
189 self.remove(toremove)
190 if self.unconnected():
191
192 self.add(toremove)
193
194 elif tipo == 4 and len(self.springs) > 0:
195 spring = choice(self.springs)
196 if choice([1, 2]) == 1:
197 spring.offset = max(spring.offset + uniform(-pi, pi), 0)
198 else:
199 added = uniform(-0.5, 0.5)
200 spring.amplitude = spring.amplitude + added if abs(spring.amplitude + added) <= 0.5 else spring.amplitude
201
202 elif tipo == 5:
203 node = choice(self.nodes)
204 node.pos.x += uniform(-nodevariation,nodevariation)
205 node.pos.y += uniform(-nodevariation,nodevariation)
206
207
208 for spring in self.springs:
209 if spring.a is node or spring.b is node:
210 spring.normal = sqrt(sum((spring.a.pos-spring.b.pos)**2))
211
212
213 return self
214
215
216
217
218
220 """
221 Creates a new springbot totally random
222 """
223
224 springbot = EvolveSpringbot(name=latimname(5))
225 springbot["adapted"] = "random"
226
227 for x in xrange(randint(nodes_num/2, nodes_num)):
228
229 newnode = Node(pos=(uniform(-noderadius, noderadius),
230 uniform(-noderadius, noderadius)))
231 springbot.add(newnode)
232
233 if len(springbot.nodes) > 1:
234 for x in xrange(randint(springs_num/2, springs_num)):
235
236 a = choice(springbot.nodes)
237 b = choice(springbot.nodes)
238
239
240 while a is b:
241 b = choice(springbot.nodes)
242
243
244 springbot.add(Spring(a, b, offset=uniform(0,pi*2), amplitude=uniform(-0.5, 0.5)))
245
246
247 springbot.removeUnconnected()
248
249 return springbot
250
251
252
253
254