1 /* book.cpp
2
3 GNU Chess protocol adapter
4
5 Copyright (C) 2001-2014 Free Software Foundation, Inc.
6
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21
22 // book.cpp
23
24 // includes
25
26 #include <cerrno>
27 #include <cstdio>
28 #include <cstdlib>
29 #include <cstring>
30
31 #include "board.h"
32 #include "book.h"
33 #include "move.h"
34 #include "move_legal.h"
35 #include "san.h"
36 #include "util.h"
37
38 namespace adapter {
39
40 // types
41
42 struct entry_t {
43 uint64 key;
44 uint16 move;
45 uint16 count;
46 uint16 n;
47 uint16 sum;
48 };
49
50 // variables
51
52 static FILE * BookFile;
53 static int BookSize;
54
55 // prototypes
56
57 static int find_pos (uint64 key);
58
59 static void read_entry (entry_t * entry, int n);
60 static void write_entry (const entry_t * entry, int n);
61
62 static uint64 read_integer (FILE * file, int size);
63 static void write_integer (FILE * file, int size, uint64 n);
64
65 // functions
66
67 // book_clear()
68
book_clear()69 void book_clear() {
70
71 BookFile = NULL;
72 BookSize = 0;
73 }
74
75 // book_open()
76
book_open(const char file_name[],int mode)77 void book_open(const char file_name[], int mode) {
78
79 ASSERT(file_name!=NULL);
80 ASSERT(mode==BookReadOnly || mode==BookReadWrite);
81
82 const int MaxModeLength = 4;
83 char full_file_name[MaxFileNameSize+1];
84 char file_open_mode[MaxModeLength]="";
85 FILE *bf;
86 if ( ( bf = fopen(file_name, "r") ) != NULL ) {
87 fclose(bf);
88 strcpy(full_file_name,"");
89 } else {
90 strcpy(full_file_name,compute_pkgdatadir());
91 strcat(full_file_name,"/");
92 }
93 strcat(full_file_name,file_name);
94
95 if (mode == BookReadWrite) {
96 strcpy(file_open_mode,"rb+");
97 } else {
98 strcpy(file_open_mode,"rb");
99 }
100 BookFile = fopen(full_file_name,file_open_mode);
101 if (BookFile == NULL) {
102 if (fopen(full_file_name,"rb") != NULL) {
103 fclose(bf);
104 my_fatal("book_open(): file \"%s\" is read only\n",full_file_name);
105 } else {
106 my_fatal("book_open(): can't open file \"%s\": %s\n",full_file_name,strerror(errno));
107 }
108 }
109
110 if (fseek(BookFile,0,SEEK_END) == -1) {
111 my_fatal("book_open(): fseek(): %s\n",strerror(errno));
112 }
113
114 BookSize = ftell(BookFile) / 16;
115 if (BookSize == 0) my_fatal("book_open(): empty file\n");
116 }
117
118 // book_close()
119
book_close()120 void book_close() {
121
122 if (fclose(BookFile) == EOF) {
123 my_fatal("book_close(): fclose(): %s\n",strerror(errno));
124 }
125 }
126
127 // is_in_book()
128
is_in_book(const board_t * board)129 bool is_in_book(const board_t * board) {
130
131 int pos;
132 entry_t entry[1];
133
134 ASSERT(board!=NULL);
135
136 for (pos = find_pos(board->key); pos < BookSize; pos++) {
137 read_entry(entry,pos);
138 if (entry->key == board->key) return true;
139 }
140
141 return false;
142 }
143
144 // book_move()
145
book_move(const board_t * board,bool random)146 int book_move(const board_t * board, bool random) {
147
148 int best_move;
149 int best_score;
150 int pos;
151 entry_t entry[1];
152 int move;
153 int score;
154
155 ASSERT(board!=NULL);
156 ASSERT(random==true||random==false);
157
158 best_move = MoveNone;
159 best_score = 0;
160
161 for (pos = find_pos(board->key); pos < BookSize; pos++) {
162
163 read_entry(entry,pos);
164 if (entry->key != board->key) break;
165
166 move = entry->move;
167 score = entry->count;
168
169 if (move != MoveNone && move_is_legal(move,board)) {
170
171 // pick this move?
172
173 ASSERT(score>0);
174
175 if (random) {
176 best_score += score;
177 if (my_random_int(best_score) < score) best_move = move;
178 } else {
179 if (score > best_score) {
180 best_move = move;
181 best_score = score;
182 }
183 }
184
185 } else {
186
187 ASSERT(false);
188 }
189 }
190
191 return best_move;
192 }
193
194 // book_move()
195
book_move(const board_t * board,bool random,bool worst)196 int book_move(const board_t * board, bool random, bool worst) {
197
198 int worst_move;
199 int worst_score;
200 int best_move;
201 int best_score;
202 int pos;
203 entry_t entry[1];
204 int move;
205 int score;
206
207 ASSERT(board!=NULL);
208 ASSERT(random==true||random==false);
209
210 worst_move = MoveNone;
211 worst_score = 10000;
212
213 best_move = MoveNone;
214 best_score = 0;
215
216 for (pos = find_pos(board->key); pos < BookSize; pos++) {
217
218 read_entry(entry,pos);
219 if (entry->key != board->key) break;
220
221 move = entry->move;
222 score = entry->count;
223
224 if (move != MoveNone && move_is_legal(move,board)) {
225
226 // pick this move?
227
228 ASSERT(score>0);
229
230 if (worst) {
231 if (score < worst_score) {
232 worst_move = move;
233 worst_score = score;
234 }
235 } else if (random) {
236 best_score += score;
237 if (my_random_int(best_score) < score) best_move = move;
238 } else {
239 if (score > best_score) {
240 best_move = move;
241 best_score = score;
242 }
243 }
244
245 } else {
246
247 ASSERT(false);
248 }
249 }
250
251 if (worst) {
252 best_move = worst_move;
253 }
254
255 return best_move;
256 }
257
258 // book_disp()
259
book_disp(const board_t * board)260 void book_disp(const board_t * board) {
261
262 int first_pos;
263 int sum;
264 int pos;
265 entry_t entry[1];
266 int move;
267 int score;
268 char move_string[256];
269
270 ASSERT(board!=NULL);
271
272 first_pos = find_pos(board->key);
273
274 // sum
275
276 sum = 0;
277
278 for (pos = first_pos; pos < BookSize; pos++) {
279
280 read_entry(entry,pos);
281 if (entry->key != board->key) break;
282
283 sum += entry->count;
284 }
285
286 // disp
287
288 for (pos = first_pos; pos < BookSize; pos++) {
289
290 read_entry(entry,pos);
291 if (entry->key != board->key) break;
292
293 move = entry->move;
294 score = entry->count;
295
296 if (score > 0 && move != MoveNone && move_is_legal(move,board)) {
297 move_to_san(move,board,move_string,256);
298 printf(" %s (%.0f%%)\n",move_string,(double(score)/double(sum))*100.0);
299 }
300 }
301
302 printf("\n");
303 }
304
305 // book_learn_move()
306
book_learn_move(const board_t * board,int move,int result)307 void book_learn_move(const board_t * board, int move, int result) {
308
309 int pos;
310 entry_t entry[1];
311
312 ASSERT(board!=NULL);
313 ASSERT(move_is_ok(move));
314 ASSERT(result>=-1&&result<=+1);
315
316 ASSERT(move_is_legal(move,board));
317
318 for (pos = find_pos(board->key); pos < BookSize; pos++) {
319
320 read_entry(entry,pos);
321 if (entry->key != board->key) break;
322
323 if (entry->move == move) {
324
325 entry->n++;
326 entry->sum += result+1;
327
328 write_entry(entry,pos);
329
330 break;
331 }
332 }
333 }
334
335 // book_flush()
336
book_flush()337 void book_flush() {
338
339 if (fflush(BookFile) == EOF) {
340 my_fatal("book_flush(): fflush(): %s\n",strerror(errno));
341 }
342 }
343
344 // find_pos()
345
find_pos(uint64 key)346 static int find_pos(uint64 key) {
347
348 int left, right, mid;
349 entry_t entry[1];
350
351 // binary search (finds the leftmost entry)
352
353 left = 0;
354 right = BookSize-1;
355
356 ASSERT(left<=right);
357
358 while (left < right) {
359
360 mid = (left + right) / 2;
361 ASSERT(mid>=left&&mid<right);
362
363 read_entry(entry,mid);
364
365 if (key <= entry->key) {
366 right = mid;
367 } else {
368 left = mid+1;
369 }
370 }
371
372 ASSERT(left==right);
373
374 read_entry(entry,left);
375
376 return (entry->key == key) ? left : BookSize;
377 }
378
379 // read_entry()
380
read_entry(entry_t * entry,int n)381 static void read_entry(entry_t * entry, int n) {
382
383 ASSERT(entry!=NULL);
384 ASSERT(n>=0&&n<BookSize);
385
386 if (fseek(BookFile,n*16,SEEK_SET) == -1) {
387 my_fatal("read_entry(): fseek(): %s\n",strerror(errno));
388 }
389
390 entry->key = read_integer(BookFile,8);
391 entry->move = read_integer(BookFile,2);
392 entry->count = read_integer(BookFile,2);
393 entry->n = read_integer(BookFile,2);
394 entry->sum = read_integer(BookFile,2);
395 }
396
397 // write_entry()
398
write_entry(const entry_t * entry,int n)399 static void write_entry(const entry_t * entry, int n) {
400
401 ASSERT(entry!=NULL);
402 ASSERT(n>=0&&n<BookSize);
403
404 if (fseek(BookFile,n*16,SEEK_SET) == -1) {
405 my_fatal("write_entry(): fseek(): %s\n",strerror(errno));
406 }
407
408 write_integer(BookFile,8,entry->key);
409 write_integer(BookFile,2,entry->move);
410 write_integer(BookFile,2,entry->count);
411 write_integer(BookFile,2,entry->n);
412 write_integer(BookFile,2,entry->sum);
413 }
414
415 // read_integer()
416
read_integer(FILE * file,int size)417 static uint64 read_integer(FILE * file, int size) {
418
419 uint64 n;
420 int i;
421 int b;
422
423 ASSERT(file!=NULL);
424 ASSERT(size>0&&size<=8);
425
426 n = 0;
427
428 for (i = 0; i < size; i++) {
429
430 b = fgetc(file);
431
432 if (b == EOF) {
433 if (feof(file)) {
434 my_fatal("read_integer(): fgetc(): EOF reached\n");
435 } else { // error
436 my_fatal("read_integer(): fgetc(): %s\n",strerror(errno));
437 }
438 }
439
440 ASSERT(b>=0&&b<256);
441 n = (n << 8) | b;
442 }
443
444 return n;
445 }
446
447 // write_integer()
448
write_integer(FILE * file,int size,uint64 n)449 static void write_integer(FILE * file, int size, uint64 n) {
450
451 int i;
452 int b;
453
454 ASSERT(file!=NULL);
455 ASSERT(size>0&&size<=8);
456 ASSERT(size==8||n>>(size*8)==0);
457
458 for (i = size-1; i >= 0; i--) {
459
460 b = (n >> (i*8)) & 0xFF;
461 ASSERT(b>=0&&b<256);
462
463 if (fputc(b,file) == EOF) {
464 my_fatal("write_integer(): fputc(): %s\n",strerror(errno));
465 }
466 }
467 }
468
469 } // namespace adapter
470
471 // end of book.cpp
472
473