/* "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 "warrior.hpp" #include "species.hpp" #include // for abs #include using namespace std; /***** CWarrior class implementation ***************/ CWarrior::CWarrior(CSpecies *species): /* create an evolving warrior stub */ _species(species), _evolving(true), _filename(""), _info("") { _mother = NONE; _mother = NONE; next_uid(); _generation = kingdom()->generation(); warrior_t::len = 0; start = 0; _fought = false; _benchmark_count = 0; create_chromosomes(); } CWarrior::CWarrior(CSpecies *species,TUid mother,TUid father): // create a new evolving warrior stub; assigns parents, assigns self a TUid */ _species(species), _evolving(true), _filename(""), _info("") { _mother = mother; _father = father; next_uid(); _generation = kingdom()->generation(); warrior_t::len = 0; start = 0; _fought = false; _benchmark_count = 0; create_chromosomes(); } CWarrior::CWarrior(CSpecies *species,const CWarrior::TUid uid): // create a new evolving warrior stub; assigns specified TUid */ _species(species), _evolving(true), _filename(""), _info("") { set_uid(uid); _generation = kingdom()->generation(); warrior_t::len = 0; start = 0; _fought = false; _benchmark_count = 0; create_chromosomes(); } CWarrior::CWarrior(CSpecies *species,const std::string &rc_filename): /* create and load a benchmark warrior */ _species(species), _evolving(false), _uid(0), _filename(rc_filename), _info("") { asm_fname(_filename.c_str(),this,_species->genus()->kingdom()->coresize()); _benchmark_count = 0; _chromosome = 0; } CWarrior::~CWarrior() { for(unsigned i=0; inum_chromosomes(); i++) delete _chromosome[i]; delete[] _chromosome; } void CWarrior::create_chromosomes() { _chromosome = new CChromosome*[species()->num_chromosomes()]; for(unsigned int i=0; inum_chromosomes(); i++) { _chromosome[i] = new CChromosome(this,i); } } CChromosome *CWarrior::chromosome(unsigned int i) const { if(safety_checks) if(i >= species()->num_chromosomes()) PANIC(MISC,"chromosome index out of bounds",NULL); return _chromosome[i]; } void CWarrior::clear() { _mother = NONE; _mother = NONE; start = 0; warrior_t::len = 0; } void CWarrior::set_start(const unsigned i) { start = i; } void CWarrior::set_uid(const CWarrior::TUid uid) { _uid = uid; _filename = "species-"; _filename += _species->name(); _filename += '-'; string_append(_filename,_uid); _filename += ".red"; } CWarrior::TUid CWarrior::next_uid() { set_uid(kingdom()->TUid_NEXT()); return _uid; } void CWarrior::calc_len() { if(is_evolving()) { CChromosome *end = _chromosome[species()->num_chromosomes()-1]; warrior_t::len = (end->_ofs + end->len()); } } const insn_t &CWarrior::code(const unsigned i) const { if(safety_checks) if(i >= len()) PANIC(MISC,"instruction index in warrior out of bounds",NULL); return warrior_t::code[i]; } bool CWarrior::is_well_formed() const { /* returns if this is *potentially* an ok warrior (basic init checks really) */ return ((len() < MAXLENGTH) && (start_idx() < len())); // remember that len and start are unsigned, so no need to do >0 checks } bool CWarrior::equals(const CWarrior &other) const { // truely equals unsigned int i; if(_uid != other._uid) return false; if(len() != other.len()) return false; if(start != other.start) return false; for(i=0; iminsep(), /* closest enemy can be */ rangepos = kingdom()->coresize() - startpos - kingdom()->minsep() - enemy.len(), // range for random start pos rounds = kingdom()->rounds(); int i, pos; // position of next warrior // some info if(verbose) { // debug output cout << "\t\tfight: " << _uid << " vs "; if(enemy.is_bench()) cout << enemy.filename() << endl; else cout << enemy.uid() << endl; } // fight? if(fight) { // prepare shortcut evalutation //TODO: make this configurable const int ROUND_STAGES = 4; const float ROUNDS_SHORTCUT_THRESHOLD = 0.20F; int ROUND[ROUND_STAGES], r; for(r=0; rcore_check(); switch(sim( 2,start,(enemy.start+pos)/*%kingdom()->coresize()*/,kingdom()->cycles(),0)) { case 0: // we win ++data.win; break; case 1: // we lose ++data.lose; break; case 2: // we tie ++data.tie; break; case -1: // sim panic PANIC(MARS,"sim panic in fight",NULL); default: // hmm PANIC(MARS,"unxpected return from sim",NULL); } } // we'll give up if win and lose diverge by more than n% float dist = ((float)abs(data.win-data.lose)/(float)ROUND[r]); shortcutted = (dist > ROUNDS_SHORTCUT_THRESHOLD) & (r < ROUND_STAGES-1); } if(shortcutted) { data.win = (int)round(((double)data.win / (double)ROUND[r-1]) * (double)rounds); data.tie = (int)round(((double)data.tie / (double)ROUND[r-1]) * (double)rounds); data.lose = (rounds - data.win - data.tie); // lazy!!! } // sanity if(safety_checks) { if(rounds != (data.win + data.tie + data.lose)) { PANIC(MISC,"rounds not computed correctly",NULL); } } } // and score it score(CFitness::ATTACK,CFitness::WIN,enemy,data.win); enemy.score(CFitness::DEFENCE,CFitness::LOSE,*this,data.win); score(CFitness::ATTACK,CFitness::LOSE,enemy,data.lose); enemy.score(CFitness::DEFENCE,CFitness::WIN,*this,data.lose); score(CFitness::ATTACK,CFitness::TIE,enemy,data.tie); enemy.score(CFitness::DEFENCE,CFitness::TIE,*this,data.tie); // done if(verbose) { // debug output cout << "\t\t\t" << rounds << " fights (" << data.win << "," << data.tie << "," << data.lose << ") " << _fitness_score; if(!fight) { cout << " (RESUMED)"; } cout << endl; } return rounds; } int CWarrior::fight(const CWarrior &bench,const int rounds,const int win,const int tie,const int lose) { /* returns score */ const int startpos = len() + kingdom()->minsep(), /* closest enemy can be */ rangepos = kingdom()->coresize() - startpos - kingdom()->minsep() - bench.len(); // range for random start pos int score = 0, // the result round, // the round number pos; // position of next warrior // ready core sim_reset_pspaces(); if(verbose) { // debug output cout << "\t\tbench-fight: " << _uid << " vs "; if(bench.is_bench()) cout << bench.filename(); else cout << bench.uid(); cout << " .. "; } // do it for(round=rounds; round>0; --round) { pos = (startpos + CRand::irand(rangepos)); sim_clear_core(); load(0); bench.load(pos); kingdom()->core_check(); switch(sim( 2,start,(bench.start+pos)%kingdom()->coresize(),kingdom()->cycles(),0)) { case 0: // we win score += win; break; case 1: // we lose score += lose; break; case 2: // we tie score += tie; break; case -1: // sim panic PANIC(MARS,"sim panic in fight",NULL); default: // hmm PANIC(MARS,"unxpected return from sim",NULL); } } if(verbose) { // debug output cout << "\t\t\tscored " << score << endl; } return score; } void CWarrior::ready() { /* this will blank all fitness metrics preceeding the fighting of a generation */ _fought = false; species()->fitness()->zero(_fitness_score); } void CWarrior::score(const CFitness::CAT cat,const CFitness::RESULT res,const CWarrior &enemy,int rounds) { /* this will adjust the fitness metrics to account for this result */ if(_evolving) { // ignore fitness for benchmarking warriors species()->fitness()->score(cat,res,enemy.species()->fitness_weighting(),_fitness_score,rounds); } } void CWarrior::code_check() const { unsigned int i; if(safety_checks) { for(i=0; icoresize()); os << linebuf << endl; } os << indent << "END" << endl; } void CWarrior::red(ostream &os,const char *note) { // prints a headed .red file to this stream os << ';' << kingdom()->hill() << endl << ";name " << species()->name() << "-" << uid() << endl << ";author " << kingdom()->author() << endl << ";email " << kingdom()->email() << endl << ";generation " << generation() << endl << ";strategy made with Species by Will Varfar" << endl << ";strategy http://redcoder.sourceforge.net" << endl << ";strategy This is an evolved warrior" << endl; if(0 != note) os << note << endl; if(NONE != _mother) os << ";mother " << _mother << endl; if(NONE != _father) os << ";father " << _father << endl; if(0 < info().length()) os << ";info " << info() << endl; os << ";assert (CORESIZE==" << kingdom()->coresize() << ")" << endl << endl; rc(os); } CFitness::NUM CWarrior::benchmark() const { CFitness::NUM ret; if(is_benchmarked()) { ret = (CFitness::NUM)round((double)_benchmark_sum / (double)_benchmark_count); } else { species()->fitness()->zero(ret); } return ret; } void CWarrior::incorporate_benchmark(CFitness::NUM score) { // incorporate this benchmark if(is_benchmarked()) { _benchmark_sum += score; } else { _benchmark_sum = score; } _benchmark_count++; } CGenus *CWarrior::genus() const { return _species->genus(); } CKingdom *CWarrior::kingdom() const { return _species->kingdom(); } COperand *CWarrior::operands() const { return _species->operands(); } CExecTrail *CWarrior::exec_trail() const { return _exec_trail; }