Add forgotten files 1.4.70b
[polyglot.git] / book_make.cpp
1 // book_make.cpp\r
2 \r
3 // includes\r
4 \r
5 #include <cerrno>\r
6 #include <cmath>\r
7 #include <cstdio>\r
8 #include <cstdlib>\r
9 #include <cstring>\r
10 \r
11 #include "board.h"\r
12 #include "book_make.h"\r
13 #include "move.h"\r
14 #include "move_do.h"\r
15 #include "move_gen.h"\r
16 #include "move_legal.h"\r
17 #include "pgn.h"\r
18 #include "san.h"\r
19 #include "util.h"\r
20 \r
21 // constants\r
22 \r
23 static const int COUNT_MAX = 16384;\r
24 static const int StringSize = 4096;\r
25 \r
26 static const int NIL = -1;\r
27 \r
28 // defines\r
29 \r
30 #define opp_search(s) ((s)==BOOK?ALL:BOOK)\r
31 \r
32 // types\r
33 \r
34 struct entry_t {\r
35     uint64 key;\r
36     uint16 move;\r
37     uint16 count;\r
38 // Unfortunately the minggw32 cross compiler [4.2.1-sjlj (mingw32-2)] \r
39 // seems to have a bug with anon structs contained in unions when using -O2.\r
40 // See the ASSERT below in "read_entry_file"...\r
41 // To be fair this seems to be illegal in C++\r
42 // although it is hard to understand why, and the compiler does not complain\r
43 // even with -Wall.\r
44 //    union {   \r
45 //        struct { \r
46             uint16 n;\r
47             uint16 sum;\r
48 //        };\r
49 //        struct {\r
50             uint8 height;\r
51             int line;\r
52 //        };\r
53         //   };\r
54     uint8 colour;\r
55 };\r
56 \r
57 struct book_t {\r
58    int size;\r
59    int alloc;\r
60    uint32 mask;\r
61    entry_t * entry;\r
62    sint32 * hash;\r
63 };\r
64 \r
65 enum search_t {\r
66     BOOK,\r
67     ALL\r
68 };\r
69 \r
70 struct info_t {\r
71     int height;\r
72     int line;\r
73     int initial_color;\r
74     bool book_trans_only;\r
75     bool extended_search;\r
76     uint16 moves[1024];\r
77     double probs[1024];\r
78     uint64 keys[1024];\r
79     FILE *output;\r
80 };\r
81 \r
82 \r
83 // variables\r
84 \r
85 static int MaxPly;\r
86 static int MinGame;\r
87 static double MinScore;\r
88 static bool RemoveWhite, RemoveBlack;\r
89 static bool Uniform;\r
90 static bool Quiet=false;\r
91 \r
92 static book_t Book[1];\r
93 \r
94 // prototypes\r
95 \r
96 static void   book_clear    ();\r
97 static void   book_insert   (const char file_name[]);\r
98 static void   book_filter   ();\r
99 static void   book_sort     ();\r
100 static void   book_save     (const char file_name[]);\r
101 \r
102 static int    find_entry    (const board_t * board, int move);\r
103 static void   resize        ();\r
104 static void   halve_stats   (uint64 key);\r
105 \r
106 static bool   keep_entry    (int pos);\r
107 \r
108 static int    entry_score    (const entry_t * entry);\r
109 \r
110 static int    key_compare   (const void * p1, const void * p2);\r
111 \r
112 static void   write_integer (FILE * file, int size, uint64 n);\r
113 static uint64 read_integer(FILE * file, int size);\r
114 \r
115 static void read_entry_file(FILE *f, entry_t *entry);\r
116 static void write_entry_file(FILE * f, const entry_t * entry);\r
117 \r
118 // functions\r
119 \r
120 // book_make()\r
121 \r
122 void book_make(int argc, char * argv[]) {\r
123 \r
124    int i;\r
125    const char * pgn_file;\r
126    const char * bin_file;\r
127 \r
128    pgn_file = NULL;\r
129    my_string_set(&pgn_file,"book.pgn");\r
130 \r
131    bin_file = NULL;\r
132    my_string_set(&bin_file,"book.bin");\r
133 \r
134    MaxPly = 1024;\r
135    MinGame = 3;\r
136    MinScore = 0.0;\r
137    RemoveWhite = false;\r
138    RemoveBlack = false;\r
139    Uniform = false;\r
140 \r
141    for (i = 1; i < argc; i++) {\r
142 \r
143       if (false) {\r
144 \r
145       } else if (my_string_equal(argv[i],"make-book")) {\r
146 \r
147          // skip\r
148 \r
149       } else if (my_string_equal(argv[i],"-pgn")) {\r
150 \r
151          i++;\r
152          if (argv[i] == NULL) my_fatal("book_make(): missing argument\n");\r
153 \r
154          my_string_set(&pgn_file,argv[i]);\r
155 \r
156       } else if (my_string_equal(argv[i],"-bin")) {\r
157 \r
158          i++;\r
159          if (argv[i] == NULL) my_fatal("book_make(): missing argument\n");\r
160 \r
161          my_string_set(&bin_file,argv[i]);\r
162 \r
163       } else if (my_string_equal(argv[i],"-max-ply")) {\r
164 \r
165          i++;\r
166          if (argv[i] == NULL) my_fatal("book_make(): missing argument\n");\r
167 \r
168          MaxPly = atoi(argv[i]);\r
169          ASSERT(MaxPly>=0);\r
170 \r
171       } else if (my_string_equal(argv[i],"-min-game")) {\r
172 \r
173          i++;\r
174          if (argv[i] == NULL) my_fatal("book_make(): missing argument\n");\r
175 \r
176          MinGame = atoi(argv[i]);\r
177          ASSERT(MinGame>0);\r
178 \r
179       } else if (my_string_equal(argv[i],"-min-score")) {\r
180 \r
181          i++;\r
182          if (argv[i] == NULL) my_fatal("book_make(): missing argument\n");\r
183 \r
184          MinScore = atof(argv[i]) / 100.0;\r
185          ASSERT(MinScore>=0.0&&MinScore<=1.0);\r
186 \r
187       } else if (my_string_equal(argv[i],"-only-white")) {\r
188 \r
189          RemoveWhite = false;\r
190          RemoveBlack = true;\r
191 \r
192       } else if (my_string_equal(argv[i],"-only-black")) {\r
193 \r
194          RemoveWhite = true;\r
195          RemoveBlack = false;\r
196 \r
197       } else if (my_string_equal(argv[i],"-uniform")) {\r
198 \r
199          Uniform = true;\r
200 \r
201       } else {\r
202 \r
203          my_fatal("book_make(): unknown option \"%s\"\n",argv[i]);\r
204       }\r
205    }\r
206 \r
207    book_clear();\r
208 \r
209    printf("inserting games ...\n");\r
210    book_insert(pgn_file);\r
211 \r
212    printf("filtering entries ...\n");\r
213    book_filter();\r
214 \r
215    printf("sorting entries ...\n");\r
216    book_sort();\r
217 \r
218    printf("saving entries ...\n");\r
219    book_save(bin_file);\r
220 \r
221    printf("all done!\n");\r
222 }\r
223 \r
224 // book_clear()\r
225 \r
226 static void book_clear() {\r
227 \r
228    int index;\r
229 \r
230    Book->alloc = 1;\r
231    Book->mask = (Book->alloc * 2) - 1;\r
232 \r
233    Book->entry = (entry_t *) my_malloc(Book->alloc*sizeof(entry_t));\r
234    Book->size = 0;\r
235 \r
236    Book->hash = (sint32 *) my_malloc((Book->alloc*2)*sizeof(sint32));\r
237    for (index = 0; index < Book->alloc*2; index++) {\r
238       Book->hash[index] = NIL;\r
239    }\r
240 }\r
241 \r
242 // book_insert()\r
243 \r
244 static void book_insert(const char file_name[]) {\r
245 \r
246    pgn_t pgn[1];\r
247    board_t board[1];\r
248    int ply;\r
249    int result;\r
250    char string[256];\r
251    int move;\r
252    int pos;\r
253 \r
254    ASSERT(file_name!=NULL);\r
255 \r
256    // init\r
257 \r
258    pgn->game_nb=1;\r
259    // scan loop\r
260 \r
261    pgn_open(pgn,file_name);\r
262 \r
263    while (pgn_next_game(pgn)) {\r
264 \r
265       board_start(board);\r
266       ply = 0;\r
267       result = 0;\r
268 \r
269       if (false) {\r
270       } else if (my_string_equal(pgn->result,"1-0")) {\r
271          result = +1;\r
272       } else if (my_string_equal(pgn->result,"0-1")) {\r
273          result = -1;\r
274       }\r
275 \r
276       while (pgn_next_move(pgn,string,256)) {\r
277 \r
278          if (ply < MaxPly) {\r
279 \r
280             move = move_from_san(string,board);\r
281 \r
282             if (move == MoveNone || !move_is_legal(move,board)) {\r
283                my_fatal("book_insert(): illegal move \"%s\" at line %d, column %d,game %d\n",string,pgn->move_line,pgn->move_column,pgn->game_nb);\r
284             }\r
285 \r
286             pos = find_entry(board,move);\r
287 \r
288             Book->entry[pos].n++;\r
289             Book->entry[pos].sum += result+1;\r
290 \r
291             if (Book->entry[pos].n >= COUNT_MAX) {\r
292                halve_stats(board->key);\r
293             }\r
294 \r
295             move_do(board,move);\r
296             ply++;\r
297             result = -result;\r
298          }\r
299       }\r
300           pgn->game_nb++;\r
301       if (pgn->game_nb % 10000 == 0) printf("%d games ...\n",pgn->game_nb);\r
302    }\r
303 \r
304    pgn_close(pgn);\r
305 \r
306    printf("%d game%s.\n",pgn->game_nb,(pgn->game_nb>2)?"s":"");\r
307    printf("%d entries.\n",Book->size);\r
308 \r
309    return;\r
310 }\r
311 \r
312 // book_filter()\r
313 \r
314 static void book_filter() {\r
315 \r
316    int src, dst;\r
317 \r
318    // entry loop\r
319 \r
320    dst = 0;\r
321 \r
322    for (src = 0; src < Book->size; src++) {\r
323       if (keep_entry(src)) Book->entry[dst++] = Book->entry[src];\r
324    }\r
325 \r
326    ASSERT(dst>=0&&dst<=Book->size);\r
327    Book->size = dst;\r
328 \r
329    printf("%d entries.\n",Book->size);\r
330 }\r
331 \r
332 // book_sort()\r
333 \r
334 static void book_sort() {\r
335 \r
336    // sort keys for binary search\r
337 \r
338    qsort(Book->entry,Book->size,sizeof(entry_t),&key_compare);\r
339 }\r
340 \r
341 // book_save()\r
342 \r
343 static void book_save(const char file_name[]) {\r
344 \r
345    FILE * file;\r
346    int pos;\r
347 \r
348    ASSERT(file_name!=NULL);\r
349 \r
350    file = fopen(file_name,"wb");\r
351    if (file == NULL) my_fatal("book_save(): can't open file \"%s\" for writing: %s\n",file_name,strerror(errno));\r
352 \r
353    // entry loop\r
354 \r
355    for (pos = 0; pos < Book->size; pos++) {\r
356 \r
357       ASSERT(keep_entry(pos));\r
358 \r
359       write_integer(file,8,Book->entry[pos].key);\r
360       write_integer(file,2,Book->entry[pos].move);\r
361       write_integer(file,2,entry_score(&Book->entry[pos]));\r
362       write_integer(file,2,0);\r
363       write_integer(file,2,0);\r
364    }\r
365 \r
366    fclose(file);\r
367 }\r
368 \r
369 // find_entry()\r
370 \r
371 static int find_entry(const board_t * board, int move) {\r
372 \r
373    uint64 key;\r
374    int index;\r
375    int pos;\r
376 \r
377    ASSERT(board!=NULL);\r
378    ASSERT(move==MoveNone || move_is_ok(move));\r
379 \r
380    ASSERT(move==MoveNone || move_is_legal(move,board));\r
381 \r
382    // init\r
383 \r
384    key = board->key;\r
385 \r
386    // search\r
387 \r
388    for (index = key & (uint64) Book->mask; (pos=Book->hash[index]) != NIL; index = (index+1) & Book->mask) {\r
389 \r
390       ASSERT(pos>=0&&pos<Book->size);\r
391 \r
392       if (Book->entry[pos].key == key && Book->entry[pos].move == move) {\r
393          return pos; // found\r
394       }\r
395    }\r
396 \r
397    // not found\r
398 \r
399    ASSERT(Book->size<=Book->alloc);\r
400 \r
401    if (Book->size == Book->alloc) {\r
402 \r
403       // allocate more memory\r
404 \r
405       resize();\r
406 \r
407       for (index = key & (uint64) Book->mask; Book->hash[index] != NIL; index = (index+1) & Book->mask)\r
408          ;\r
409    }\r
410 \r
411    // create a new entry\r
412 \r
413    ASSERT(Book->size<Book->alloc);\r
414    pos = Book->size++;\r
415 \r
416    Book->entry[pos].key = key;\r
417    Book->entry[pos].move = move;\r
418    Book->entry[pos].n = 0;\r
419    Book->entry[pos].sum = 0;\r
420    Book->entry[pos].colour = board->turn;\r
421 \r
422    // insert into the hash table\r
423 \r
424    ASSERT(index>=0&&index<Book->alloc*2);\r
425    ASSERT(Book->hash[index]==NIL);\r
426    Book->hash[index] = pos;\r
427 \r
428    ASSERT(pos>=0&&pos<Book->size);\r
429 \r
430    return pos;\r
431 }\r
432 \r
433 // rebuild_hash_table\r
434 \r
435 static void rebuild_hash_table(){\r
436     int index,pos;\r
437     for (index = 0; index < Book->alloc*2; index++) {\r
438         Book->hash[index] = NIL;\r
439     }\r
440     for (pos = 0; pos < Book->size; pos++) {\r
441         for (index = Book->entry[pos].key & (uint64) Book->mask; Book->hash[index] != NIL; index = (index+1) & Book->mask)\r
442          ;\r
443         ASSERT(index>=0&&index<Book->alloc*2);\r
444         Book->hash[index] = pos;\r
445     }\r
446 }\r
447 \r
448 static void resize() {\r
449 \r
450    int size;\r
451 \r
452    ASSERT(Book->size==Book->alloc);\r
453 \r
454    Book->alloc *= 2;\r
455    Book->mask = (Book->alloc * 2) - 1;\r
456 \r
457    size = 0;\r
458    size += Book->alloc * sizeof(entry_t);\r
459    size += (Book->alloc*2) * sizeof(sint32);\r
460 \r
461    if (size >= 1048576) if(!Quiet){printf("allocating %gMB ...\n",double(size)/1048576.0);}\r
462 \r
463    // resize arrays\r
464 \r
465    Book->entry = (entry_t *) my_realloc(Book->entry,Book->alloc*sizeof(entry_t));\r
466    Book->hash = (sint32 *) my_realloc(Book->hash,(Book->alloc*2)*sizeof(sint32));\r
467 \r
468    // rebuild hash table\r
469 \r
470    rebuild_hash_table();\r
471 }\r
472 \r
473 \r
474 // halve_stats()\r
475 \r
476 static void halve_stats(uint64 key) {\r
477 \r
478    int index;\r
479    int pos;\r
480 \r
481    // search\r
482 \r
483    for (index = key & (uint64) Book->mask; (pos=Book->hash[index]) != NIL; index = (index+1) & Book->mask) {\r
484 \r
485       ASSERT(pos>=0&&pos<Book->size);\r
486 \r
487       if (Book->entry[pos].key == key) {\r
488          Book->entry[pos].n = (Book->entry[pos].n + 1) / 2;\r
489          Book->entry[pos].sum = (Book->entry[pos].sum + 1) / 2;\r
490       }\r
491    }\r
492 }\r
493 \r
494 // keep_entry()\r
495 \r
496 static bool keep_entry(int pos) {\r
497 \r
498    const entry_t * entry;\r
499    int colour;\r
500    double score;\r
501 \r
502    ASSERT(pos>=0&&pos<Book->size);\r
503 \r
504    entry = &Book->entry[pos];\r
505 \r
506    // if (entry->n == 0) return false;\r
507    if (entry->n < MinGame) return false;\r
508 \r
509    if (entry->sum == 0) return false;\r
510 \r
511    score = (double(entry->sum) / double(entry->n)) / 2.0;\r
512    ASSERT(score>=0.0&&score<=1.0);\r
513 \r
514    if (score < MinScore) return false;\r
515 \r
516    colour = entry->colour;\r
517 \r
518    if ((RemoveWhite && colour_is_white(colour))\r
519     || (RemoveBlack && colour_is_black(colour))) {\r
520       return false;\r
521    }\r
522 \r
523    if (entry_score(entry) == 0) return false; // REMOVE ME?\r
524 \r
525    return true;\r
526 }\r
527 \r
528 // entry_score()\r
529 \r
530 static int entry_score(const entry_t * entry) {\r
531 \r
532    int score;\r
533 \r
534    ASSERT(entry!=NULL);\r
535 \r
536    // score = entry->n; // popularity\r
537    score = entry->sum; // "expectancy"\r
538 \r
539    if (Uniform) score = 1;\r
540 \r
541    ASSERT(score>=0);\r
542 \r
543    return score;\r
544 }\r
545 \r
546 // key_compare()\r
547 \r
548 static int key_compare(const void * p1, const void * p2) {\r
549 \r
550    const entry_t * entry_1, * entry_2;\r
551 \r
552    ASSERT(p1!=NULL);\r
553    ASSERT(p2!=NULL);\r
554 \r
555    entry_1 = (const entry_t *) p1;\r
556    entry_2 = (const entry_t *) p2;\r
557 \r
558    if (entry_1->key > entry_2->key) {\r
559       return +1;\r
560    } else if (entry_1->key < entry_2->key) {\r
561       return -1;\r
562    } else {\r
563       return entry_score(entry_2) - entry_score(entry_1); // highest score first\r
564    }\r
565 }\r
566 \r
567 // write_integer()\r
568 \r
569 static void write_integer(FILE * file, int size, uint64 n) {\r
570 \r
571    int i;\r
572    int b;\r
573 \r
574    ASSERT(file!=NULL);\r
575    ASSERT(size>0&&size<=8);\r
576    ASSERT(size==8||n>>(size*8)==0);\r
577 \r
578    for (i = size-1; i >= 0; i--) {\r
579       b = (n >> (i*8)) & 0xFF;\r
580       ASSERT(b>=0&&b<256);\r
581       fputc(b,file);\r
582    }\r
583 }\r
584 \r
585 // read_integer()\r
586 \r
587 static uint64 read_integer(FILE * file, int size) {\r
588    uint64 n;\r
589    int i;\r
590    int b;\r
591    ASSERT(file!=NULL);\r
592    ASSERT(size>0&&size<=8);\r
593    n = 0;\r
594    for (i = 0; i < size; i++) {\r
595       b = fgetc(file);\r
596       if (b == EOF) {\r
597          if (feof(file)) {\r
598             my_fatal("read_integer(): fgetc(): EOF reached\n");\r
599          } else { // error\r
600             my_fatal("read_integer(): fgetc(): %s\n",strerror(errno));\r
601          }\r
602       }\r
603       ASSERT(b>=0&&b<256);\r
604       n = (n << 8) | b;\r
605    }\r
606    return n;\r
607 }\r
608 \r
609 // read_entry_file\r
610 \r
611 static void read_entry_file(FILE *f, entry_t *entry){\r
612     uint64 n;\r
613     ASSERT(entry!=NULL);\r
614     n = entry->key   = read_integer(f,8);\r
615     entry->move  = read_integer(f,2);\r
616     entry->count = read_integer(f,2);\r
617     entry->n     = read_integer(f,2);\r
618     entry->sum   = read_integer(f,2);\r
619     ASSERT(n==entry->key); // test for mingw compiler bug with anon structs\r
620 }\r
621 \r
622 // write_entry_file\r
623 \r
624 static void write_entry_file(FILE * f, const entry_t * entry) {\r
625    ASSERT(entry!=NULL);\r
626    write_integer(f,8,entry->key);\r
627    write_integer(f,2,entry->move);\r
628    write_integer(f,2,entry->count);\r
629    write_integer(f,2,entry->n);\r
630    write_integer(f,2,entry->sum);\r
631 }\r
632 \r
633 static void print_list(const board_t *board, list_t *list){\r
634     int i;\r
635     uint16 move;\r
636     char move_string[256];\r
637     for (i = 0; i < list_size(list); i++) {\r
638         move = list_move(list,i);\r
639         move_to_san(move,board,move_string,256);\r
640         printf("%s",move_string);\r
641     }\r
642     printf("\n");\r
643 }\r
644 \r
645 // book_load()\r
646 // loads a polyglot book\r
647 \r
648 static void book_load(const char filename[]){\r
649     FILE* f;\r
650     entry_t entry[1];\r
651     int size;\r
652     int i;\r
653     int pos;\r
654     int index;\r
655     ASSERT(filename!=NULL);\r
656     if(!(f=fopen(filename,"rb"))){\r
657         my_fatal("book_load() : can't open file \"%s\" for reading: %s\n",filename,strerror(errno));\r
658     }\r
659     fseek(f,0L,SEEK_END);   // superportable way to get size of book!\r
660     size=ftell(f)/16;\r
661     fseek(f,0,SEEK_SET);\r
662     for(i=0L;i<size;i++){\r
663         read_entry_file(f,entry);\r
664         ASSERT(Book->size<=Book->alloc);\r
665         if (Book->size == Book->alloc) {\r
666                 // allocate more memoryx\r
667             resize();\r
668         }\r
669             // insert into the book\r
670         pos = Book->size++;\r
671         Book->entry[pos].key = entry->key;\r
672         ASSERT(entry->move!=MoveNone);\r
673         Book->entry[pos].move = entry->move;\r
674         Book->entry[pos].count = entry->count;\r
675         Book->entry[pos].n = entry->n;\r
676         Book->entry[pos].sum = entry->sum;\r
677         Book->entry[pos].colour = ColourNone;\r
678             // find free hash table spot\r
679         for (index = entry->key & (uint64) Book->mask;\r
680              Book->hash[index] != NIL;\r
681              index = (index+1) & Book->mask);\r
682             // insert into the hash table\r
683         ASSERT(index>=0&&index<Book->alloc*2);\r
684         ASSERT(Book->hash[index]==NIL);\r
685         Book->hash[index] = pos;\r
686         ASSERT(pos>=0&&pos<Book->size);\r
687     }\r
688     fclose(f);\r
689 }\r
690 \r
691 // gen_book_moves()\r
692 // similar signature as gen_legal_moves\r
693 static int gen_book_moves(list_t * list, const board_t * board){\r
694     int first_pos, pos, index;\r
695     entry_t entry[1];\r
696     list_clear(list);\r
697     bool found;\r
698     found=FALSE;\r
699     for (index = board->key & (uint64) Book->mask; (first_pos=Book->hash[index]) != NIL; index = (index+1) & Book->mask) {\r
700         ASSERT(first_pos>=0&&first_pos<Book->size);\r
701         if (Book->entry[first_pos].key == board->key) {\r
702             found=TRUE;\r
703             break; // found\r
704         }\r
705     }\r
706     if(!found) return -1;\r
707     if(Book->entry[first_pos].move==MoveNone) return -1;\r
708     for (pos = first_pos; pos < Book->size; pos++) {\r
709         *entry=Book->entry[pos];\r
710         if (entry->key != board->key) break;\r
711         if (entry->count > 0 &&\r
712             entry->move != MoveNone &&\r
713             move_is_legal(entry->move,board)) {\r
714             list_add(list,entry->move,entry->count);\r
715         }\r
716     }\r
717     return first_pos;\r
718 }\r
719 \r
720 // gen_opp_book_moves()\r
721 // moves to which opponent has a reply in book\r
722 // similar signature as gen_legal_moves\r
723 static void gen_opp_book_moves(list_t * list, const board_t * board){\r
724     int move;\r
725     list_t new_list[1], legal_moves[1];\r
726     board_t new_board[1];\r
727     int i;\r
728     list_clear(list);\r
729     gen_legal_moves(legal_moves,board);\r
730     for (i = 0; i < list_size(legal_moves); i++) {\r
731         move = list_move(legal_moves,i);\r
732             // scratch_board\r
733         memcpy(new_board, board, sizeof(board_t));\r
734         move_do(new_board,move);\r
735         gen_book_moves(new_list,new_board); // wasteful in time but tested!\r
736         if(list_size(new_list)!=0){\r
737             list_add(list,move);\r
738         }\r
739     }\r
740 }\r
741 \r
742 static void print_moves(info_t *info){\r
743     board_t board[1];\r
744     char move_string[256];\r
745     int i;\r
746     int color=White;\r
747     if(!info->output){\r
748         return;\r
749     }\r
750     board_start(board);\r
751     for(i=0;i<info->height;i++){\r
752         if(color==White){\r
753             fprintf(info->output,"%d. ",i/2+1);\r
754             color=Black;\r
755         }else{\r
756             color=White;\r
757         }\r
758         move_to_san(info->moves[i],board,move_string,256);\r
759         fprintf(info->output,"%s", move_string);\r
760         if(color==colour_opp(info->initial_color)){\r
761             fprintf(info->output,"{%.0f%%} ",100*info->probs[i]);\r
762         }else{\r
763             fprintf(info->output," ");\r
764         }\r
765         move_do(board,info->moves[i]);\r
766     }\r
767 }\r
768 \r
769 static int search_book(board_t *board, info_t *info, search_t search){\r
770     list_t list[1];\r
771     board_t new_board[1];\r
772     uint16 move;\r
773     int count;\r
774     int ret;\r
775     int i;\r
776     int offset;\r
777     int pos;\r
778     int size;\r
779     int prob_sum;\r
780     double probs[256];\r
781     for(i=0;i<256;i++){\r
782         probs[i]=0.0;  // kill compiler warnings\r
783     }\r
784     for(i=0;i<info->height;i++){\r
785         if(board->key==info->keys[i]){\r
786             if(info->output){\r
787                 fprintf(info->output,"%d: ",info->line);\r
788                 print_moves(info);\r
789                 fprintf(info->output,"{cycle: ply=%d}\n",i);\r
790             }\r
791             info->line++;\r
792             return 1; // end of line because of cycle\r
793         }\r
794     }\r
795     if(!info->book_trans_only || (info->book_trans_only && search==BOOK)){\r
796         info->keys[info->height]=board->key;\r
797         size=Book->size;  // hack\r
798         pos=find_entry(board,MoveNone);\r
799         if(size==Book->size){\r
800             if(info->output){\r
801                 fprintf(info->output,"%d: ",info->line);\r
802                 print_moves(info);\r
803                 fprintf(info->output,"{trans: line=%d, ply=%d}\n",\r
804                         Book->entry[pos].line,\r
805                         Book->entry[pos].height);\r
806             }\r
807             info->line++;\r
808             return 1; // end of line because of transposition\r
809         }else{\r
810             Book->entry[pos].height=info->height;\r
811             Book->entry[pos].line=info->line;\r
812         }\r
813     }\r
814     count=0;\r
815     if(search==BOOK){\r
816         offset=gen_book_moves(list,board);\r
817         if(info->extended_search){\r
818             gen_legal_moves(list,board);\r
819         }\r
820 //        ASSERT(offset!=-1);\r
821         if(offset!=-1){ // only false in starting position for black book\r
822             Book->entry[offset].colour=board->turn;\r
823             prob_sum=0;\r
824             if(!info->extended_search){\r
825                 for(i=0;i<list_size(list);i++){\r
826                     prob_sum+=uint16(list_value(list,i));\r
827                 }\r
828                 for(i=0;i<list_size(list);i++){\r
829                     probs[i]=double(uint16(list_value(list,i)))/double(prob_sum);\r
830                 }\r
831             }\r
832         }\r
833     }else{\r
834         gen_opp_book_moves(list,board);\r
835     }\r
836     for (i = 0; i < list_size(list); i++) {\r
837         move = list_move(list,i);\r
838         memcpy(new_board, board, sizeof(board_t));\r
839         ASSERT(move_is_legal(move,new_board));\r
840         move_do(new_board,move);\r
841         ASSERT(search!=opp_search(search));\r
842         info->moves[info->height++]=move;\r
843         if(search==BOOK){\r
844             info->probs[info->height-1]=probs[i];\r
845         }\r
846         ret=search_book(new_board, info, opp_search(search));\r
847         if(ret==0 && search==BOOK){\r
848             if(info->output){\r
849                 fprintf(info->output,"%d: ",info->line);\r
850                 print_moves(info);\r
851                 fprintf(info->output,"\n");\r
852             }\r
853             info->line++;\r
854             ret=1; // end of line book move counts for 1\r
855         }\r
856         info->height--;\r
857         ASSERT(info->height>=0);\r
858         count+=ret;\r
859     }\r
860     return count;\r
861 }\r
862 \r
863 void init_info(info_t *info){\r
864     info->line=1;\r
865     info->height=0;\r
866     info->output=NULL;\r
867     info->initial_color=White;\r
868     info->book_trans_only=FALSE;\r
869 }\r
870 \r
871 // book_clean()\r
872 // remove MoveNone entries from book and rebuild hash table\r
873 void book_clean(){\r
874     int read_ptr,write_ptr;\r
875     write_ptr=0;\r
876     for(read_ptr=0;read_ptr<Book->size;read_ptr++){\r
877         if(Book->entry[read_ptr].move!=MoveNone){\r
878             Book->entry[write_ptr++]=Book->entry[read_ptr];\r
879         }\r
880     }\r
881     Book->size=write_ptr;\r
882     rebuild_hash_table();\r
883 }\r
884 \r
885 // book_dump()\r
886 \r
887 void book_dump(int argc, char * argv[]) {\r
888     const char * bin_file=NULL;\r
889     const char * txt_file=NULL;\r
890     char string[StringSize];\r
891     int color=ColourNone;\r
892     board_t board[1];\r
893     info_t info[1];\r
894     int i;\r
895     FILE *f;\r
896     my_string_set(&bin_file,"book.bin");\r
897     for (i = 1; i < argc; i++) {\r
898         if (false) {\r
899         } else if (my_string_equal(argv[i],"dump-book")) {\r
900                 // skip\r
901         } else if (my_string_equal(argv[i],"-bin")) {\r
902             i++;\r
903             if (i==argc) my_fatal("book_dump(): missing argument\n");\r
904             my_string_set(&bin_file,argv[i]);\r
905         } else if (my_string_equal(argv[i],"-out")) {\r
906             i++;\r
907             if (i==argc) my_fatal("book_dump(): missing argument\n");\r
908             my_string_set(&txt_file,argv[i]);\r
909         } else if (my_string_equal(argv[i],"-color") || my_string_equal(argv[i],"-colour")) {\r
910             i++;\r
911             if (i == argc) my_fatal("book_dump(): missing argument\n");\r
912             if(my_string_equal(argv[i],"white")){\r
913                 color=White;\r
914             }else if (my_string_equal(argv[i],"black")){\r
915                 color=Black;\r
916             }else{\r
917                 my_fatal("book_dump(): unknown color \"%s\"\n",argv[i]);\r
918             }\r
919         } else {\r
920             my_fatal("book_dump(): unknown option \"%s\"\n",argv[i]);\r
921         }\r
922     }\r
923     if(color==ColourNone){\r
924         my_fatal("book_dump(): you must specify a color\n");\r
925     }\r
926     if(txt_file==NULL){\r
927         snprintf(string,StringSize,"book_%s.txt",color?"white":"black");\r
928         my_string_set(&txt_file,string);\r
929     }\r
930 \r
931     book_clear();\r
932     if(!Quiet){printf("loading book ...\n");}\r
933     book_load(bin_file);\r
934     board_start(board);\r
935     init_info(info);\r
936     info->initial_color=color;\r
937     if(!(f=fopen(txt_file,"w"))){\r
938         my_fatal("book_dump(): can't open file \"%s\" for writing: %s",\r
939                  txt_file,strerror(errno));\r
940     }\r
941     info->output=f;\r
942     fprintf(info->output,"Dump of \"%s\" for %s.\n",\r
943             bin_file,color==White?"white":"black");\r
944     if(color==White){\r
945         if(!Quiet){printf("generating lines for white...\n");}\r
946         search_book(board,info, BOOK);\r
947     }else{\r
948         if(!Quiet){printf("generating lines for black...\n");}\r
949         search_book(board,info, ALL);\r
950     }\r
951 }\r
952 \r
953 // book_info()\r
954 \r
955 void book_info(int argc,char* argv[]){\r
956     const char *bin_file=NULL;\r
957     board_t board[1];\r
958     info_t info[1];\r
959     uint64 last_key;\r
960     int pos;\r
961     int white_pos,black_pos,total_pos,white_pos_extended,\r
962         black_pos_extended,white_pos_extended_diff,black_pos_extended_diff;\r
963     int s;\r
964     bool extended_search=FALSE;\r
965     int i;\r
966     Quiet=TRUE;\r
967     my_string_set(&bin_file,"book.bin");\r
968 \r
969     for (i = 1; i < argc; i++) {\r
970         if (false) {\r
971         } else if (my_string_equal(argv[i],"info-book")) {\r
972                 // skip\r
973         } else if (my_string_equal(argv[i],"-bin")) {\r
974             i++;\r
975             if (i==argc) my_fatal("book_info(): missing argument\n");\r
976             my_string_set(&bin_file,argv[i]);\r
977         } else if (my_string_equal(argv[i],"-exact")) {\r
978             extended_search=TRUE;\r
979         } else {\r
980             my_fatal("book_info(): unknown option \"%s\"\n",argv[i]);\r
981         }\r
982     }\r
983     book_clear();\r
984     if(!Quiet){printf("loading book ...\n");}\r
985     book_load(bin_file);\r
986     s=Book->size;\r
987 \r
988     board_start(board);\r
989     init_info(info);\r
990     info->book_trans_only=FALSE;\r
991     info->initial_color=White;\r
992     info->extended_search=FALSE;\r
993     search_book(board,info, BOOK);\r
994     printf("Lines for white                : %8d\n",info->line-1);\r
995 \r
996 \r
997     info->line=1;\r
998     info->height=0;\r
999     info->initial_color=Black;\r
1000     book_clean();\r
1001     ASSERT(Book->size==s);\r
1002     board_start(board);\r
1003     search_book(board,info, ALL);\r
1004     printf("Lines for black                : %8d\n",info->line-1);\r
1005 \r
1006     book_clean();\r
1007     ASSERT(Book->size==s);\r
1008     white_pos=0;\r
1009     black_pos=0;\r
1010     total_pos=0;\r
1011     last_key=0;\r
1012     for(pos=0;pos<Book->size;pos++){\r
1013         if(Book->entry[pos].key==last_key){\r
1014             ASSERT(Book->entry[pos].colour==ColourNone);\r
1015             continue;\r
1016         }\r
1017         last_key=Book->entry[pos].key;\r
1018         total_pos++;\r
1019         if(Book->entry[pos].colour==White){\r
1020             white_pos++;\r
1021         }else if(Book->entry[pos].colour==Black){\r
1022             black_pos++;\r
1023         }\r
1024     }\r
1025     printf("Positions on lines for white   : %8d\n",white_pos);\r
1026     printf("Positions on lines for black   : %8d\n",black_pos);\r
1027 \r
1028     \r
1029     if(extended_search){\r
1030         init_info(info);\r
1031         info->book_trans_only=TRUE;\r
1032         info->initial_color=White;\r
1033         info->extended_search=TRUE;\r
1034         book_clean();\r
1035         board_start(board);\r
1036         search_book(board,info, BOOK);\r
1037 \r
1038         init_info(info);\r
1039         info->book_trans_only=TRUE;\r
1040         info->initial_color=Black;\r
1041         info->extended_search=TRUE;\r
1042         book_clean();\r
1043         board_start(board);\r
1044         search_book(board,info, ALL);\r
1045         book_clean();\r
1046         ASSERT(Book->size==s);\r
1047         white_pos_extended=0;\r
1048         black_pos_extended=0;\r
1049         last_key=0;\r
1050         for(pos=0;pos<Book->size;pos++){\r
1051             if(Book->entry[pos].key==last_key){\r
1052                 ASSERT(Book->entry[pos].colour==ColourNone);\r
1053                 continue;\r
1054             }\r
1055             last_key=Book->entry[pos].key;\r
1056             if(Book->entry[pos].colour==White){\r
1057                 white_pos_extended++;\r
1058             }else if(Book->entry[pos].colour==Black){\r
1059                 black_pos_extended++;\r
1060             }\r
1061         }\r
1062         white_pos_extended_diff=white_pos_extended-white_pos;\r
1063         black_pos_extended_diff=black_pos_extended-black_pos;\r
1064         printf("Unreachable white positions(?) : %8d\n",\r
1065                white_pos_extended_diff);\r
1066         printf("Unreachable black positions(?) : %8d\n",\r
1067                black_pos_extended_diff);\r
1068 \r
1069     }\r
1070     if(extended_search){\r
1071         printf("Isolated positions             : %8d\n",\r
1072                total_pos-white_pos_extended-black_pos_extended);\r
1073     }else{\r
1074         printf("Isolated positions             : %8d\n",\r
1075                total_pos-white_pos-black_pos);\r
1076     }\r
1077 }\r
1078 \r
1079 \r
1080 \r
1081 // end of book_make.cpp\r
1082 \r