version 1.4.37b
[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 = (entry->count*10000)/sum;  // 32 bit safe!\r
216 \r
217       if (score > 0 && 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 \r
233    ASSERT(board!=NULL);\r
234 \r
235    if(BookFile==NULL) return;\r
236 \r
237    book_moves(list,board);\r
238    \r
239    for(i=0; i<list_size(list); i++){\r
240        move_to_san(list->move[i],board,move_string,256);\r
241        printf(" %6s %5.2f%%\n",move_string,list->value[i]/100.0);\r
242    }\r
243 }\r
244 \r
245 // book_learn_move()\r
246 \r
247 void book_learn_move(const board_t * board, int move, int result) {\r
248 \r
249    int pos;\r
250    entry_t entry[1];\r
251 \r
252    if(BookFile==NULL) return;\r
253 \r
254    ASSERT(board!=NULL);\r
255    ASSERT(move_is_ok(move));\r
256    ASSERT(result>=-1&&result<=+1);\r
257 \r
258    ASSERT(move_is_legal(move,board));\r
259 \r
260    for (pos = find_pos(board->key); pos < BookSize; pos++) {\r
261 \r
262       read_entry(entry,pos);\r
263       if (entry->key != board->key) break;\r
264 \r
265       if (entry->move == move) {\r
266 \r
267          entry->n++;\r
268          entry->sum += result+1;\r
269 \r
270          write_entry(entry,pos);\r
271 \r
272          break;\r
273       }\r
274    }\r
275 }\r
276 \r
277 // book_flush()\r
278 \r
279 void book_flush() {\r
280 \r
281    if(BookFile==NULL) return;\r
282 \r
283    if (fflush(BookFile) == EOF) {\r
284       my_fatal("book_flush(): fflush(): %s\n",strerror(errno));\r
285    }\r
286 }\r
287 \r
288 // find_pos()\r
289 \r
290 static int find_pos(uint64 key) {\r
291 \r
292    int left, right, mid;\r
293    entry_t entry[1];\r
294 \r
295    // binary search (finds the leftmost entry)\r
296 \r
297    left = 0;\r
298    right = BookSize-1;\r
299 \r
300    ASSERT(left<=right);\r
301 \r
302    while (left < right) {\r
303 \r
304       mid = (left + right) / 2;\r
305       ASSERT(mid>=left&&mid<right);\r
306 \r
307       read_entry(entry,mid);\r
308 \r
309       if (key <= entry->key) {\r
310          right = mid;\r
311       } else {\r
312          left = mid+1;\r
313       }\r
314    }\r
315 \r
316    ASSERT(left==right);\r
317 \r
318    read_entry(entry,left);\r
319 \r
320    return (entry->key == key) ? left : BookSize;\r
321 }\r
322 \r
323 // read_entry()\r
324 \r
325 static void read_entry(entry_t * entry, int n) {\r
326 \r
327    ASSERT(entry!=NULL);\r
328    ASSERT(n>=0&&n<BookSize);\r
329 \r
330    if (fseek(BookFile,n*16,SEEK_SET) == -1) {\r
331       my_fatal("read_entry(): fseek(): %s\n",strerror(errno));\r
332    }\r
333 \r
334    entry->key   = read_integer(BookFile,8);\r
335    entry->move  = read_integer(BookFile,2);\r
336    entry->count = read_integer(BookFile,2);\r
337    entry->n     = read_integer(BookFile,2);\r
338    entry->sum   = read_integer(BookFile,2);\r
339 }\r
340 \r
341 // write_entry()\r
342 \r
343 static void write_entry(const entry_t * entry, int n) {\r
344 \r
345    ASSERT(entry!=NULL);\r
346    ASSERT(n>=0&&n<BookSize);\r
347 \r
348    if (fseek(BookFile,n*16,SEEK_SET) == -1) {\r
349       my_fatal("write_entry(): fseek(): %s\n",strerror(errno));\r
350    }\r
351 \r
352    write_integer(BookFile,8,entry->key);\r
353    write_integer(BookFile,2,entry->move);\r
354    write_integer(BookFile,2,entry->count);\r
355    write_integer(BookFile,2,entry->n);\r
356    write_integer(BookFile,2,entry->sum);\r
357 }\r
358 \r
359 // read_integer()\r
360 \r
361 static uint64 read_integer(FILE * file, int size) {\r
362 \r
363    uint64 n;\r
364    int i;\r
365    int b;\r
366 \r
367    ASSERT(file!=NULL);\r
368    ASSERT(size>0&&size<=8);\r
369 \r
370    n = 0;\r
371 \r
372    for (i = 0; i < size; i++) {\r
373 \r
374       b = fgetc(file);\r
375 \r
376       if (b == EOF) {\r
377          if (feof(file)) {\r
378             my_fatal("read_integer(): fgetc(): EOF reached\n");\r
379          } else { // error\r
380             my_fatal("read_integer(): fgetc(): %s\n",strerror(errno));\r
381          }\r
382       }\r
383 \r
384       ASSERT(b>=0&&b<256);\r
385       n = (n << 8) | b;\r
386    }\r
387 \r
388    return n;\r
389 }\r
390 \r
391 // write_integer()\r
392 \r
393 static void write_integer(FILE * file, int size, uint64 n) {\r
394 \r
395    int i;\r
396    int b;\r
397 \r
398    ASSERT(file!=NULL);\r
399    ASSERT(size>0&&size<=8);\r
400    ASSERT(size==8||n>>(size*8)==0);\r
401 \r
402    for (i = size-1; i >= 0; i--) {\r
403 \r
404       b = (n >> (i*8)) & 0xFF;\r
405       ASSERT(b>=0&&b<256);\r
406 \r
407       if (fputc(b,file) == EOF) {\r
408          my_fatal("write_integer(): fputc(): %s\n",strerror(errno));\r
409       }\r
410    }\r
411 }\r
412 \r
413 // end of book.cpp\r
414 \r