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