Add option to allow Polyglot to accept draw offers
[polyglot.git] / book.c
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
48 void book_clear() {
49
50    BookFile = NULL;
51    BookSize = 0;
52 }
53
54 bool book_is_open(){
55     return BookFile!=NULL;
56 }
57
58 // book_open()
59
60 void book_open(const char file_name[]) {
61
62    ASSERT(file_name!=NULL);
63    if(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
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
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
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
174 void book_moves(list_t * list, const board_t * board) {
175
176    int first_pos;
177    double sum;
178    int pos;
179    entry_t entry[1];
180    int move;
181    int score;
182    uint32 weight[1000]; // [HGM] assumes not more than 1000 book moves per position!
183    char move_string[256];
184
185    ASSERT(board!=NULL);
186    ASSERT(list!=NULL);
187
188    if(BookFile==NULL) return;
189
190    // init
191
192    list_clear(list);
193
194    first_pos = find_pos(board->key);
195
196    // sum
197
198    sum = 0;
199
200    for (pos = first_pos; pos < BookSize; pos++) {
201
202       read_entry(entry,pos);
203       if (entry->key != board->key) break;
204
205       weight[pos - first_pos] = 1000 * (uint32)entry->count;
206       if(option_get_bool(Option,"BookLearn")) // [HGM] improvised use of learn info
207           weight[pos - first_pos] *= ((uint32)entry->n + 10.) /((uint32)entry->sum + 1.);
208       sum += weight[pos - first_pos];
209    }
210
211    // disp
212
213    for (pos = first_pos; pos < BookSize; pos++) {
214
215       read_entry(entry,pos);
216       if (entry->key != board->key) break;
217
218       move = entry->move;
219       score = (10000.*weight[pos-first_pos])/sum;
220
221       if (move != MoveNone && move_is_legal(move,board)) {
222               list_add_ex(list,move,score);
223       }
224    }
225
226 }
227
228
229 // book_disp()
230
231 void book_disp(const board_t * board) {
232
233    char move_string[256];
234    list_t list[1];
235    int i;
236    int treshold=option_get_int(Option,"BookTreshold");
237
238    ASSERT(board!=NULL);
239
240    if(BookFile==NULL) return;
241
242    book_moves(list,board);
243    
244    for(i=0; i<list_size(list); i++){
245        move_to_san(list->move[i],board,move_string,256);
246        if(list->value[i]>10*treshold){
247            printf(" %6s %5.2f%%\n",move_string,list->value[i]/100.0);
248        }else{
249            printf(" %6s %5.2f%% (below treshold %4.2f%%)\n",
250                   move_string,list->value[i]/100.0,treshold/10.0);
251        }
252    }
253    // this is necessary by the xboard protocol
254    printf("\n");
255 }
256
257 // book_learn_move()
258
259 void book_learn_move(const board_t * board, int move, int result) {
260
261    int pos;
262    entry_t entry[1];
263
264    if(BookFile==NULL) return;
265
266    ASSERT(board!=NULL);
267    ASSERT(move_is_ok(move));
268    ASSERT(result>=-1&&result<=+1);
269
270    ASSERT(move_is_legal(move,board));
271
272    for (pos = find_pos(board->key); pos < BookSize; pos++) {
273
274       read_entry(entry,pos);
275       if (entry->key != board->key) break;
276
277       if (entry->move == move) {
278
279          entry->n++;
280          entry->sum += result+1;
281
282          write_entry(entry,pos);
283
284          break;
285       }
286    }
287 }
288
289 // book_flush()
290
291 void book_flush() {
292
293    if(BookFile==NULL) return;
294
295    if (fflush(BookFile) == EOF) {
296       my_fatal("book_flush(): fflush(): %s\n",strerror(errno));
297    }
298 }
299
300 // find_pos()
301
302 static int find_pos(uint64 key) {
303
304    int left, right, mid;
305    entry_t entry[1];
306
307    // binary search (finds the leftmost entry)
308
309    left = 0;
310    right = BookSize-1;
311
312    ASSERT(left<=right);
313
314    while (left < right) {
315
316       mid = (left + right) / 2;
317       ASSERT(mid>=left&&mid<right);
318
319       read_entry(entry,mid);
320
321       if (key <= entry->key) {
322          right = mid;
323       } else {
324          left = mid+1;
325       }
326    }
327
328    ASSERT(left==right);
329
330    read_entry(entry,left);
331
332    return (entry->key == key) ? left : BookSize;
333 }
334
335 // read_entry()
336
337 static void read_entry(entry_t * entry, int n) {
338
339    ASSERT(entry!=NULL);
340    ASSERT(n>=0&&n<BookSize);
341
342    if (fseek(BookFile,n*16,SEEK_SET) == -1) {
343       my_fatal("read_entry(): fseek(): %s\n",strerror(errno));
344    }
345
346    entry->key   = read_integer(BookFile,8);
347    entry->move  = read_integer(BookFile,2);
348    entry->count = read_integer(BookFile,2);
349    entry->n     = read_integer(BookFile,2);
350    entry->sum   = read_integer(BookFile,2);
351 }
352
353 // write_entry()
354
355 static void write_entry(const entry_t * entry, int n) {
356
357    ASSERT(entry!=NULL);
358    ASSERT(n>=0&&n<BookSize);
359
360    if (fseek(BookFile,n*16,SEEK_SET) == -1) {
361       my_fatal("write_entry(): fseek(): %s\n",strerror(errno));
362    }
363
364    write_integer(BookFile,8,entry->key);
365    write_integer(BookFile,2,entry->move);
366    write_integer(BookFile,2,entry->count);
367    write_integer(BookFile,2,entry->n);
368    write_integer(BookFile,2,entry->sum);
369 }
370
371 // read_integer()
372
373 static uint64 read_integer(FILE * file, int size) {
374
375    uint64 n;
376    int i;
377    int b;
378
379    ASSERT(file!=NULL);
380    ASSERT(size>0&&size<=8);
381
382    n = 0;
383
384    for (i = 0; i < size; i++) {
385
386       b = fgetc(file);
387
388       if (b == EOF) {
389          if (feof(file)) {
390             my_fatal("read_integer(): fgetc(): EOF reached\n");
391          } else { // error
392             my_fatal("read_integer(): fgetc(): %s\n",strerror(errno));
393          }
394       }
395
396       ASSERT(b>=0&&b<256);
397       n = (n << 8) | b;
398    }
399
400    return n;
401 }
402
403 // write_integer()
404
405 static void write_integer(FILE * file, int size, uint64 n) {
406
407    int i;
408    int b;
409
410    ASSERT(file!=NULL);
411    ASSERT(size>0&&size<=8);
412    ASSERT(size==8||n>>(size*8)==0);
413
414    for (i = size-1; i >= 0; i--) {
415
416       b = (n >> (i*8)) & 0xFF;
417       ASSERT(b>=0&&b<256);
418
419       if (fputc(b,file) == EOF) {
420          my_fatal("write_integer(): fputc(): %s\n",strerror(errno));
421       }
422    }
423 }
424
425 // end of book.cpp
426