1 
2 // book.c
3 
4 // includes
5 
6 #include <errno.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 
11 #include "board.h"
12 #include "book.h"
13 #include "move.h"
14 #include "move_legal.h"
15 #include "san.h"
16 #include "util.h"
17 #include "option.h"
18 
19 // types
20 
21 typedef struct {
22    uint64 key;
23    uint16 move;
24    uint16 count;
25    uint16 n;
26    uint16 sum;
27 } entry_t;
28 
29 // variables
30 
31 static FILE * BookFile;
32 static int BookSize;
33 
34 // prototypes
35 
36 static int    find_pos      (uint64 key);
37 
38 static void   read_entry    (entry_t * entry, int n);
39 static void   write_entry   (const entry_t * entry, int n);
40 
41 static uint64 read_integer  (FILE * file, int size);
42 static void   write_integer (FILE * file, int size, uint64 n);
43 
44 // functions
45 
46 // book_clear()
47 
book_clear()48 void book_clear() {
49 
50    BookFile = NULL;
51    BookSize = 0;
52 }
53 
book_is_open()54 bool book_is_open(){
55     return BookFile!=NULL;
56 }
57 
58 // book_open()
59 
book_open(const char file_name[])60 void book_open(const char file_name[]) {
61 
62    ASSERT(file_name!=NULL);
63    if(FALSE && option_get_bool(Option,"BookLearn")){
64        BookFile = fopen(file_name,"rb+");
65    }else{
66        BookFile = fopen(file_name,"rb");
67    }
68 
69 //   if (BookFile == NULL) my_fatal("book_open(): can't open file \"%s\": %s\n",file_name,strerror(errno));
70    if (BookFile == NULL)  return;
71 
72    if (fseek(BookFile,0,SEEK_END) == -1) {
73       my_fatal("book_open(): fseek(): %s\n",strerror(errno));
74    }
75 
76    BookSize = ftell(BookFile) / 16;
77 //   if (BookSize == 0) my_fatal("book_open(): empty file\n");
78    if (BookSize == 0) {
79       book_close();
80       book_clear();
81    };
82 }
83 
84 // book_close()
85 
book_close()86 void book_close() {
87 
88    if(BookFile==NULL) return;
89 
90    if (fclose(BookFile) == EOF) {
91       my_fatal("book_close(): fclose(): %s\n",strerror(errno));
92    }
93 }
94 
95 // is_in_book()
96 
is_in_book(const board_t * board)97 bool is_in_book(const board_t * board) {
98 
99    int pos;
100    entry_t entry[1];
101 
102    if(BookFile==NULL) return FALSE;
103 
104    ASSERT(board!=NULL);
105 
106    for (pos = find_pos(board->key); pos < BookSize; pos++) {
107       read_entry(entry,pos);
108       if (entry->key == board->key) return TRUE;
109    }
110 
111    return FALSE;
112 }
113 
114 // book_move()
115 
book_move(const board_t * board,bool random)116 int book_move(const board_t * board, bool random) {
117 
118    int best_move;
119    int best_score;
120    int pos;
121    entry_t entry[1];
122    int move;
123    int score;
124    list_t list[1];
125    int i;
126 
127    if(BookFile==NULL) return MoveNone;
128 
129    ASSERT(board!=NULL);
130    ASSERT(random==TRUE||random==FALSE);
131 
132    // init
133 
134    list_clear(list);
135 
136    book_moves(list,board);
137 
138    best_move = MoveNone;
139    best_score = 0;
140    for(i=0; i<list_size(list); i++){
141 
142       move = list->move[i];
143       score = list->value[i];
144 
145       if (move != MoveNone &&
146           move_is_legal(move,board) &&
147           score>10*option_get_int(Option,"BookTreshold")) {
148 
149          // pick this move?
150 
151          ASSERT(score>0);
152 
153          if (random) {
154             best_score += score;
155             if (my_random_int(best_score) < score) best_move = move;
156          } else {
157             if (score > best_score) {
158                best_move = move;
159                best_score = score;
160             }
161          }
162 
163       } else {
164 
165          ASSERT(FALSE);
166       }
167    }
168 
169    return best_move;
170 }
171 
172 // book_moves()
173 
book_moves(list_t * list,const board_t * board)174 void book_moves(list_t * list, const board_t * board) {
175 
176    int first_pos;
177    int sum;
178    int pos;
179    entry_t entry[1];
180    int move;
181    int score;
182    char move_string[256];
183 
184    ASSERT(board!=NULL);
185    ASSERT(list!=NULL);
186 
187    if(BookFile==NULL) return;
188 
189    // null keys are reserved for the header
190    if(board->key==U64(0x0)) return;
191 
192 
193 
194 
195    // init
196 
197    list_clear(list);
198 
199    first_pos = find_pos(board->key);
200 
201    // sum
202 
203    sum = 0;
204 
205    for (pos = first_pos; pos < BookSize; pos++) {
206 
207       read_entry(entry,pos);
208       if (entry->key != board->key) break;
209 
210       sum += entry->count;
211    }
212 
213    // disp
214 
215    for (pos = first_pos; pos < BookSize; pos++) {
216 
217       read_entry(entry,pos);
218       if (entry->key != board->key) break;
219 
220       move = entry->move;
221       score = (((uint32)entry->count)*((uint32)10000))/sum;  // 32 bit safe!
222 
223       if (move != MoveNone && move_is_legal(move,board)) {
224               list_add_ex(list,move,score);
225       }
226    }
227 
228 }
229 
230 
231 // book_disp()
232 
book_disp(const board_t * board)233 void book_disp(const board_t * board) {
234 
235    char move_string[256];
236    list_t list[1];
237    int i;
238    int treshold=option_get_int(Option,"BookTreshold");
239 
240    ASSERT(board!=NULL);
241 
242    if(BookFile==NULL) return;
243 
244    book_moves(list,board);
245 
246    for(i=0; i<list_size(list); i++){
247        move_to_san(list->move[i],board,move_string,256);
248        if(list->value[i]>10*treshold){
249            printf(" %6s %5.2f%%\n",move_string,list->value[i]/100.0);
250        }else{
251            printf(" %6s %5.2f%% (below treshold %4.2f%%)\n",
252                   move_string,list->value[i]/100.0,treshold/10.0);
253        }
254    }
255    // this is necessary by the xboard protocol
256    printf("\n");
257 }
258 
259 // book_learn_move()
260 
book_learn_move(const board_t * board,int move,int result)261 void book_learn_move(const board_t * board, int move, int result) {
262 
263    int pos;
264    entry_t entry[1];
265 
266    if(BookFile==NULL) return;
267 
268    ASSERT(board!=NULL);
269    ASSERT(move_is_ok(move));
270    ASSERT(result>=-1&&result<=+1);
271 
272    ASSERT(move_is_legal(move,board));
273 
274    for (pos = find_pos(board->key); pos < BookSize; pos++) {
275 
276       read_entry(entry,pos);
277       if (entry->key != board->key) break;
278 
279       if (entry->move == move) {
280 
281          entry->n++;
282          entry->sum += result+1;
283 
284          write_entry(entry,pos);
285 
286          break;
287       }
288    }
289 }
290 
291 // book_flush()
292 
book_flush()293 void book_flush() {
294 
295    if(BookFile==NULL) return;
296 
297    if (fflush(BookFile) == EOF) {
298       my_fatal("book_flush(): fflush(): %s\n",strerror(errno));
299    }
300 }
301 
302 // find_pos()
303 
find_pos(uint64 key)304 static int find_pos(uint64 key) {
305 
306    int left, right, mid;
307    entry_t entry[1];
308 
309    // binary search (finds the leftmost entry)
310 
311    left = 0;
312    right = BookSize-1;
313 
314    ASSERT(left<=right);
315 
316    while (left < right) {
317 
318       mid = (left + right) / 2;
319       ASSERT(mid>=left&&mid<right);
320 
321       read_entry(entry,mid);
322 
323       if (key <= entry->key) {
324          right = mid;
325       } else {
326          left = mid+1;
327       }
328    }
329 
330    ASSERT(left==right);
331 
332    read_entry(entry,left);
333 
334    return (entry->key == key) ? left : BookSize;
335 }
336 
337 // read_entry()
338 
read_entry(entry_t * entry,int n)339 static void read_entry(entry_t * entry, int n) {
340 
341    ASSERT(entry!=NULL);
342    ASSERT(n>=0&&n<BookSize);
343 
344    if (fseek(BookFile,n*16,SEEK_SET) == -1) {
345       my_fatal("read_entry(): fseek(): %s\n",strerror(errno));
346    }
347 
348    entry->key   = read_integer(BookFile,8);
349    entry->move  = read_integer(BookFile,2);
350    entry->count = read_integer(BookFile,2);
351    entry->n     = read_integer(BookFile,2);
352    entry->sum   = read_integer(BookFile,2);
353 }
354 
355 // write_entry()
356 
write_entry(const entry_t * entry,int n)357 static void write_entry(const entry_t * entry, int n) {
358 
359    ASSERT(entry!=NULL);
360    ASSERT(n>=0&&n<BookSize);
361 
362    if (fseek(BookFile,n*16,SEEK_SET) == -1) {
363       my_fatal("write_entry(): fseek(): %s\n",strerror(errno));
364    }
365 
366    write_integer(BookFile,8,entry->key);
367    write_integer(BookFile,2,entry->move);
368    write_integer(BookFile,2,entry->count);
369    write_integer(BookFile,2,entry->n);
370    write_integer(BookFile,2,entry->sum);
371 }
372 
373 // read_integer()
374 
read_integer(FILE * file,int size)375 static uint64 read_integer(FILE * file, int size) {
376 
377    uint64 n;
378    int i;
379    int b;
380 
381    ASSERT(file!=NULL);
382    ASSERT(size>0&&size<=8);
383 
384    n = 0;
385 
386    for (i = 0; i < size; i++) {
387 
388       b = fgetc(file);
389 
390       if (b == EOF) {
391          if (feof(file)) {
392             my_fatal("read_integer(): fgetc(): EOF reached\n");
393          } else { // error
394             my_fatal("read_integer(): fgetc(): %s\n",strerror(errno));
395          }
396       }
397 
398       ASSERT(b>=0&&b<256);
399       n = (n << 8) | b;
400    }
401 
402    return n;
403 }
404 
405 // write_integer()
406 
write_integer(FILE * file,int size,uint64 n)407 static void write_integer(FILE * file, int size, uint64 n) {
408 
409    int i;
410    int b;
411 
412    ASSERT(file!=NULL);
413    ASSERT(size>0&&size<=8);
414    ASSERT(size==8||n>>(size*8)==0);
415 
416    for (i = size-1; i >= 0; i--) {
417 
418       b = (n >> (i*8)) & 0xFF;
419       ASSERT(b>=0&&b<256);
420 
421       if (fputc(b,file) == EOF) {
422          my_fatal("write_integer(): fputc(): %s\n",strerror(errno));
423       }
424    }
425 }
426 
427 // end of book.cpp
428 
429