/* "Species" - a CoreWars evolver. Copyright (C) 2003 'Varfar' * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 1, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "kingdom.hpp" #include #include using namespace std; /***** globals declared in species.hpp that belong here ******/ const float VERSION = 1.2F; /***** CKingdomFile class implementation *************/ CKingdomFile::CKingdomFile(): _filename("species-gen.dat") {} void CKingdomFile::writeKingdom(const CGeneration::NUM generation,const CWarrior::TUid uid) { // open file _f = fopen(_filename.c_str(),"wb"); if(0 == _f) PANIC(BAD_GEN_FILE,_filename.c_str(),"cannot open file"); // write stuff if(1 != fwrite(&generation,sizeof(CGeneration::NUM),1,_f)) PANIC(BAD_GEN_FILE,_filename.c_str(),"cannot write generation"); if(1 != fwrite(&uid,sizeof(CWarrior::TUid),1,_f)) PANIC(BAD_GEN_FILE,_filename.c_str(),"cannot write uid"); // done if(0 != fclose(_f)) PANIC(BAD_GEN_FILE,_filename.c_str(),"cannot close file"); } void CKingdomFile::readKingdom(CGeneration::NUM &generation,CWarrior::TUid &uid) { // open file _f = fopen(_filename.c_str(),"rb"); if(0 == _f) PANIC(BAD_GEN_FILE,_filename.c_str(),"cannot open file"); // read stuff if(1 != fread(&generation,sizeof(CGeneration::NUM),1,_f)) PANIC(BAD_GEN_FILE,_filename.c_str(),"cannot read generation"); if(1 != fread(&uid,sizeof(CWarrior::TUid),1,_f)) PANIC(BAD_GEN_FILE,_filename.c_str(),"cannot read uid"); // done if(0 != fclose(_f)) PANIC(BAD_GEN_FILE,_filename.c_str(),"cannot close file"); } /***** CKingdom class implementation *******************/ CKingdom::CKingdom() { // default metrics _coresize = 8000; _processes = 8000; _cycles = 80000; _rounds = 300; _mars_prepared = false; _uid = 0; set_generation(0); _in_experiment = true; _fitness = new CFitness(); _length = new CLength(); _freq = 0; // factory! _operands = COperand::NewDefault(_coresize); _reproduction = new CReproduction(); _genus = 0; // null! _num_genera = 0; _kingdom_file = new CKingdomFile(); _hill = "redcode-94"; _author = "Anon"; _email = "made@up.com"; _benchmark_genus = 0; _benchmark_species = 0; } CKingdom::~CKingdom() { clear(); delete _fitness; delete _length; delete _freq; delete _operands; delete _reproduction; delete _kingdom_file; if(0 != _benchmark_genus) delete _benchmark_genus; if(0 != _benchmark_species) delete _benchmark_species; } void CKingdom::clear() { /* clears all the genera */ int i; if(0 == _genus) // not allocated? return; for(i=0; i<_num_genera; ++i) { delete _genus[i]; } _num_genera = 0; delete[] _genus; _genus = 0; } int CKingdom::num_evolving_species() const { int i, count=0; for(i=0; i<_num_genera; i++) count += _genus[i]->num_evolving_species(); return count; } int CKingdom::num_evolved_warriors() const { int i, count=0; for(i=0; i<_num_genera; i++) count += _genus[i]->num_evolved_warriors(); return count; } int CKingdom::num_warriors() const { /* returns a trivia number of warriors 'live' in the kingdom at this time */ int i, count=0; for(i=0; i<_num_genera; i++) count += _genus[i]->num_warriors(); return count; } void CKingdom::set_generation(const CGeneration::NUM num) { _generation = num; } void CKingdom::load(const bool runnable) { bool gen0 = false; // generation 0 marker? fstream fs; INIFile *ini; clear(); // open the ini file if(verbose) { cout << "openning species.dat" << endl; } fs.clear(); fs.open("species.dat",ios::in|ios::binary); // open data file _in_experiment = fs; if(!_in_experiment) { // couldn't open it? if(verbose) { cout << "species.dat failed; trying species.ini" << endl; } fs.close(); fs.clear(); fs.open("species.ini",ios::in|ios::binary); if(!fs) { PANIC(CANNOT_OPEN_INI,"species.ini is missing",NULL); } gen0 = true; _generation = 0; _in_experiment = runnable; } else { // load kingdom file _kingdom_file->readKingdom(_generation,_uid); } // load it ini = new INIFile(fs); read_ini(*ini); delete ini; fs.close(); // init? if(gen0 && _in_experiment) { if(verbose) { cout << "writing species.dat" << endl; } fs.open("species.dat",ios::out|ios::trunc); write_ini(fs); fs.close(); _kingdom_file->writeKingdom(_generation,_uid); } } void CKingdom::save() { int i; for(i=0; i<_num_genera; ++i) { _genus[i]->save(); } _kingdom_file->writeKingdom(_generation,_uid); } void CKingdom::read_ini(INIFile &ini) { KeyValuePair *kvp; int i; bool has_benchmark = false; string num, benchmark; // forget memory! clear(); dealloc_mars(); // go to correct section if(!ini.seek("kingdom")) PANIC(MISC,"[kingdom] section expected",NULL); // load the core metrics kvp = ini.get("hill"); if(0 != kvp) kvp->getValueAsString(_hill); kvp = ini.get("author"); if(0 != kvp) kvp->getValueAsString(_author); kvp = ini.get("email"); if(0 != kvp) kvp->getValueAsString(_email); kvp = ini.get("coresize"); if(0 == kvp) PANIC(MISC,"coresize expected",NULL); _coresize = kvp->getValueAsInt(); kvp = ini.get("processes"); if(0 == kvp) PANIC(MISC,"processes expected",NULL); _processes = kvp->getValueAsInt(); kvp = ini.get("cycles"); if(0 == kvp) PANIC(MISC,"cycles expected",NULL); _cycles = kvp->getValueAsInt(); kvp = ini.get("minsep"); if(0 == kvp) PANIC(MISC,"minsep expected",NULL); _minsep = kvp->getValueAsInt(); kvp = ini.get("pspacesize"); if(0 == kvp) PANIC(MISC,"pspacesize expected",NULL); _pspacesize = kvp->getValueAsInt(); alloc_mars(); // load benchmark? kvp = ini.get("benchmark"); if(0 != kvp) { kvp->getValueAsString(benchmark); kvp = ini.get("benchmark_n"); if(0 == kvp) PANIC(MISC,"benchmark_n expected",NULL); _benchmark_n = kvp->getValueAsFloat(); if(0.0F >= _benchmark_n) PANIC(MISC,"benchmark_n cannot be 0.0 or less",NULL); kvp = ini.get("benchmark_candidate"); if(0 == kvp) PANIC(MISC,"benchmark_candidate expected",NULL); _benchmark_candidate = kvp->getValueAsInt(); if(0 >= _benchmark_candidate) PANIC(MISC,"benchmark_candidate cannot be 0 or less",NULL); has_benchmark = true; } // how many rounds are battles measured upon? must be specified kvp = ini.get("rounds"); if(0 == kvp) PANIC(MISC,"rounds expected",NULL); _rounds = kvp->getValueAsInt(); // load the state of evolution so far; if not specified, initial defaults assumed //kvp = ini.get("generation"); if(0 == kvp) _generation = 0; else _generation = kvp->getValueAsInt(); //kvp = ini.get("uid"); if(0 == kvp) { if(0 != _generation) PANIC(MISC,"uid expected"); else _uid = 0; } else _uid = kvp->getValueAsInt(); // init some defaults _fitness->read_ini(ini); _length->read_ini(ini); _freq = CInstGenerator::read_ini(ini); delete _operands; _operands = COperand::read_ini(ini,_coresize); _reproduction->read_ini(ini); // how many genera do we have? kvp = ini.get("num_genera"); if(0 == kvp) PANIC(MISC,"num_genera expected",NULL); _num_genera = kvp->getValueAsInt(); // load each genus name _genus = new CGenus*[_num_genera]; for(i=0; i<_num_genera; ++i) { // prepare the number of this genus as a string to search for num = ""; string_append(num,i); // and get it kvp = ini.get(num); if (0 == kvp) PANIC(MISC,"genus expected",NULL); // and create it if(verbose) { // debug output cout << "adding " << kvp->getValueAsChar() << endl; } _genus[i] = new CGenus(this,kvp->getValueAsChar()); } // actually load each genus for(i=0; i<_num_genera; ++i) { // go to the correct section if(!silent) { // debug output cout << "loading " << _genus[i]->name() << endl; } if(!ini.seek(_genus[i]->name())) { PANIC(MISC,_genus[i]->name().c_str(),"genus section not found"); } // and load it _genus[i]->read_ini(ini); } // load the benchmark? if(has_benchmark) { _benchmark_genus = new CGenus(this,"benchmark"); _benchmark_species = new CSpecies(_benchmark_genus,benchmark,false); // go to the correct section if(!silent) { // debug output cout << "loading bench-species " << _benchmark_species->name() << endl; } if(!ini.seek(_benchmark_species->name())) { PANIC(MISC,_benchmark_species->name().c_str()," benchmark-species section not found"); } // and load it _benchmark_species->read_ini(ini); } } void CKingdom::write_ini(ostream &os) { int i; os << endl << "[kingdom]" << endl << "hill=" << _hill << endl << "author=" << _author << endl << "email=" << _email << endl << "coresize=" << _coresize << endl << "processes=" << _processes << endl << "cycles=" << _cycles << endl << "rounds=" << _rounds << endl << "minsep=" << _minsep << endl << "pspacesize=" << _pspacesize << endl /* << "generation=" << _generation << endl << "uid=" << _uid << endl*/; // default stuff _fitness->write_ini(os); _length->write_ini(os); _freq->write_ini(os); _operands->write_ini(os); _reproduction->write_ini(os); // benchmark? if(0 != _benchmark_species) { os << "benchmark=" << _benchmark_species->name() << endl << "benchmark_n=" << _benchmark_n << endl << "benchmark_candidate=" << _benchmark_candidate << endl; } // list all genuses declared i=0; os << "num_genera=" << _num_genera << endl; for(i=0; i<_num_genera; ++i) { os << i << '=' << _genus[i]->name() << endl; } // dump the list of all the species declared for each genus for(i=0; i<_num_genera; ++i) { os << endl << '[' << _genus[i]->name() << ']' << endl; _genus[i]->write_ini(os); } // dump the benchmark? if(0 != _benchmark_species) { os << endl << '[' << _benchmark_species->name() << ']' << endl; _benchmark_species->write_ini(os); } } void CKingdom::fight() { /* this will actually do all the fights for a generation */ int i, rounds = 0, enemy = 1; time_t time_start, time_fight_start, time_fight_end, time_end; CWarrior *best = 0, *candidate; // check! if(!_in_experiment) PANIC(BAD_SPECIES_STATE,"kingdom cannot fight outside an experiment",NULL); // ok if(metrics) { time(&time_start); } save(); // ready all species for(i=0; i<_num_genera; ++i) { _genus[i]->ready(); } if(!silent) { cout << "fighting.." << endl; } // and fight them if(metrics) { time(&time_fight_start); } for(i=0; i<_num_genera; ++i) { // do the fight if(!silent) { // debug output cout << "fight: " << _genus[i]->name() << " vs. " << _genus[enemy]->name() << endl; } rounds += _genus[i]->fight(*_genus[enemy]); // next if(++enemy >= _num_genera) // wrap? enemy = 0; } if(metrics) { time(&time_fight_end); } if(!silent) { cout << "breeding.." << endl; } // and breed them for(i=0; i<_num_genera; ++i) { candidate = _genus[i]->selection(); if(0 == best) // not set? best = candidate; else if(0 != candidate) // something returned? if(candidate->score() > best->score()) // beaten? best = candidate; } set_generation(_generation+1); if(metrics) { time(&time_end); } // metrics? if(metrics) { cout << "generation " << _generation-1 << " took " << (time_end-time_start) << "s " << "(" << (time_fight_start-time_start) << "s, " << (time_fight_end-time_fight_start) << "s, " << (time_end-time_fight_end) << "s) ("; if((rounds > 0) && ((time_fight_end-time_fight_start) > 0)) cout << rounds << "r, " << (rounds/(time_fight_end-time_fight_start)) << "r/s) "; else cout << "no rounds) "; if(0 != best) { // have something? cout << best->species()->name() << ':' << best->uid() << " scores " << fixed << setprecision(1) << best->score() << endl; best->rc(cout); } else { cout << "no best?!?!" << endl; } } } CWarrior *CKingdom::warrior(const CWarrior::TUid uid) const { /* fetch a particular warrior from this kingdom; NULL if not present */ int i; CWarrior *ret = 0; for(i=0; (i < _num_genera) && (0 == ret); ++i) { ret = _genus[i]->warrior(uid); } return ret; } void CKingdom::alloc_mars() { if(_mars_prepared) return; // allocate core _core = sim_alloc_bufs2(2, _coresize, _processes, _cycles, _pspacesize); if(0 == _core) PANIC(MARS_ALLOC_ERROR,NULL,NULL); // done _mars_prepared = true; } void CKingdom::dealloc_mars() { if(!_mars_prepared) return; // deallocate core sim_free_bufs(); // done _mars_prepared = false; } void CKingdom::core_check() const { int i; if(safety_checks) { for(i=0; i<_coresize; ++i) { if(!_core[i].valid()) PANIC(MARS,"bad core",NULL); } } } //*** array index accessors CGenus &CKingdom::genus(unsigned index) const { if(index >= _num_genera) PANIC(MISC,"illegal CKingdom genus index",NULL); return *_genus[index]; }