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