1 #include "race.h"
2
WorldRace()3 WorldRace::WorldRace()
4 {
5 }
6
init()7 void WorldRace::init()
8 {
9 testcounter = 1;
10
11 cerr << endl << "Initializing run " << testcounter << " ... " << endl;
12
13 // insert Floor
14 makeFloor();
15
16 // autoload critters
17 if ( settings->getCVar("autoload") )
18 loadAllCritters();
19
20 // insert first batch of critters
21 for ( unsigned int i=critters.size(); i < settings->getCVar("mincritters"); i++ )
22 insRandomCritter( i );
23
24 // insert food
25 for ( unsigned int i=0; i < settings->getCVar("mincritters"); i++ )
26 insFood( i );
27
28 framecounter = 0;
29 haveWinner = false;
30
31 cerr<< "Running" << " ... " << endl;
32 }
33
process()34 void WorldRace::process()
35 {
36 autosaveCritters();
37
38 // do a bullet step
39 m_dynamicsWorld->stepSimulation(0.016667f, 0, 0.016667f);
40 // m_dynamicsWorld->stepSimulation(0.016667f);
41 // m_dynamicsWorld->stepSimulation(Timer::Instance()->bullet_ms / 1000.f);
42
43 // render critter vision, optimized for this sim
44 renderVision();
45 // Read pixels into retina
46 grabVision();
47
48 // process all critters
49 for( unsigned int i=0; i < critters.size(); i++)
50 {
51 CritterB *c = critters[i];
52
53 // TOUCH inputs and references -> find overlappings
54 checkCollisions( c );
55
56 // process
57 c->process();
58
59 // process Output Neurons
60 if ( c->eat && c->touchingFood )
61 {
62 Food* f = static_cast<Food*>(c->touchedEntity);
63 float eaten = *critter_maxenergy / 100.0f;
64 if ( c->energyLevel + eaten > *critter_maxenergy )
65 eaten -= (c->energyLevel + eaten) - *critter_maxenergy;
66 if ( f->energyLevel - eaten < 0 )
67 eaten = f->energyLevel;
68
69 c->energyLevel += eaten;
70 f->energyLevel -= eaten;
71
72 // if a food unit has no more energy left, we have a winner, the race is over
73 if ( f->energyLevel == 0.0f )
74 haveWinner = true;
75 }
76 }
77
78 framecounter++;
79 if ( (haveWinner || framecounter >= settings->getCVar("critter_maxlifetime")) )
80 {
81 if ( haveWinner )
82 cerr << "we have a WINNER after " << framecounter << " frames" << endl;
83
84 cerr << "Evaluating..." << endl;
85
86 // measure their distances from their respective food targets
87 for ( unsigned int i=0; i < critters.size(); i++ )
88 {
89 // fitness function 1: distance to food cube
90 btDefaultMotionState* cmyMotionState = (btDefaultMotionState*)critters[i]->body.mouths[0]->body->getMotionState();
91 btVector3 cposi = cmyMotionState->m_graphicsWorldTrans.getOrigin();
92
93 btDefaultMotionState* fmyMotionState = (btDefaultMotionState*)food[i]->body.bodyparts[0]->body->getMotionState();
94 btVector3 fposi = fmyMotionState->m_graphicsWorldTrans.getOrigin();
95
96 critters[i]->fitness_index = 1.0f /(cposi.distance(fposi) + 0.0000001);
97
98 // fitness function 2: energy of food consumed
99 critters[i]->fitness_index += ( (float)settings->getCVar("food_maxenergy") /(food[i]->energyLevel + 0.0000001));
100
101 }
102
103 // initialize sort indices
104 vector<int> indices ( critters.size(), 0 );
105 for ( unsigned int i = 0; i < critters.size(); i++ )
106 indices[i] = i;
107
108 // sort results
109 for ( int i = critters.size(); i>0; i-- )
110 for ( int j = 0; j < i-1; j++ )
111 if ( critters[indices[j]]->fitness_index < critters[indices[j+1]]->fitness_index )
112 {
113 unsigned keepI = indices[j];
114 indices[j] = indices[j+1];
115 indices[j+1] = keepI;
116 }
117
118 // display results
119 for ( unsigned int i=0; i < critters.size(); i++ )
120 cerr << "c " << indices[i] << " : " << critters[indices[i]]->fitness_index << endl;
121
122 cerr << endl << "Initializing run " << ++testcounter << " ... " << endl;
123
124 // backup the 50% best critters
125 vector<CritterB*> best;
126 unsigned int bestNum = critters.size()/2;
127 if ( critters.size() == 1 )
128 bestNum = 1;
129 for ( unsigned int i=0; i < bestNum; i++ )
130 best.push_back( new CritterB(*critters[indices[i]], critters[indices[i]]->critterID, btVector3( 0.0f, 0.0f, 0.0f ), false, false) );
131 // remove critters and food
132 for ( unsigned int i=0; i < critters.size(); i++ )
133 {
134 stringstream buf;
135 buf << setw(4) << critters[i]->critterID << " old";
136 Textverbosemessage::Instance()->addDeath(buf);
137
138 if ( critters[i]->isPicked )
139 mousepicker->detach();
140 // FIXME on windows, we segfault here 1/10 after the first run
141 critterselection->unregisterCritterID(critters[i]->critterID);
142 critterselection->deselectCritter(critters[i]->critterID);
143 delete critters[i];
144 // FIXME
145 }
146 critters.clear();
147
148 for ( unsigned int i=0; i < food.size(); i++ )
149 {
150 if ( food[i]->isPicked )
151 mousepicker->detach();
152 delete food[i];
153 }
154 food.clear();
155
156 // clear floor and remake it
157 makeFloor();
158
159 // reinsert the best critters
160 for ( unsigned int i=0; i < best.size() && i < settings->getCVar("mincritters"); i++ )
161 insMutatedCritter( *best[i], critters.size(), best[i]->critterID, false, false );
162
163 // insert the mutants
164 unsigned int count = 0;
165 while ( critters.size() < settings->getCVar("mincritters") )
166 {
167 if ( best.size() > 0 )
168 {
169 bool brainmutant = false;
170 bool bodymutant = false;
171 if ( randgen->Instance()->get(1,100) <= settings->getCVar("brain_mutationrate") )
172 brainmutant = true;
173
174 if ( randgen->Instance()->get(1,100) <= settings->getCVar("body_mutationrate") )
175 bodymutant = true;
176
177 insMutatedCritter( *best[count], critters.size(), currentCritterID++, brainmutant, bodymutant );
178
179 CritterB* c = best[count];
180 CritterB* nc = critters[critters.size()-1];
181 stringstream buf;
182 buf << setw(4) << c->critterID << " : " << setw(4) << nc->critterID;
183 buf << " ad: " << setw(4) << nc->genotype->adamdist;
184 buf << " n: " << setw(4) << nc->brain.totalNeurons << " s: " << setw(5) << nc->brain.totalSynapses;
185
186 count++;
187 if ( count == best.size() && count > 0 )
188 count = 0;
189
190 if ( brainmutant || bodymutant )
191 {
192 buf << " ";
193 if ( brainmutant ) buf << "brain";
194 if ( brainmutant && bodymutant ) buf << "+";
195 if ( bodymutant ) buf << "body";
196 buf << " mutant";
197 }
198
199 Textverbosemessage::Instance()->addBirth(buf);
200 }
201 else
202 insRandomCritter( critters.size() );
203 }
204
205 // remove best again
206 for ( unsigned int i=0; i < best.size(); i++ )
207 delete best[i];
208
209 // reinsert respective food units
210 for ( unsigned int i=0; i < settings->getCVar("mincritters"); i++ )
211 insFood( i );
212
213 framecounter = 0;
214 haveWinner = false;
215
216 cerr << "Running... " << endl;
217 }
218 }
219
makeFloor()220 void WorldRace::makeFloor()
221 {
222 for ( unsigned int i=0; i < walls.size(); i++ )
223 delete walls[i];
224 walls.clear();
225
226 critterspacing = (float)settings->getCVar("worldsizeX") / settings->getCVar("mincritters");
227
228 makeDefaultFloor();
229
230 // seperator walls
231 float WallWidth = 0.2f;
232 float WallHalfWidth = WallWidth/2.0f;
233 float WallHeight = 1.0f;
234 float WallHalfHeight = WallHeight/2.0f;
235
236 for ( unsigned int i=1; i < settings->getCVar("mincritters"); i++ )
237 {
238 btVector3 position = btVector3 ( 0.0f-WallHalfWidth + (critterspacing*i), WallHalfHeight-WallWidth, settings->getCVar("worldsizeY")/2.0f );
239 Wall* w = new Wall( WallWidth, WallHeight, settings->getCVar("worldsizeY"), position, m_dynamicsWorld );
240 w->color.r = 0.34f; w->color.g = 0.25f; w->color.b = 0.11f;
241 walls.push_back(w);
242 }
243 }
244
insRandomCritter(int nr)245 void WorldRace::insRandomCritter(int nr)
246 {
247 CritterB *c = new CritterB(m_dynamicsWorld, currentCritterID++, btVector3( (critterspacing/2)+(critterspacing*nr), 1.0f, settings->getCVar("worldsizeY")-2.0f ), retina);
248 c->energyLevel = settings->getCVar("critter_maxenergy") / 2;
249 critters.push_back( c );
250 c->calcFramePos(critters.size()-1);
251 }
252
insMutatedCritter(CritterB & other,int nr,unsigned int id,bool mutateBrain,bool mutateBody)253 void WorldRace::insMutatedCritter(CritterB& other, int nr, unsigned int id, bool mutateBrain, bool mutateBody)
254 {
255 CritterB *nc = new CritterB(other, id, btVector3( (critterspacing/2)+(critterspacing*nr), 1.0f, settings->getCVar("worldsizeY")-2.0f ), mutateBrain, mutateBody);
256 nc->energyLevel = settings->getCVar("critter_maxenergy") / 2;
257 critters.push_back( nc );
258 nc->calcFramePos(critters.size()-1);
259 }
260
insFood(int nr)261 void WorldRace::insFood(int nr)
262 {
263 Food *f = new Food;
264 f->energyLevel = settings->getCVar("food_maxenergy");
265 f->createBody( m_dynamicsWorld, btVector3( (critterspacing/2)+(critterspacing*nr), 1.0f, 2.0f ) );
266 food.push_back( f );
267 }
268
insertCritter()269 void WorldRace::insertCritter()
270 {
271 cerr << "inserting critters is disabled during race" << endl;
272 }
273
loadAllCritters()274 void WorldRace::loadAllCritters()
275 {
276 if ( critters.size() > 0 )
277 {
278 stringstream buf;
279 buf << "use --autoload 1 at commandline to autoload critters into a race";
280 Logbuffer::Instance()->add(buf);
281 cerr << "use --autoload 1 at commandline to autoload critters into a race" << endl;
282 }
283 else
284 {
285 vector<string> files;
286 dirH.listContentsFull(dirlayout->loaddir, files);
287
288 unsigned int inserted = 0;
289 for ( unsigned int i = 0; i < files.size() && inserted < settings->getCVar("mincritters"); i++ )
290 {
291 if ( parseH->Instance()->endMatches( ".cr", files[i] ) )
292 {
293 stringstream buf;
294 buf << "loading " << files[i];
295 Logbuffer::Instance()->add(buf);
296
297 string content;
298 fileH.open( files[i], content );
299
300 critterspacing = (float)settings->getCVar("worldsizeX") / settings->getCVar("mincritters");
301 CritterB *c = new CritterB(content, m_dynamicsWorld, btVector3( (critterspacing/2)+(critterspacing*critters.size()), 1.0f, settings->getCVar("worldsizeY")-(settings->getCVar("worldsizeY")/4) ), retina);
302
303 unsigned int error = 0;
304 if ( c->genotype->bodyArch->retinasize != *critter_retinasize ) error = 1;
305
306 if ( !error)
307 {
308 critters.push_back( c );
309
310 c->critterID = currentCritterID++;
311 c->calcFramePos(critters.size()-1);
312 c->energyLevel = settings->getCVar("critter_maxenergy") / 2;
313 inserted++;
314 }
315 else
316 {
317 delete c;
318 if ( error == 1 )
319 {
320 stringstream buf;
321 buf << "ERROR: critter retinasize (" << c->genotype->bodyArch->retinasize << ") doesn't fit world retinasize (" << *critter_retinasize << ")" << files[i];
322 Logbuffer::Instance()->add(buf);
323
324 cerr << "ERROR: critter retinasize (" << c->genotype->bodyArch->retinasize << ") doesn't fit world retinasize (" << *critter_retinasize << ")" << endl;
325 }
326 }
327 }
328 }
329 stringstream buf;
330 buf << "Loaded critters from " << dirlayout->loaddir;
331 Logbuffer::Instance()->add(buf);
332 //cerr << endl << "Loaded critters from " << loaddir << endl << endl;
333 }
334 }
335
~WorldRace()336 WorldRace::~WorldRace()
337 {
338 }
339