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 "warrior.hpp"
20 #include "species.hpp"
21 
22 #include <stdlib.h> // for abs
23 #include <iomanip>
24 using namespace std;
25 
26 /***** CWarrior class implementation ***************/
27 
CWarrior(CSpecies * species)28 CWarrior::CWarrior(CSpecies *species): /* create an evolving warrior stub */
29 	_species(species), _evolving(true), _filename(""), _info("") {
30 	_mother = NONE;
31 	_mother = NONE;
32 	next_uid();
33 	_generation = kingdom()->generation();
34 	warrior_t::len = 0;
35 	start = 0;
36 	_fought = false;
37 	_benchmark_count = 0;
38 	create_chromosomes();
39 }
40 
CWarrior(CSpecies * species,TUid mother,TUid father)41 CWarrior::CWarrior(CSpecies *species,TUid mother,TUid father): // create a new evolving warrior stub; assigns parents, assigns self a TUid */
42 	_species(species), _evolving(true), _filename(""), _info("") {
43 	_mother = mother;
44 	_father = father;
45 	next_uid();
46 	_generation = kingdom()->generation();
47 	warrior_t::len = 0;
48 	start = 0;
49 	_fought = false;
50 	_benchmark_count = 0;
51 	create_chromosomes();
52 }
53 
CWarrior(CSpecies * species,const CWarrior::TUid uid)54 CWarrior::CWarrior(CSpecies *species,const CWarrior::TUid uid): // create a new evolving warrior stub; assigns specified TUid */
55 	_species(species), _evolving(true), _filename(""), _info("") {
56 	set_uid(uid);
57 	_generation = kingdom()->generation();
58 	warrior_t::len = 0;
59 	start = 0;
60 	_fought = false;
61 	_benchmark_count = 0;
62 	create_chromosomes();
63 }
64 
CWarrior(CSpecies * species,const std::string & rc_filename)65 CWarrior::CWarrior(CSpecies *species,const std::string &rc_filename): /* create and load a benchmark warrior */
66 	_species(species), _evolving(false), _uid(0), _filename(rc_filename), _info("") {
67 	asm_fname(_filename.c_str(),this,_species->genus()->kingdom()->coresize());
68 	_benchmark_count = 0;
69 	_chromosome = 0;
70 }
71 
~CWarrior()72 CWarrior::~CWarrior() {
73 	for(unsigned i=0; i<species()->num_chromosomes(); i++)
74 		delete _chromosome[i];
75 	delete[] _chromosome;
76 }
77 
create_chromosomes()78 void CWarrior::create_chromosomes() {
79 	_chromosome = new CChromosome*[species()->num_chromosomes()];
80 	for(unsigned int i=0; i<species()->num_chromosomes(); i++) {
81 		_chromosome[i] = new CChromosome(this,i);
82 	}
83 }
84 
chromosome(unsigned int i) const85 CChromosome *CWarrior::chromosome(unsigned int i) const {
86 	if(safety_checks)
87 		if(i >= species()->num_chromosomes())
88 			PANIC(MISC,"chromosome index out of bounds",NULL);
89 	return _chromosome[i];
90 }
91 
clear()92 void CWarrior::clear() {
93 	_mother = NONE;
94 	_mother = NONE;
95 	start = 0;
96 	warrior_t::len = 0;
97 }
98 
set_start(const unsigned i)99 void CWarrior::set_start(const unsigned i) {
100 	start = i;
101 }
102 
set_uid(const CWarrior::TUid uid)103 void CWarrior::set_uid(const CWarrior::TUid uid) {
104 	_uid = uid;
105 	_filename = "species-";
106 	_filename += _species->name();
107 	_filename += '-';
108 	string_append(_filename,_uid);
109 	_filename += ".red";
110 }
111 
next_uid()112 CWarrior::TUid CWarrior::next_uid() {
113 	set_uid(kingdom()->TUid_NEXT());
114 	return _uid;
115 }
116 
calc_len()117 void CWarrior::calc_len() {
118 	if(is_evolving()) {
119 		CChromosome *end = _chromosome[species()->num_chromosomes()-1];
120 		warrior_t::len = (end->_ofs + end->len());
121 	}
122 }
123 
code(const unsigned i) const124 const insn_t &CWarrior::code(const unsigned i) const {
125 	if(safety_checks)
126 		if(i >= len())
127 			PANIC(MISC,"instruction index in warrior out of bounds",NULL);
128 	return warrior_t::code[i];
129 }
130 
is_well_formed() const131 bool CWarrior::is_well_formed() const { /* returns if this is *potentially* an ok warrior (basic init checks really) */
132 	return ((len() < MAXLENGTH) && (start_idx() < len())); // remember that len and start are unsigned, so no need to do >0 checks
133 }
134 
equals(const CWarrior & other) const135 bool CWarrior::equals(const CWarrior &other) const { // truely equals
136 	unsigned int i;
137 	if(_uid != other._uid) return false;
138 	if(len() != other.len()) return false;
139 	if(start != other.start) return false;
140 	for(i=0; i<len(); ++i) {
141 		if((code(i).in != other.code(i).in)
142 			|| (code(i).a != other.code(i).a)
143 			|| (code(i).b != other.code(i).b)) return false;
144 	}
145 	return true; // if here, the was equal
146 }
147 
load(const int pos) const148 void CWarrior::load(const int pos) const {
149 	code_check();
150 	sim_load_warrior(pos,&(code(0)),len());
151 }
152 
fight(CWarrior & enemy,FIGHT_DATA & data,const bool fight)153 int CWarrior::fight(CWarrior &enemy,FIGHT_DATA &data,const bool fight) {
154 	const int startpos = len() + kingdom()->minsep(), /* closest enemy can be */
155 		rangepos = kingdom()->coresize() - startpos - kingdom()->minsep() - enemy.len(), // range for random start pos
156 		rounds = kingdom()->rounds();
157 	int i,
158 		pos; // position of next warrior
159 	// some info
160 	if(verbose) { // debug output
161 		cout << "\t\tfight: " << _uid << " vs ";
162 		if(enemy.is_bench())
163 			cout << enemy.filename() << endl;
164 		else
165 			cout << enemy.uid() << endl;
166 	}
167 	// fight?
168 	if(fight) {
169 		// prepare shortcut evalutation
170 		//TODO: make this configurable
171 		const int ROUND_STAGES = 4;
172 		const float ROUNDS_SHORTCUT_THRESHOLD = 0.20F;
173 		int ROUND[ROUND_STAGES], r;
174 		for(r=0; r<ROUND_STAGES-1; r++)
175 			ROUND[r] = (rounds / ROUND_STAGES) * (r+1);
176 		ROUND[ROUND_STAGES-1] = rounds;
177 		bool shortcutted = false;
178 		// clear stats
179 		data.src = uid();
180 		data.dest = enemy.uid();
181 		data.win = 0;
182 		data.lose = 0;
183 		data.tie = 0;
184 		// ready core
185 		sim_reset_pspaces();
186 		// do it
187 		i=0;
188 		for(r=0; r<ROUND_STAGES && !shortcutted; r++) {
189 			for(; i<ROUND[r]; ++i) {
190 				pos = (startpos + CRand::irand(rangepos));
191 				sim_clear_core();
192 				load(0);
193 				enemy.load(pos);
194 				kingdom()->core_check();
195 				switch(sim( 2,start,(enemy.start+pos)/*%kingdom()->coresize()*/,kingdom()->cycles(),0)) {
196 					case 0: // we win
197 						++data.win;
198 						break;
199 					case 1: // we lose
200 						++data.lose;
201 						break;
202 					case 2: // we tie
203 						++data.tie;
204 						break;
205 					case -1: // sim panic
206 						PANIC(MARS,"sim panic in fight",NULL);
207 					default: // hmm
208 						PANIC(MARS,"unxpected return from sim",NULL);
209 				}
210 			}
211 			// we'll give up if win and lose diverge by more than n%
212 			float dist = ((float)abs(data.win-data.lose)/(float)ROUND[r]);
213 			shortcutted = (dist > ROUNDS_SHORTCUT_THRESHOLD) & (r < ROUND_STAGES-1);
214 		}
215 		if(shortcutted) {
216 			data.win = (int)round(((double)data.win / (double)ROUND[r-1]) * (double)rounds);
217 			data.tie = (int)round(((double)data.tie / (double)ROUND[r-1]) * (double)rounds);
218 			data.lose = (rounds - data.win - data.tie); // lazy!!!
219 		}
220 		// sanity
221 		if(safety_checks) {
222 			if(rounds != (data.win + data.tie + data.lose)) {
223 				PANIC(MISC,"rounds not computed correctly",NULL);
224 			}
225 		}
226 	}
227 	// and score it
228 	score(CFitness::ATTACK,CFitness::WIN,enemy,data.win);
229 	enemy.score(CFitness::DEFENCE,CFitness::LOSE,*this,data.win);
230 	score(CFitness::ATTACK,CFitness::LOSE,enemy,data.lose);
231 	enemy.score(CFitness::DEFENCE,CFitness::WIN,*this,data.lose);
232 	score(CFitness::ATTACK,CFitness::TIE,enemy,data.tie);
233 	enemy.score(CFitness::DEFENCE,CFitness::TIE,*this,data.tie);
234 	// done
235 	if(verbose) { // debug output
236 		cout << "\t\t\t" << rounds << " fights (" << data.win << ","
237 			<< data.tie << "," << data.lose << ") "
238 			<< _fitness_score;
239 		if(!fight) {
240 			cout << " (RESUMED)";
241 		}
242 		cout << endl;
243 	}
244 	return rounds;
245 }
246 
fight(const CWarrior & bench,const int rounds,const int win,const int tie,const int lose)247 int CWarrior::fight(const CWarrior &bench,const int rounds,const int win,const int tie,const int lose) { /* returns score */
248 	const int startpos = len() + kingdom()->minsep(), /* closest enemy can be */
249 		rangepos = kingdom()->coresize() - startpos - kingdom()->minsep() - bench.len(); // range for random start pos
250 	int score  = 0, // the result
251 		round, // the round number
252 		pos; // position of next warrior
253 	// ready core
254 	sim_reset_pspaces();
255 	if(verbose) { // debug output
256 		cout << "\t\tbench-fight: " << _uid << " vs ";
257 		if(bench.is_bench())
258 			cout << bench.filename();
259 		else
260 			cout << bench.uid();
261 		cout << " .. ";
262 	}
263 	// do it
264 	for(round=rounds; round>0; --round) {
265 		pos = (startpos + CRand::irand(rangepos));
266 		sim_clear_core();
267 		load(0);
268 		bench.load(pos);
269 		kingdom()->core_check();
270 		switch(sim( 2,start,(bench.start+pos)%kingdom()->coresize(),kingdom()->cycles(),0)) {
271 			case 0: // we win
272 				score += win;
273 				break;
274 			case 1: // we lose
275 				score += lose;
276 				break;
277 			case 2: // we tie
278 				score += tie;
279 				break;
280 			case -1: // sim panic
281 				PANIC(MARS,"sim panic in fight",NULL);
282 			default: // hmm
283 				PANIC(MARS,"unxpected return from sim",NULL);
284 		}
285 	}
286 	if(verbose) { // debug output
287 		cout << "\t\t\tscored " << score << endl;
288 	}
289 	return score;
290 }
291 
ready()292 void CWarrior::ready() { /* this will blank all fitness metrics preceeding the fighting of a generation */
293 	_fought = false;
294 	species()->fitness()->zero(_fitness_score);
295 }
296 
297 
score(const CFitness::CAT cat,const CFitness::RESULT res,const CWarrior & enemy,int rounds)298 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 */
299 	if(_evolving) { // ignore fitness for benchmarking warriors
300 		species()->fitness()->score(cat,res,enemy.species()->fitness_weighting(),_fitness_score,rounds);
301 	}
302 }
303 
code_check() const304 void CWarrior::code_check() const {
305 	unsigned int i;
306 	if(safety_checks) {
307 		for(i=0; i<len(); ++i) {
308 			if(!code(i).valid())
309 				PANIC(MARS,"corrupt warrior",NULL);
310 		}
311 	}
312 }
313 
rc(std::ostream & os)314 void CWarrior::rc(std::ostream &os) { // prints an .rc to this stream
315 	unsigned int i;
316 	const char
317 		*indent =  "        ",
318 		*orgword = "ORG     ",
319 		*orglbl =  "START   ";
320 	char linebuf[128];
321 	os << indent << orgword << orglbl << endl;
322 	for(i=0; i<len(); i++) {
323 		if(start==i) {
324 			os << orglbl;
325 		} else {
326 			os << indent;
327 		}
328 		dis1(linebuf,code(i),kingdom()->coresize());
329 		os << linebuf << endl;
330 	}
331 	os << indent << "END" << endl;
332 }
333 
red(ostream & os,const char * note)334 void CWarrior::red(ostream &os,const char *note) { // prints a headed .red file to this stream
335 	os	<< ';' << kingdom()->hill() << endl
336 		<< ";name " << species()->name() << "-" << uid() << endl
337 		<< ";author " << kingdom()->author() << endl
338 		<< ";email " << kingdom()->email() << endl
339 		<< ";generation " << generation() << endl
340 		<< ";strategy made with Species by Will Varfar" << endl
341 		<< ";strategy http://redcoder.sourceforge.net" << endl
342 		<< ";strategy This is an evolved warrior" << endl;
343 	if(0 != note)
344 		os << note << endl;
345 	if(NONE != _mother)
346 		os << ";mother " << _mother << endl;
347 	if(NONE != _father)
348 		os << ";father " << _father << endl;
349 	if(0 < info().length())
350 		os << ";info " << info() << endl;
351 	os	<< ";assert (CORESIZE==" << kingdom()->coresize() << ")" << endl
352 		<< endl;
353 	rc(os);
354 }
355 
benchmark() const356 CFitness::NUM CWarrior::benchmark() const {
357 	CFitness::NUM ret;
358 	if(is_benchmarked()) {
359 		ret = (CFitness::NUM)round((double)_benchmark_sum / (double)_benchmark_count);
360 	} else {
361 		species()->fitness()->zero(ret);
362 	}
363 	return ret;
364 }
365 
incorporate_benchmark(CFitness::NUM score)366 void CWarrior::incorporate_benchmark(CFitness::NUM score) { // incorporate this benchmark
367 	if(is_benchmarked()) {
368 		_benchmark_sum += score;
369 	} else {
370 		_benchmark_sum = score;
371 	}
372 	_benchmark_count++;
373 }
374 
375 
genus() const376 CGenus *CWarrior::genus() const { return _species->genus(); }
kingdom() const377 CKingdom *CWarrior::kingdom() const { return _species->kingdom(); }
operands() const378 COperand *CWarrior::operands() const { return _species->operands(); }
exec_trail() const379 CExecTrail *CWarrior::exec_trail() const { return _exec_trail; }
380