1 /* "Species" - a CoreWars evolver. Copyright (C) 2003 'Varfar'
2 *
3 * This program is free software; you can redistribute it and/or modify it
4 * under the terms of the GNU General Public License as published by the Free
5 * Software Foundation; either version 1, or (at your option) any later
6 * version.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 * more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 675 Mass Ave, Cambridge, MA 02139, USA.
16 */
17
18 #include "kingdom.hpp"
19
20 #include <fstream>
21 #include <iomanip>
22 using namespace std;
23
24 /***** globals declared in species.hpp that belong here ******/
25
26 const float VERSION = 1.2F;
27
28 /***** CKingdomFile class implementation *************/
29
CKingdomFile()30 CKingdomFile::CKingdomFile(): _filename("species-gen.dat") {}
31
writeKingdom(const CGeneration::NUM generation,const CWarrior::TUid uid)32 void CKingdomFile::writeKingdom(const CGeneration::NUM generation,const CWarrior::TUid uid) {
33 // open file
34 _f = fopen(_filename.c_str(),"wb");
35 if(0 == _f)
36 PANIC(BAD_GEN_FILE,_filename.c_str(),"cannot open file");
37 // write stuff
38 if(1 != fwrite(&generation,sizeof(CGeneration::NUM),1,_f))
39 PANIC(BAD_GEN_FILE,_filename.c_str(),"cannot write generation");
40 if(1 != fwrite(&uid,sizeof(CWarrior::TUid),1,_f))
41 PANIC(BAD_GEN_FILE,_filename.c_str(),"cannot write uid");
42 // done
43 if(0 != fclose(_f))
44 PANIC(BAD_GEN_FILE,_filename.c_str(),"cannot close file");
45 }
46
readKingdom(CGeneration::NUM & generation,CWarrior::TUid & uid)47 void CKingdomFile::readKingdom(CGeneration::NUM &generation,CWarrior::TUid &uid) {
48 // open file
49 _f = fopen(_filename.c_str(),"rb");
50 if(0 == _f)
51 PANIC(BAD_GEN_FILE,_filename.c_str(),"cannot open file");
52 // read stuff
53 if(1 != fread(&generation,sizeof(CGeneration::NUM),1,_f))
54 PANIC(BAD_GEN_FILE,_filename.c_str(),"cannot read generation");
55 if(1 != fread(&uid,sizeof(CWarrior::TUid),1,_f))
56 PANIC(BAD_GEN_FILE,_filename.c_str(),"cannot read uid");
57 // done
58 if(0 != fclose(_f))
59 PANIC(BAD_GEN_FILE,_filename.c_str(),"cannot close file");
60 }
61
62 /***** CKingdom class implementation *******************/
63
CKingdom()64 CKingdom::CKingdom() {
65 // default metrics
66 _coresize = 8000;
67 _processes = 8000;
68 _cycles = 80000;
69 _rounds = 300;
70 _mars_prepared = false;
71 _uid = 0;
72 set_generation(0);
73 _in_experiment = true;
74 _fitness = new CFitness();
75 _length = new CLength();
76 _freq = 0; // factory!
77 _operands = COperand::NewDefault(_coresize);
78 _reproduction = new CReproduction();
79 _genus = 0; // null!
80 _num_genera = 0;
81 _kingdom_file = new CKingdomFile();
82 _hill = "redcode-94";
83 _author = "Anon";
84 _email = "made@up.com";
85 _benchmark_genus = 0;
86 _benchmark_species = 0;
87 }
88
~CKingdom()89 CKingdom::~CKingdom() {
90 clear();
91 delete _fitness;
92 delete _length;
93 delete _freq;
94 delete _operands;
95 delete _reproduction;
96 delete _kingdom_file;
97 if(0 != _benchmark_genus) delete _benchmark_genus;
98 if(0 != _benchmark_species) delete _benchmark_species;
99 }
100
clear()101 void CKingdom::clear() { /* clears all the genera */
102 int i;
103 if(0 == _genus) // not allocated?
104 return;
105 for(i=0; i<_num_genera; ++i) {
106 delete _genus[i];
107 }
108 _num_genera = 0;
109 delete[] _genus;
110 _genus = 0;
111 }
112
num_evolving_species() const113 int CKingdom::num_evolving_species() const {
114 int i, count=0;
115 for(i=0; i<_num_genera; i++)
116 count += _genus[i]->num_evolving_species();
117 return count;
118 }
119
num_evolved_warriors() const120 int CKingdom::num_evolved_warriors() const {
121 int i, count=0;
122 for(i=0; i<_num_genera; i++)
123 count += _genus[i]->num_evolved_warriors();
124 return count;
125 }
126
num_warriors() const127 int CKingdom::num_warriors() const { /* returns a trivia number of warriors 'live' in the kingdom at this time */
128 int i, count=0;
129 for(i=0; i<_num_genera; i++)
130 count += _genus[i]->num_warriors();
131 return count;
132 }
133
set_generation(const CGeneration::NUM num)134 void CKingdom::set_generation(const CGeneration::NUM num) {
135 _generation = num;
136 }
137
load(const bool runnable)138 void CKingdom::load(const bool runnable) {
139 bool gen0 = false; // generation 0 marker?
140 fstream fs;
141 INIFile *ini;
142 clear();
143 // open the ini file
144 if(verbose) {
145 cout << "openning species.dat" << endl;
146 }
147 fs.clear();
148 fs.open("species.dat",ios::in|ios::binary); // open data file
149 _in_experiment = fs;
150 if(!_in_experiment) { // couldn't open it?
151 if(verbose) {
152 cout << "species.dat failed; trying species.ini" << endl;
153 }
154 fs.close();
155 fs.clear();
156 fs.open("species.ini",ios::in|ios::binary);
157 if(!fs) {
158 PANIC(CANNOT_OPEN_INI,"species.ini is missing",NULL);
159 }
160 gen0 = true;
161 _generation = 0;
162 _in_experiment = runnable;
163 } else {
164 // load kingdom file
165 _kingdom_file->readKingdom(_generation,_uid);
166 }
167 // load it
168 ini = new INIFile(fs);
169 read_ini(*ini);
170 delete ini;
171 fs.close();
172 // init?
173 if(gen0 && _in_experiment) {
174 if(verbose) {
175 cout << "writing species.dat" << endl;
176 }
177 fs.open("species.dat",ios::out|ios::trunc);
178 write_ini(fs);
179 fs.close();
180 _kingdom_file->writeKingdom(_generation,_uid);
181 }
182 }
183
save()184 void CKingdom::save() {
185 int i;
186 for(i=0; i<_num_genera; ++i) {
187 _genus[i]->save();
188 }
189 _kingdom_file->writeKingdom(_generation,_uid);
190 }
191
read_ini(INIFile & ini)192 void CKingdom::read_ini(INIFile &ini) {
193 KeyValuePair *kvp;
194 int i;
195 bool has_benchmark = false;
196 string num, benchmark;
197 // forget memory!
198 clear();
199 dealloc_mars();
200 // go to correct section
201 if(!ini.seek("kingdom")) PANIC(MISC,"[kingdom] section expected",NULL);
202 // load the core metrics
203 kvp = ini.get("hill"); if(0 != kvp) kvp->getValueAsString(_hill);
204 kvp = ini.get("author"); if(0 != kvp) kvp->getValueAsString(_author);
205 kvp = ini.get("email"); if(0 != kvp) kvp->getValueAsString(_email);
206 kvp = ini.get("coresize"); if(0 == kvp) PANIC(MISC,"coresize expected",NULL); _coresize = kvp->getValueAsInt();
207 kvp = ini.get("processes"); if(0 == kvp) PANIC(MISC,"processes expected",NULL); _processes = kvp->getValueAsInt();
208 kvp = ini.get("cycles"); if(0 == kvp) PANIC(MISC,"cycles expected",NULL); _cycles = kvp->getValueAsInt();
209 kvp = ini.get("minsep"); if(0 == kvp) PANIC(MISC,"minsep expected",NULL); _minsep = kvp->getValueAsInt();
210 kvp = ini.get("pspacesize"); if(0 == kvp) PANIC(MISC,"pspacesize expected",NULL); _pspacesize = kvp->getValueAsInt();
211 alloc_mars();
212 // load benchmark?
213 kvp = ini.get("benchmark");
214 if(0 != kvp) {
215 kvp->getValueAsString(benchmark);
216 kvp = ini.get("benchmark_n"); if(0 == kvp) PANIC(MISC,"benchmark_n expected",NULL);
217 _benchmark_n = kvp->getValueAsFloat();
218 if(0.0F >= _benchmark_n) PANIC(MISC,"benchmark_n cannot be 0.0 or less",NULL);
219 kvp = ini.get("benchmark_candidate"); if(0 == kvp) PANIC(MISC,"benchmark_candidate expected",NULL); _benchmark_candidate = kvp->getValueAsInt();
220 if(0 >= _benchmark_candidate) PANIC(MISC,"benchmark_candidate cannot be 0 or less",NULL);
221 has_benchmark = true;
222 }
223 // how many rounds are battles measured upon? must be specified
224 kvp = ini.get("rounds"); if(0 == kvp) PANIC(MISC,"rounds expected",NULL); _rounds = kvp->getValueAsInt();
225 // load the state of evolution so far; if not specified, initial defaults assumed
226 //kvp = ini.get("generation"); if(0 == kvp) _generation = 0; else _generation = kvp->getValueAsInt();
227 //kvp = ini.get("uid"); if(0 == kvp) { if(0 != _generation) PANIC(MISC,"uid expected"); else _uid = 0; } else _uid = kvp->getValueAsInt();
228 // init some defaults
229 _fitness->read_ini(ini);
230 _length->read_ini(ini);
231 _freq = CInstGenerator::read_ini(ini);
232 delete _operands;
233 _operands = COperand::read_ini(ini,_coresize);
234 _reproduction->read_ini(ini);
235 // how many genera do we have?
236 kvp = ini.get("num_genera"); if(0 == kvp) PANIC(MISC,"num_genera expected",NULL); _num_genera = kvp->getValueAsInt();
237 // load each genus name
238 _genus = new CGenus*[_num_genera];
239 for(i=0; i<_num_genera; ++i) {
240 // prepare the number of this genus as a string to search for
241 num = "";
242 string_append(num,i);
243 // and get it
244 kvp = ini.get(num); if (0 == kvp) PANIC(MISC,"genus expected",NULL);
245 // and create it
246 if(verbose) { // debug output
247 cout << "adding " << kvp->getValueAsChar() << endl;
248 }
249 _genus[i] = new CGenus(this,kvp->getValueAsChar());
250 }
251 // actually load each genus
252 for(i=0; i<_num_genera; ++i) {
253 // go to the correct section
254 if(!silent) { // debug output
255 cout << "loading " << _genus[i]->name() << endl;
256 }
257 if(!ini.seek(_genus[i]->name())) {
258 PANIC(MISC,_genus[i]->name().c_str(),"genus section not found");
259 }
260 // and load it
261 _genus[i]->read_ini(ini);
262 }
263 // load the benchmark?
264 if(has_benchmark) {
265 _benchmark_genus = new CGenus(this,"benchmark");
266 _benchmark_species = new CSpecies(_benchmark_genus,benchmark,false);
267 // go to the correct section
268 if(!silent) { // debug output
269 cout << "loading bench-species " << _benchmark_species->name() << endl;
270 }
271 if(!ini.seek(_benchmark_species->name())) {
272 PANIC(MISC,_benchmark_species->name().c_str()," benchmark-species section not found");
273 }
274 // and load it
275 _benchmark_species->read_ini(ini);
276 }
277 }
278
write_ini(ostream & os)279 void CKingdom::write_ini(ostream &os) {
280 int i;
281 os << endl
282 << "[kingdom]" << endl
283 << "hill=" << _hill << endl
284 << "author=" << _author << endl
285 << "email=" << _email << endl
286 << "coresize=" << _coresize << endl
287 << "processes=" << _processes << endl
288 << "cycles=" << _cycles << endl
289 << "rounds=" << _rounds << endl
290 << "minsep=" << _minsep << endl
291 << "pspacesize=" << _pspacesize << endl
292 /* << "generation=" << _generation << endl
293 << "uid=" << _uid << endl*/;
294 // default stuff
295 _fitness->write_ini(os);
296 _length->write_ini(os);
297 _freq->write_ini(os);
298 _operands->write_ini(os);
299 _reproduction->write_ini(os);
300 // benchmark?
301 if(0 != _benchmark_species) {
302 os << "benchmark=" << _benchmark_species->name() << endl
303 << "benchmark_n=" << _benchmark_n << endl
304 << "benchmark_candidate=" << _benchmark_candidate << endl;
305 }
306 // list all genuses declared
307 i=0;
308 os << "num_genera=" << _num_genera << endl;
309 for(i=0; i<_num_genera; ++i) {
310 os << i << '=' << _genus[i]->name() << endl;
311 }
312 // dump the list of all the species declared for each genus
313 for(i=0; i<_num_genera; ++i) {
314 os << endl
315 << '[' << _genus[i]->name() << ']' << endl;
316 _genus[i]->write_ini(os);
317 }
318 // dump the benchmark?
319 if(0 != _benchmark_species) {
320 os << endl
321 << '[' << _benchmark_species->name() << ']' << endl;
322 _benchmark_species->write_ini(os);
323 }
324 }
325
fight()326 void CKingdom::fight() { /* this will actually do all the fights for a generation */
327 int i, rounds = 0,
328 enemy = 1;
329 time_t time_start, time_fight_start, time_fight_end, time_end;
330 CWarrior *best = 0, *candidate;
331 // check!
332 if(!_in_experiment)
333 PANIC(BAD_SPECIES_STATE,"kingdom cannot fight outside an experiment",NULL);
334 // ok
335 if(metrics) {
336 time(&time_start);
337 }
338 save();
339 // ready all species
340 for(i=0; i<_num_genera; ++i) {
341 _genus[i]->ready();
342 }
343 if(!silent) {
344 cout << "fighting.." << endl;
345 }
346 // and fight them
347 if(metrics) {
348 time(&time_fight_start);
349 }
350 for(i=0; i<_num_genera; ++i) {
351 // do the fight
352 if(!silent) { // debug output
353 cout << "fight: " << _genus[i]->name() << " vs. " << _genus[enemy]->name() << endl;
354 }
355 rounds += _genus[i]->fight(*_genus[enemy]);
356 // next
357 if(++enemy >= _num_genera) // wrap?
358 enemy = 0;
359 }
360 if(metrics) {
361 time(&time_fight_end);
362 }
363 if(!silent) {
364 cout << "breeding.." << endl;
365 }
366 // and breed them
367 for(i=0; i<_num_genera; ++i) {
368 candidate = _genus[i]->selection();
369 if(0 == best) // not set?
370 best = candidate;
371 else if(0 != candidate) // something returned?
372 if(candidate->score() > best->score()) // beaten?
373 best = candidate;
374 }
375 set_generation(_generation+1);
376 if(metrics) {
377 time(&time_end);
378 }
379 // metrics?
380 if(metrics) {
381 cout << "generation " << _generation-1 << " took " << (time_end-time_start) << "s "
382 << "(" << (time_fight_start-time_start) << "s, " << (time_fight_end-time_fight_start) << "s, "
383 << (time_end-time_fight_end) << "s) (";
384 if((rounds > 0) && ((time_fight_end-time_fight_start) > 0))
385 cout << rounds << "r, " << (rounds/(time_fight_end-time_fight_start)) << "r/s) ";
386 else
387 cout << "no rounds) ";
388 if(0 != best) { // have something?
389 cout << best->species()->name() << ':' << best->uid() << " scores " << fixed << setprecision(1) << best->score() << endl;
390 best->rc(cout);
391 } else {
392 cout << "no best?!?!" << endl;
393 }
394 }
395 }
396
warrior(const CWarrior::TUid uid) const397 CWarrior *CKingdom::warrior(const CWarrior::TUid uid) const { /* fetch a particular warrior from this kingdom; NULL if not present */
398 int i;
399 CWarrior *ret = 0;
400 for(i=0; (i < _num_genera) && (0 == ret); ++i) {
401 ret = _genus[i]->warrior(uid);
402 }
403 return ret;
404 }
405
alloc_mars()406 void CKingdom::alloc_mars() {
407 if(_mars_prepared) return;
408 // allocate core
409 _core = sim_alloc_bufs2(2, _coresize, _processes, _cycles, _pspacesize);
410 if(0 == _core) PANIC(MARS_ALLOC_ERROR,NULL,NULL);
411 // done
412 _mars_prepared = true;
413 }
414
dealloc_mars()415 void CKingdom::dealloc_mars() {
416 if(!_mars_prepared) return;
417 // deallocate core
418 sim_free_bufs();
419 // done
420 _mars_prepared = false;
421 }
422
core_check() const423 void CKingdom::core_check() const {
424 int i;
425 if(safety_checks) {
426 for(i=0; i<_coresize; ++i) {
427 if(!_core[i].valid())
428 PANIC(MARS,"bad core",NULL);
429 }
430 }
431 }
432
433 //*** array index accessors
434
genus(unsigned index) const435 CGenus &CKingdom::genus(unsigned index) const {
436 if(index >= _num_genera)
437 PANIC(MISC,"illegal CKingdom genus index",NULL);
438 return *_genus[index];
439 }
440
441