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