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