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