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 "operand_table.hpp"
20 #include "species.hpp"
21 
22 #include "opcode_branch_lookup.hpp"
23 
24 #include <fstream>
25 using namespace std;
26 
27 /*************** COperandBin implementation ******************************/
28 
COperandBin(const unsigned coresize)29 COperandTable::COperandBin::COperandBin(const unsigned coresize) {
30 	_coresize = coresize;
31 	// work out how many bins
32 	_bins = 1;
33 	for(unsigned i=1; i<(_coresize/2); i <<= 1) {
34 		_bins++;
35 	}
36 	// and actually assign them
37 	_bin = new unsigned[_bins];
38 	for(unsigned i=0; i<_bins-1; i++) {
39 		_bin[i] = 1 << i;
40 	}
41 	_bin[_bins-1] = _coresize/2;
42 }
43 
bin(const unsigned idx) const44 unsigned COperandTable::COperandBin::bin(const unsigned idx) const { // get the max of this bin
45 	if(safety_checks)
46 		if(idx < 0 || idx >= _bins)
47 			PANIC(MISC,"invalid bin index",NULL);
48 	return _bin[idx];
49 }
50 
get(const unsigned operand) const51 unsigned COperandTable::COperandBin::get(const unsigned operand) const { // index of bin this operand falls into
52 	const unsigned op = (operand < (_coresize/2)) ? operand: (_coresize - operand); // fold, discard sign
53 	for(unsigned i=0; i<_bins; i++) {
54 		if(op <= _bin[i])
55 			return i;
56 	}
57 	PANIC(MISC,"Bad bin for operand",NULL);
58 	return (_bins-1); // dumb compiler!
59 }
60 
dump(ostream & out) const61 void COperandTable::COperandBin::dump(ostream &out) const {
62 	out << "there are " << _bins << " bins: ";
63 	for(unsigned i=0; i<_bins; i++) {
64 		out << _bin[i] << ' ';
65 	}
66 	out << endl;
67 }
68 
69 /*************** COperandEntry implementation *****************************/
70 
COperandEntry(const COperandBin * bins)71 COperandTable::COperandEntry::COperandEntry(const COperandBin *bins): _bins(bins) {
72 	_count = 0;
73 	_bin = new unsigned[_bins->bins()];
74 	for(unsigned i=0; i<_bins->bins(); i++) {
75 		_bin[i] = 0;
76 	}
77 }
78 
COperandEntry(const COperandBin * bins,istream & in)79 COperandTable::COperandEntry::COperandEntry(const COperandBin *bins,istream &in): _bins(bins) {
80 	_count = bread_unsigned(in);
81 	_bin = new unsigned[_bins->bins()];
82 	unsigned sum = 0;
83 	for(unsigned i=0; i<_bins->bins(); i++) {
84 		_bin[i] = bread_unsigned(in);
85 		sum += _bin[i];
86 	}
87 	if(sum != _count) {
88 		PANIC(MISC,"error reading in operand prob table",NULL);
89 	}
90 }
91 
learn(const unsigned operand)92 void COperandTable::COperandEntry::learn(const unsigned operand) {
93 	_bin[_bins->get(operand)]++;
94 	_count++;
95 }
96 
save(ostream & out) const97 void COperandTable::COperandEntry::save(ostream &out) const {
98 	bwrite_unsigned(out,_count);
99 	for(unsigned i=0; i<_bins->bins(); i++)
100 		bwrite_unsigned(out,_bin[i]);
101 }
102 
dump(ostream & out) const103 void COperandTable::COperandEntry::dump(ostream &out) const {
104 	out << _count << " sampled: ";
105 	for(unsigned i=0; i<_bins->bins(); i++)
106 		out << _bin[i] << ' ';
107 	out << endl;
108 }
109 
suggest() const110 unsigned COperandTable::COperandEntry::suggest() const {
111 	const unsigned TARGET_BIN = CRand::irand(_count),
112 		CORESIZE = _bins->coresize();
113 	unsigned y = 0;
114 	for(unsigned i=0; i<_bins->bins(); i++) {
115 		y += _bin[i];
116 		if(y > TARGET_BIN) {
117 			unsigned ret;
118 			if(0 == i) {
119 				ret = CRand::irand(_bins->bin(i));
120 			} else {
121 				ret = (_bins->bin(i-1) + CRand::irand(_bins->bin(i)-_bins->bin(i-1)));
122 			}
123 			if(0 == CRand::irand(2)) // flip sign?
124 				ret = (CORESIZE - 1 - ret);
125 			return ret;
126 		}
127 	}
128 	PANIC(MISC,"roulette off end of COperandTable::COperandTable::suggest",NULL);
129 }
130 
131 /*************** COperandTable implementation *****************************/
132 
133 const char *COperandTable::FILE_KEY = "operands_file";
134 
COperandTable(const char * lookup_filename)135 COperandTable::COperandTable(const char *lookup_filename):
136 	COperand(0) {
137 	_type = TABLE;
138 	load(lookup_filename,false);
139 }
140 
COperandTable(const field_t coresize)141 COperandTable::COperandTable(const field_t coresize):
142 	COperand(coresize) {
143 	_type = TABLE;
144 	_operand_bin = new COperandBin(_coresize);
145 	for(unsigned i=0; i<OPCODE_LAST; i++) {
146 		_operand[(OPCODE)i][A] = new COperandEntry(_operand_bin);
147 		_operand[(OPCODE)i][B] = new COperandEntry(_operand_bin);
148 	}
149 }
150 
151 // actual query point
rnd(insn_t & instruction,const Field field)152 void COperandTable::rnd(insn_t &instruction,const Field field) {
153 	switch(field) {
154 		case A:
155 			instruction.a = _operand[instruction.opcode()][A]->suggest();
156 			break;
157 		case B:
158 			instruction.b = _operand[instruction.opcode()][B]->suggest();
159 			break;
160 		default:
161 			PANIC(MISC,"bad field in COperandTable::rnd()",NULL);
162 	}
163 }
164 
load(const char * lookup_filename,const bool check_constraints)165 void COperandTable::load(const char *lookup_filename,const bool check_constraints) {
166 	unsigned file_version;
167 	_filename = lookup_filename;
168 	ifstream in(lookup_filename,ifstream::binary|ifstream::in);
169 	if(!in.good())
170 		PANIC(MISC,"error openning file",lookup_filename);
171 	file_version = bread_unsigned(in);
172 	if(FILE_VERSION != file_version)
173 		PANIC(MISC,"bad file version in lookup",lookup_filename);
174 	unsigned coresize = bread_unsigned(in);
175 	if(check_constraints)
176 		if(_coresize != coresize)
177 			PANIC(MISC,"bad coresize in ",lookup_filename);
178 	_coresize = coresize;
179 	_operand_bin = new COperandBin(_coresize);
180 	for(unsigned i=0; i<OPCODE_LAST; i++) {
181 		_operand[(OPCODE)i][A] = new COperandEntry(_operand_bin,in);
182 		_operand[(OPCODE)i][B] = new COperandEntry(_operand_bin,in);
183 	}
184 	if(!in.good())
185 		PANIC(MISC,"error reading from file",lookup_filename);
186 	in.close();
187 }
188 
learn(const char * rc_filename)189 void COperandTable::learn(const char *rc_filename) {
190 	warrior_t warrior;
191 	// load it
192 	cout << "loading " << rc_filename << ".." << endl;
193 	asm_fname(rc_filename,&warrior,_coresize); // will panic fatally if can't
194 	// and analyse
195 	for(int i=0; i<warrior.len; i++) {
196 		_operand[warrior.code[i].opcode()][A]->learn(warrior.code[i].a);
197 		_operand[warrior.code[i].opcode()][B]->learn(warrior.code[i].b);
198 	}
199 }
200 
save(const char * lookup_filename)201 void COperandTable::save(const char *lookup_filename) {
202 	ofstream out(lookup_filename,ofstream::binary|ofstream::trunc);
203 	bwrite_unsigned(out,FILE_VERSION);
204 	bwrite_unsigned(out,_coresize);
205 	for(unsigned i=0; i<OPCODE_LAST; i++) {
206 		_operand[(OPCODE)i][A]->save(out);
207 		_operand[(OPCODE)i][B]->save(out);
208 	}
209 	if(!out.good())
210 		PANIC(MISC,"error writing to ",lookup_filename);
211 	out.close();
212 }
213 
dump(ostream & out)214 void COperandTable::dump(ostream &out) {
215 	_operand_bin->dump(out);
216 	for(unsigned i=0; i<OPCODE_LAST; i++) {
217 		// A operand
218 		out << MNEMONIC_OPCODE((OPCODE)i) << "-A: ";
219 		_operand[(OPCODE)i][A]->dump(out);
220 		// B operand
221 		out << MNEMONIC_OPCODE((OPCODE)i) << "-B: ";
222 		_operand[(OPCODE)i][B]->dump(out);
223 	}
224 }
225 
bwrite_u16_t(std::ostream & out,const u16_t i)226 void COperandTable::bwrite_u16_t(std::ostream &out,const u16_t i) {
227 	out.write(reinterpret_cast<const char*>(&i),sizeof(i));
228 }
229 
bwrite_unsigned(std::ostream & out,const unsigned i)230 void COperandTable::bwrite_unsigned(std::ostream &out,const unsigned i) {
231 	out.write(reinterpret_cast<const char*>(&i),sizeof(i));
232 }
233 
bread_u16_t(std::istream & in)234 u16_t COperandTable::bread_u16_t(std::istream &in) {
235 	u16_t i;
236 	in.read(reinterpret_cast<char*>(&i),sizeof(i));
237 	return i;
238 }
239 
bread_unsigned(std::istream & in)240 unsigned COperandTable::bread_unsigned(std::istream &in) {
241 	unsigned i;
242 	in.read(reinterpret_cast<char*>(&i),sizeof(i));
243 	return i;
244 }
245 
246 // and saving
write_ini(ostream & os)247 void COperandTable::write_ini(ostream &os) {
248 	os << type_key() << "=" << impl_desc(type()) << endl
249 		<< FILE_KEY << '=' << _filename << endl;
250 }
251 
write_override_ini(std::ostream & os,const COperand * parent)252 void COperandTable::write_override_ini(std::ostream &os,const COperand *parent) {
253 	if(parent == this) // nothing to do?
254 		return;
255 	write_ini(os);
256 }
257 
258 // from COperand
read_ini_impl(INIFile & ini)259 void COperandTable::read_ini_impl(INIFile &ini) {
260 	KeyValuePair *kvp;
261 	kvp = ini.get(FILE_KEY); if(0 == kvp) PANIC(MISC,FILE_KEY,"expected"); // null?!
262 	kvp->getValueAsString(_filename);
263 	load(_filename.c_str());
264 }
265 
read_override_ini_impl(INIFile & ini)266 COperand *COperandTable::read_override_ini_impl(INIFile &ini) { /* loads freq values; if any freq values are
267 	specified, then a copy of this freq will be made; all unspecified values in the new
268 	class will be "inherited" from this one, and any specified values will be overriden.  If nothing
269 	is specified, then this will be returned */
270 	string filename;
271 	COperand *ret = this;
272 	KeyValuePair *kvp;
273 	kvp = ini.get(FILE_KEY);
274 	if(0 != kvp) { // specified?
275 		kvp->getValueAsString(filename);
276 		if(_filename != filename) {
277 			ret = COperand::read_ini(ini,_coresize);
278 		}
279 	}
280 	return ret;
281 }
282