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