1
2 /* "Species" - a CoreWars evolver. Copyright (C) 2003 'Varfar'
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the Free
6 * Software Foundation; either version 1, or (at your option) any later
7 * version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18
19 #include "species.hpp"
20 #include "rand.hpp"
21
22 #include "exec_trail.hpp"
23
24 #include <time.h>
25
26 #include <iostream>
27 #include <fstream>
28 #include <iomanip>
29 using namespace std;
30
31 #ifdef SHOW_PROGRESS_TWIDDLE
32 int progress = 0;
33 const char *progress_twiddle = "-\\|/";
34 #endif // ifdef SHOW_PROGRESS_TWIDDLE
35
36 /***** CResumeFile class declaration/implementation ********/
37
38 class CResumeFile {
39 public:
CResumeFile(CSpecies * species)40 CResumeFile(CSpecies *species): _species(species) {
41 // name of this file?
42 _name = "species-";
43 _name += species->name();
44 _name += "-resume.dat";
45 _write_count = 0;
46 }
openForRead()47 bool openForRead() {
48 CGeneration::NUM gen;
49 _write_count = 0;
50 // try to open existing file..
51 _file.clear();
52 _file.open(_name.c_str(),ios::in|ios::out|ios::binary);
53 if(!_file.is_open()) return false;
54 // check generation
55 _file.read((char*)&gen,sizeof(gen));
56 if(!_file.good() || (_species->kingdom()->generation() != gen)
57 || _file.eof()) {
58 close();
59 return false;
60 }
61 return true;
62 }
openForWrite()63 void openForWrite() { // write
64 CGeneration::NUM gen = _species->kingdom()->generation();
65 _write_count = 0;
66 // (re)create file
67 _file.clear();
68 _file.open(_name.c_str(),ios::out|ios::binary);
69 // write generation
70 _file.write((char*)&gen,sizeof(gen));
71 flush();
72 }
swapFromReadToWrite()73 void swapFromReadToWrite() {
74 _write_count = 0;
75 _file.clear();
76 }
read(CWarrior::FIGHT_DATA & fight)77 bool read(CWarrior::FIGHT_DATA &fight) {
78 if(!_file.good())
79 return false;
80 _file.read((char*)&fight,sizeof(fight));
81 return _file.good();
82 }
write(const CWarrior::FIGHT_DATA & fight)83 void write(const CWarrior::FIGHT_DATA &fight) {
84 if(_file.good()) {
85 _file.write((const char*)&fight,sizeof(fight));
86 if(++_write_count >= WRITE_FLUSH) {
87 flush();
88 _write_count = 0;
89 }
90 }
91 }
flush()92 void flush() {
93 if(_file.good()) {
94 _file.flush();
95 }
96 }
close()97 void close() {
98 _file.close();
99 }
100 private:
101 static const int WRITE_FLUSH = 15; // every n records, flush
102 CSpecies *_species;
103 string _name;
104 fstream _file;
105 int _write_count;
106 };
107
108
109 /***** CSpeciesFile class implementation *******************/
110
CSpeciesFile(const string species,const CGeneration::NUM generation)111 CSpeciesFile::CSpeciesFile(const string species,const CGeneration::NUM generation) {
112 set_filename(species,generation);
113 open_file();
114 }
115
CSpeciesFile(const CSpecies & species,const CGeneration::NUM generation)116 CSpeciesFile::CSpeciesFile(const CSpecies &species,const CGeneration::NUM generation) {
117 set_filename(species.name(),generation);
118 open_file();
119 }
120
~CSpeciesFile()121 CSpeciesFile::~CSpeciesFile() {
122 fclose(_f);
123 }
124
set_filename(const string name,const CGeneration::NUM generation)125 void CSpeciesFile::set_filename(const string name,const CGeneration::NUM generation) {
126 // build filename
127 _filename = "species-";
128 _filename += name;
129 _filename += '-';
130 string_append(_filename,generation);
131 _filename += ".dat";
132 }
133
open_file()134 void CSpeciesFile::open_file() {
135 // open file
136 _f = fopen(_filename.c_str(),"r+b");
137 if(0 == _f) { // can't open file, probably doesn't exist?
138 _f = fopen(_filename.c_str(),"w+b");
139 if(0 == _f) { // couldn't create a file either
140 PANIC(CANNOT_OPEN_GENERATION_FILE,_filename.c_str(),NULL);
141 }
142 }
143 }
144
writeWarrior(const CWarrior & warrior)145 void CSpeciesFile::writeWarrior(const CWarrior &warrior) {
146 unsigned int i, j;
147 CFitness::NUM num;
148 HEADER header;
149 // prepare header
150 header.options = HEADER::NONE;
151 if(0 < warrior.info().length())
152 header.options |= HEADER::HAS_INFO;
153 if(warrior.is_benchmarked())
154 header.options |= HEADER::HAS_BENCH;
155 header.uid = warrior.uid();
156 header.generation = warrior.generation();
157 header.mother = warrior.mother();
158 header.father = warrior.father();
159 header.start = warrior.start_idx();
160 header.num_chromosomes = warrior.species()->num_chromosomes();
161 // write header
162 if(1 != fwrite(&header,_header_size,1,_f))
163 PANIC(GENERATION_FILE_ERROR,_filename.c_str(),"could not write header");
164 // write optional parts
165 if(HEADER::HAS_INFO == (header.options & HEADER::HAS_INFO)) {
166 i = warrior.info().length()+1; // remember space for \0
167 if(MAX_INFO_LEN <= i)
168 PANIC(GENERATION_FILE_ERROR,_filename.c_str(),"info length too long");
169 if(1 != fwrite(&i,sizeof(i),1,_f))
170 PANIC(GENERATION_FILE_ERROR,_filename.c_str(),"could not write info length");
171 if(i != fwrite(warrior.info().c_str(),1,i,_f))
172 PANIC(GENERATION_FILE_ERROR,_filename.c_str(),"could not write info");
173 }
174 if(HEADER::HAS_BENCH == (header.options & HEADER::HAS_BENCH)) {
175 i = warrior.benchmark_count();
176 if(1 != fwrite(&i,sizeof(i),1,_f))
177 PANIC(GENERATION_FILE_ERROR,_filename.c_str(),"could not write benchmark_count");
178 num = warrior.benchmark();
179 if(1 != fwrite(&num,sizeof(num),1,_f))
180 PANIC(GENERATION_FILE_ERROR,_filename.c_str(),"could not write benchmark");
181 }
182 // write body
183 for(i=0; i<header.num_chromosomes; ++i) {
184 CChromosome &chromosome = *warrior.chromosome(i);
185 j = chromosome.len(); // reuse this!
186 if(1 != fwrite(&j,sizeof(j),1,_f))
187 PANIC(GENERATION_FILE_ERROR,_filename.c_str(),"could not write chromosome length");
188 for(j=0; j<chromosome.len(); ++j) {
189 if(1 != fwrite(&chromosome.code(j),_insn_size,1,_f))
190 PANIC(GENERATION_FILE_ERROR,_filename.c_str(),"could not write body");
191 }
192 }
193 }
194
readWarrior(CWarrior & warrior)195 void CSpeciesFile::readWarrior(CWarrior &warrior) {
196 unsigned int i, j, clen;
197 CFitness::NUM num;
198 HEADER header;
199 // read header
200 if(1 != fread(&header,_header_size,1,_f))
201 PANIC(GENERATION_FILE_ERROR,_filename.c_str(),"could not read header");
202 // prepare warrior
203 warrior.set_uid(header.uid);
204 warrior.set_generation(header.generation);
205 warrior.set_parents(header.mother,header.father);
206 warrior.set_start(header.start);
207 if(warrior.species()->num_chromosomes() != header.num_chromosomes)
208 PANIC(MISC,"bad number of chromosomes in file",NULL);
209 // read optional parts
210 if(HEADER::HAS_INFO == (header.options & HEADER::HAS_INFO)) {
211 if(1 != fread(&i,sizeof(i),1,_f))
212 PANIC(GENERATION_FILE_ERROR,_filename.c_str(),"could not read info length");
213 if(MAX_INFO_LEN <= i)
214 PANIC(GENERATION_FILE_ERROR,_filename.c_str(),"info length too long");
215 if(i != fread(_info,1,i,_f))
216 PANIC(GENERATION_FILE_ERROR,_filename.c_str(),"could not read info");
217 warrior.set_info(_info);
218 } else {
219 warrior.clear_info();
220 }
221 if(HEADER::HAS_BENCH == (header.options & HEADER::HAS_BENCH)) {
222 if(1 != fread(&i,sizeof(i),1,_f))
223 PANIC(GENERATION_FILE_ERROR,_filename.c_str(),"could not read benchmark count");
224 if(1 != fread(&num,sizeof(num),1,_f))
225 PANIC(GENERATION_FILE_ERROR,_filename.c_str(),"could not read benchmark sum");
226 warrior.set_benchmark(num,i);
227 } else
228 warrior.set_benchmark(0,0);
229 // read body
230 for(i=0; i<header.num_chromosomes; ++i) {
231 CChromosome &chromosome = *warrior.chromosome(i);
232 if(1 != fread(&clen,sizeof(clen),1,_f))
233 PANIC(GENERATION_FILE_ERROR,_filename.c_str(),"could not read chromosome length");
234 chromosome.set_len(clen);
235 for(j=0; j<clen; j++) {
236 if(1 != fread(&chromosome.code(j),_insn_size,1,_f))
237 PANIC(GENERATION_FILE_ERROR,_filename.c_str(),"could not read body");
238 }
239 }
240 }
241
242 /***** CSpecies class implementation *******************/
243
244 CSpecies::TUid CSpecies::_uid_seq = 0; // init it
245 const CFitness::NUM CSpecies::DEFAULT_FITNESS_WEIGHTING = 1.0F; // no affect
246
CSpecies(CGenus * genus,const string & name,const bool evolving)247 CSpecies::CSpecies(CGenus *genus,const string &name,const bool evolving): // create an empty species
248 CHookable(), _name(name), _evolving(evolving), _genus(genus) {
249 _uid = ++_uid_seq; // set unique uid
250 /* inherit defaults */
251 _fitness_weighting = DEFAULT_FITNESS_WEIGHTING;
252 _fitness = _genus->fitness();
253 _length = _genus->length();
254 _freq = _genus->freq();
255 _operands = _genus->operands();
256 _reproduction = _genus->reproduction();
257 _exec_trail = 0;
258 _chromosome = 0;
259 _num_chromosomes = 0;
260 _start_chromosome = 0;
261 _warrior = 0;
262 _next = 0;
263 _num_warriors = 0;
264 _survives = 0;
265 _resume = 0;
266 _resuming = false;
267 }
268
~CSpecies()269 CSpecies::~CSpecies() {
270 // delete all species
271 clear();
272 delete _exec_trail; //TODO: ophaned COpcodeMap
273 // delele chromosomes
274 for(int i=0; i<_num_chromosomes; i++)
275 delete _chromosome[i];
276 // delete anything not inherited
277 if(genus()->fitness() != _fitness)
278 delete _fitness;
279 if(genus()->length() != _length)
280 delete _length;
281 if(genus()->freq() != _freq)
282 delete _freq;
283 if(genus()->operands() != _operands)
284 delete _operands;
285 if(genus()->reproduction() != _reproduction)
286 delete _reproduction;
287 if(0 != _resume)
288 delete _resume;
289 }
290
clear()291 void CSpecies::clear() { /* removes all warriors from this species */
292 int i;
293 if(0 == _warrior) // not allocated?
294 return;
295 for(i=0; i<_num_warriors; i++) {
296 delete _warrior[i];
297 }
298 _num_warriors = 0;
299 delete[] _warrior;
300 delete[] _survives;
301 delete[] _next; // pointers not 'owned'
302 delete[] _chromosome;
303 _warrior = 0;
304 _next = 0;
305 _survives = 0;
306 }
307
find(const CWarrior::TUid uid) const308 int CSpecies::find(const CWarrior::TUid uid) const { // -1 if not found in this species
309 int i;
310 for(i=_num_warriors-1; i>=0; --i) { // seek backwards, since more recent warriors are more likely to be interesting
311 if(uid == _warrior[i]->uid())
312 return i;
313 }
314 return -1; // not found
315 }
316
read_ini(INIFile & ini)317 void CSpecies::read_ini(INIFile &ini) {
318 int i, tries;
319 string num;
320 CSpeciesFile *file;
321 KeyValuePair *kvp;
322 clear();
323 // uid
324 kvp = ini.get("uid"); if(0 == kvp) { // not specified?
325 if(0 != kingdom()->generation()) { // not first generation? this is an error
326 PANIC(MISC,name().c_str(),"uid expected");
327 }
328 } else { // uid specified
329 _uid = kvp->getValueAsInt();
330 }
331 // evolving?
332 kvp = ini.get("evolving"); if(0 == kvp) PANIC(MISC,"evolving expected",NULL);
333 _evolving = kvp->getValueAsBool();
334 // load some defaults; these might be overloaded
335 _fitness = _fitness->read_override_ini(ini);
336 _length = _length->read_override_ini(ini);
337 _freq = CInstGenerator::read_override_ini(_freq,ini);
338 _operands = COperand::read_override_ini(_operands,ini);
339 _reproduction = _reproduction->read_override_ini(ini);
340 // exec trail specified?
341 kvp = ini.get("exec_trail");
342 if(0 != kvp) {
343 _exec_trail = new CExecTrail(new COpcodeMap()); // hmm
344 if(!_exec_trail->load_from_file(kvp->getValueAsChar()))
345 PANIC(MISC,_exec_trail->filename().c_str(),"could not load exec trail from file");
346 }
347 // fitness weighting specified?
348 kvp = ini.get("fitness_weighting");
349 if(0 != kvp) {
350 _fitness_weighting = kvp->getValueAsFloat();
351 }
352 // split into several chromosomes?
353 kvp = ini.get("num_chromosomes");
354 if(!_evolving) {
355 _auto_chromosome = true; // avoids lots of writing/reading of ini entries later etc
356 if(0 != kvp)
357 PANIC(MISC,"chromosomes specified for a benchmark warrior",NULL);
358 } else if(0 != kvp) {
359 _auto_chromosome = false;
360 _num_chromosomes = kvp->getValueAsInt();
361 if((_num_chromosomes <= 0) || (_num_chromosomes >= length()->max()))
362 PANIC(MISC,name().c_str(),"invalid number of chromosomes specified");
363 _chromosome = new CChromosomeType*[_num_chromosomes];
364 for(i=0; i<_num_chromosomes; i++) {
365 // prepare the number of this chromosome as a string to search for
366 num = "chromosome_";
367 string_append(num,i);
368 // and get it
369 kvp = ini.get(num); if (0 == kvp) PANIC(MISC,name().c_str(),"chromosome expected");
370 // and create it
371 if(verbose) { // debug output
372 cout << "\t\tadding " << kvp->getValueAsChar() << endl;
373 }
374 _chromosome[i] = new CChromosomeType(this,kvp->getValueAsChar());
375 }
376 // start specified?
377 kvp = ini.get("start_chromosome");
378 if(0 != kvp) {
379 _start_chromosome = kvp->getValueAsInt();
380 if(_start_chromosome > _num_chromosomes)
381 PANIC(MISC,_name.c_str(),"start chromosome is greater than the number of chromosomes in the specices");
382 }
383 } else {
384 _auto_chromosome = true;
385 _num_chromosomes = 1;
386 _chromosome = new CChromosomeType*[1];
387 _chromosome[0] = new CChromosomeType(this);
388 }
389 // how many warriors?
390 kvp = ini.get("num_warriors"); if(0 == kvp) PANIC(MISC,"num_warriors expected",NULL);
391 _num_warriors = kvp->getValueAsInt();
392 _warrior = new CWarrior*[_num_warriors];
393 _next = new CWarrior*[_num_warriors];
394 _survives = new bool[_num_warriors];
395 // load chromosomes
396 if(!_auto_chromosome) {
397 for(i=0; i<_num_chromosomes; i++) {
398 if(verbose) { // debug output
399 cout << "\t\tloading " << _chromosome[i]->name() << endl;
400 }
401 _chromosome[i]->read_ini(ini);
402 }
403 }
404 // load them if benchmark
405 if(is_bench()) {
406 for(i=0; i<_num_warriors; ++i) {
407 // prepare index number
408 num = "warrior_";
409 string_append(num,i);
410 // get it
411 kvp = ini.get(num); if(0 == kvp) { // hmm
412 PANIC(MISC,num.c_str(),"expected");
413 } else {
414 kvp->getValueAsString(num); // reuse string
415 if(verbose) { // debug output
416 cout << "\t\tbench " << i << '=' << num << endl;
417 }
418 _warrior[i] = new CWarrior(this,num);
419 _warrior[i]->code_check();
420 }
421 }
422 } else { // evolving
423 num = "species-";
424 num += _name;
425 num += "-";
426 string_append(num,kingdom()->generation());
427 num += ".dat";
428 if(file_exists(num) || (0 != kingdom()->generation())) { // not first generation, so load them?
429 file = new CSpeciesFile(*this,kingdom()->generation());
430 for(i=0; i<_num_warriors; ++i) {
431 _warrior[i] = new CWarrior(this,0); // uid will be overriden, no need to use up uidspace
432 file->readWarrior(*_warrior[i]);
433 }
434 delete file;
435 } else { // first generation, so create some random warriors
436 if(kingdom()->in_experiment()) {
437 for(i=0; i<_num_warriors; ++i) {
438 _warrior[i] = new CWarrior(this);
439 if(!silent) { // debug info
440 cout << "\t\tgenerating " << _name << ':' << _warrior[i]->uid() << " .. ";
441 }
442 tries = 0;
443 do {
444 tries++;
445 _warrior[i]->clear();
446 _warrior[i]->set_info("generated from random");
447 for(int j=0; j<num_chromosomes(); j++) {
448 chromosome(j)->reproduction()->generateCompletelyRandom(*(_warrior[i]->chromosome(j)));
449 }
450 } while(!CReproduction::fit_enough(*_warrior[i]));
451 if(!silent) { // debug info
452 cout << "took " << tries << " tries";
453 if(verbose) {
454 cout << ':' << endl;
455 _warrior[i]->rc(cout);
456 }
457 cout << endl;
458 }
459 }
460 } else { // lazy load?
461 for(i=0; i<_num_warriors; ++i) {
462 _warrior[i] = 0; // null
463 }
464 }
465 }
466 }
467 }
468
write_ini(ostream & os)469 void CSpecies::write_ini(ostream &os) {
470 int i;
471 os << "uid=" << _uid << endl
472 << "evolving=" << _evolving << endl;
473 // dump misc stuff
474 _fitness->write_override_ini(os,_genus->fitness());
475 _length->write_override_ini(os,_genus->length());
476 _freq->write_override_ini(os,_genus->freq());
477 _operands->write_override_ini(os,_genus->operands());
478 _reproduction->write_override_ini(os,_genus->reproduction());
479 // exec trail?
480 if(_exec_trail) {
481 os << "exec_trail=" << _exec_trail->filename() << endl;
482 }
483 // fitness weighting?
484 if(_fitness_weighting != DEFAULT_FITNESS_WEIGHTING) { // non-default?
485 os << "fitness_weighting=" << _fitness_weighting << endl;
486 }
487 // chromosomes
488 if(!_auto_chromosome) {
489 os << "num_chromosomes=" << _num_chromosomes << endl;
490 for(i=0; i<_num_chromosomes; ++i)
491 os << "chromosome_" << i << '=' << _chromosome[i]->name() << endl;
492 if(0 != _start_chromosome)
493 os << "start_chromosome=" << _start_chromosome << endl;
494 }
495 // list warriors
496 os << "num_warriors=" << size() << endl;
497 if(is_bench()) {
498 for(i=0; i<_num_warriors; ++i) {
499 os << "warrior_" << i << '=' << _warrior[i]->filename() << endl;
500 }
501 } else { // is evolver
502 save();
503 }
504 // print each chromosome
505 if(!_auto_chromosome) {
506 for(i=0; i<_num_chromosomes; ++i)
507 _chromosome[i]->write_ini(os);
508 }
509 }
510
save()511 void CSpecies::save() {
512 int i;
513 CSpeciesFile *file;
514 if(is_bench()) // no point?
515 return;
516 file = new CSpeciesFile(*this,kingdom()->generation());
517 for(i=0; i<_num_warriors; ++i) {
518 file->writeWarrior(*_warrior[i]);
519 }
520 delete file;
521 }
522
ready()523 void CSpecies::ready() { /* this will blank all fitness metrics preceeding the fighting of a generation */
524 int i;
525 for(i=0; i<_num_warriors; ++i) {
526 _warrior[i]->ready();
527 }
528 // resume support
529 if(0 != _resume)
530 delete _resume;
531 if(is_evolving()) {
532 _resume = new CResumeFile(this);
533 _resuming = _resume->openForRead();
534 if(!_resuming)
535 _resume->openForWrite();
536 } else
537 _resuming = false;
538 }
539
fight(CSpecies & enemy)540 int CSpecies::fight(CSpecies &enemy) { /* this will actually do all the fights for a generation; returns number of rounds fought */
541 CWarrior::FIGHT_DATA data;
542 int i, j, count=0, resumed = 0;
543 for(i=0; i<_num_warriors; ++i) {
544 for(j=0; j<enemy._num_warriors; ++j) {
545 // try to resume..
546 if(_resuming) {
547 if(_resume->read(data)) {
548 // sanity check
549 if(data.src != _warrior[i]->uid())
550 PANIC(BAD_RESUMEFILE,"src uid failed",NULL);
551 if(data.dest != enemy._warrior[j]->uid())
552 PANIC(BAD_RESUMEFILE,"dest uid failed",NULL);
553 // do the actual scoring
554 count += _warrior[i]->fight(*enemy._warrior[j],data,false);
555 // done, so next enemy
556 resumed++;
557 continue;
558 } else {
559 if(!silent && (0 < resumed)) {
560 cout << "(loaded " << resumed << " fights from " << name() << "\'s resume log)" << endl;
561 }
562 // failed, so no more resumes
563 _resuming = false;
564 _resume->swapFromReadToWrite();
565 // drop through
566 }
567 }
568 // actually fight
569 PROGRESS_TWIDDLE
570 count += _warrior[i]->fight(*enemy._warrior[j],data);
571 if(is_evolving()) {
572 _resume->write(data);
573 }
574 }
575 }
576 if(0 != _resume) {
577 _resume->flush();
578 }
579 if(!silent && _resuming && (0 < resumed)) {
580 cout << "(loaded all " << resumed << " fights from " << name() << "\'s resume log)" << endl;
581 }
582 return count;
583 }
584
selection()585 CWarrior *CSpecies::selection() { /* this will do selection on all the warriors in this species; returns pointer to the best too */
586 // there is bound to be a better way than this
587 int i, j, tries,
588 mother, father, best,
589 benchmark_n, benchmark_score, benchmark_sum = 0;
590 bool ok;
591 fstream red;
592 string name, note;
593 if(!_evolving) // no point with benchmark warriors
594 return 0;
595 _resume->close();
596 // ready survives table
597 for(i=0; i<_num_warriors; ++i) { // clear survives flags
598 _survives[i] = false;
599 }
600 // prepare for benchmarking?
601 if(0 != kingdom()->benchmark()) {
602 sort();
603 }
604 // best always survives
605 best = scores(_min,_sum);
606 _next[0] = _warrior[best];
607 _survives[best] = true;
608 // benchmark the better ones?
609 if(0 != kingdom()->benchmark()) {
610 if(verbose) {
611 cout << "\tbenchmarking " << _name << endl;
612 }
613 // how many warriors to test?
614 benchmark_n = (int)(_num_warriors * kingdom()->benchmark_n());
615 if(0 == benchmark_n) benchmark_n = 1; // always at least the first(best) warrior
616 if(benchmark_n > _num_warriors) PANIC(MISC,"bad benchmark_n",NULL);
617 // and do it
618 for(i=0; i<benchmark_n; ++i) {
619 if(!silent) {
620 cout << "\t\tbenchmarking " << _name << ':' << _warrior[i]->uid() << " .. " << flush;
621 }
622 // todo: make this use a variable number of rounds in the benchmark
623 benchmark_score = 0;
624 for(j=0; j<kingdom()->benchmark()->_num_warriors; ++j) {
625 benchmark_score += _warrior[i]->fight(*kingdom()->benchmark()->_warrior[j],100,3,1,0);
626 }
627 benchmark_score = (int)((double)benchmark_score / (double)kingdom()->benchmark()->num_warriors()); // = score / num_warriors_in_bench / rounds_per_warrior * 100
628 if(!silent) {
629 cout << "scored " << benchmark_score << endl;
630 }
631 _warrior[i]->incorporate_benchmark(benchmark_score);
632 benchmark_sum += benchmark_score;
633 name = _name;
634 name += "--";
635 string_append(name,_warrior[i]->uid());
636 name += ".red";
637 note = ";benchmark ";
638 string_append(note,(int)round(_warrior[i]->benchmark()));
639 if(kingdom()->benchmark_candidate() <= benchmark_score) {
640 note += " CANDIDATE!";
641 if(!silent) {
642 cout << "Warrior " << name << " is a CANDIDATE with a benchmark score of " << benchmark_score << endl;
643 }
644 }
645 red.clear();
646 red.open(name.c_str(),ios_base::out|ios_base::trunc);
647 if(red) {
648 _warrior[i]->red(red,note.c_str());
649 } else {
650 cerr << "WARNING: could not write to " << name << endl;
651 }
652 red.close();
653 }
654 } else { // just save the best of this generation
655 name = _name;
656 name += "-";
657 string_append(name,kingdom()->generation());
658 name += ".red";
659 red.clear();
660 red.open(name.c_str(),ios_base::out|ios_base::trunc);
661 if(red) {
662 _warrior[best]->red(red);
663 } else {
664 cerr << "WARNING: could not write to " << name << endl;
665 }
666 red.close();
667 }
668 // and adjust the value to return (since best is always moved to _next[0])
669 best = 0;
670 // and select the rest
671 for(i=1; i<_num_warriors; ++i) { // for each new warrior; note base 1
672 ok = false;
673 do {
674 if(reproduction()->survive()) { // does a warrior from the old generation survive?
675 j = rnd(); // get a random one
676 if(_survives[j]) { // already surviving?
677 continue;
678 }
679 _survives[j] = true;
680 _next[i] = _warrior[j];
681 if(verbose) {
682 cout << _next[i]->uid() << " survives" << endl;
683 }
684 } else { // breed a warrior
685 mother = rnd(); // get a random one
686 do {
687 father = rnd(); // get a random one
688 } while(father == mother); // until different
689 _next[i] = new CWarrior(this,_warrior[mother]->uid(),_warrior[father]->uid());
690 if(verbose) {
691 cout << _next[i]->uid() << " born from " << _warrior[mother]->uid() << " and " << _warrior[father]->uid() << endl;
692 }
693 tries = 0;
694 do {
695 tries++;
696 for(int j=0; j<num_chromosomes(); j++) {
697 chromosome(j)->reproduction()->breed(*(_next[i]->chromosome(j)),*(_warrior[mother]->chromosome(j)),*(_warrior[father]->chromosome(j)));
698 }
699 } while(!CReproduction::fit_enough(*_next[i]));
700 _next[i]->code_check();
701 if(verbose) { // debug output
702 cout << "generating " << _warrior[i]->uid() << " took " << tries << " tries:" << endl;
703 _warrior[i]->rc(cout);
704 cout << endl;
705 }
706 }
707 ok = true; // if here, we have generated something
708 } while(!ok);
709 }
710 // transfer next generation to this one
711 for(i=0; i<_num_warriors; ++i) {
712 if(!_survives[i]) { // clean up?
713 delete _warrior[i];
714 }
715 _warrior[i] = _next[i];
716 }
717 // done
718 return _warrior[best];
719 }
720
721 /* This returns a random warrior based upon fitness */
rnd() const722 int CSpecies::rnd() const {
723 int i;
724 const CFitness::NUM x = CRand::frand(_sum);
725 CFitness::NUM y; fitness()->zero(y);
726 do {
727 for(i=0; i<_num_warriors; ++i) {
728 y += (_warrior[i]->score() - _min); // based upon _min
729 if(y > x) { // found it?
730 return i;
731 }
732 }
733 cerr << "WARNING: roulette off end of CSpecies for fitness" << endl;
734 } while(true);
735 }
736
737 /* This returns the minimum and sum (adjusted relative to min) scores for this species;
738 these are the numbers needed to do roulette selection of the warriors based upon fitness.
739 The min is actually 1 less than the true min, so even the worst warriors have a small chance
740 of being selected; returns index of the best too */
scores(CFitness::NUM & min,CFitness::NUM & sum) const741 int CSpecies::scores(CFitness::NUM &min,CFitness::NUM &sum) const {
742 int i, ret = 0;
743 CFitness::NUM max = _warrior[ret]->score();
744 min = max;
745 fitness()->zero(sum);
746 // find min and max
747 for(i=0; i<_num_warriors; ++i) {
748 if(_warrior[i]->score() > max) {
749 ret = i;
750 max = _warrior[ret]->score();
751 } else if(_warrior[i]->score() < min) {
752 min = _warrior[i]->score();
753 }
754 }
755 // work out sum, adjusted for min as a base
756 min--;
757 for(i=0; i<_num_warriors; ++i) {
758 sum += _warrior[i]->score();
759 sum -= _min;
760 }
761 if(sum <= 0.0F)
762 PANIC(MISC,"error in CSpecies::scores(..)",NULL);
763 return ret;
764 }
765
766 /* this sorts the warriors into score order, best in 0 */
sort()767 void CSpecies::sort() {
768 // TODO: replace this bubblesort?
769 bool changed;
770 int i, j, len = _num_warriors;
771 CWarrior *swap;
772 do {
773 changed = false;
774 len--;
775 j=0;
776 for(i=0; i<len; i++) {
777 j++;
778 if(_warrior[i]->score() < _warrior[j]->score()) {
779 // swap the pointers over
780 swap = _warrior[i];
781 _warrior[i] = _warrior[j];
782 _warrior[j] = swap;
783 // and remember we've done something
784 changed = true;
785 }
786 }
787 } while(changed);
788 }
789
chromosome(const unsigned int i) const790 CChromosomeType *CSpecies::chromosome(const unsigned int i) const {
791 if(safety_checks)
792 if(_num_chromosomes < i)
793 PANIC(MISC,"chromosome index beyond range",NULL);
794 return _chromosome[i];
795 }
796
kingdom() const797 CKingdom *CSpecies::kingdom() const { return _genus->kingdom(); }
798
warrior(const CWarrior::TUid uid) const799 CWarrior *CSpecies::warrior(const CWarrior::TUid uid) const { /* fetch a particular warrior from this species; NULL if not present */
800 int i;
801 CWarrior *ret = 0;
802 for(i=0; i<_num_warriors; ++i) {
803 if(_warrior[i]->uid() == uid) { // match?
804 return _warrior[i];
805 }
806 }
807 return 0; // not found
808 }
809
810