--- /dev/null
+// book_make.c\r
+\r
+// includes\r
+\r
+#include <errno.h>\r
+#include <math.h>\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+\r
+#include "board.h"\r
+#include "book_make.h"\r
+#include "move.h"\r
+#include "move_do.h"\r
+#include "move_gen.h"\r
+#include "move_legal.h"\r
+#include "pgn.h"\r
+#include "san.h"\r
+#include "util.h"\r
+\r
+// constants\r
+\r
+static const int COUNT_MAX = 16384;\r
+static const int StringSize = 4096;\r
+\r
+static const int NIL = -1;\r
+\r
+// defines\r
+\r
+#define opp_search(s) ((s)==BOOK?ALL:BOOK)\r
+\r
+// types\r
+\r
+typedef struct {\r
+ uint64 key;\r
+ uint16 move;\r
+ uint16 count;\r
+// Unfortunately the minggw32 cross compiler [4.2.1-sjlj (mingw32-2)] \r
+// seems to have a bug with anon structs contained in unions when using -O2.\r
+// See the ASSERT below in "read_entry_file"...\r
+// To be fair this seems to be illegal in C++\r
+// although it is hard to understand why, and the compiler does not complain\r
+// even with -Wall.\r
+// union { \r
+// struct { \r
+ uint16 n;\r
+ uint16 sum;\r
+// };\r
+// struct {\r
+ uint8 height;\r
+ int line;\r
+// };\r
+// };\r
+ uint8 colour;\r
+} entry_t;\r
+\r
+typedef struct {\r
+ int size;\r
+ int alloc;\r
+ uint32 mask;\r
+ entry_t * entry;\r
+ sint32 * hash;\r
+} book_t;\r
+\r
+typedef enum {\r
+ BOOK,\r
+ ALL\r
+} search_t;\r
+\r
+typedef struct {\r
+ int height;\r
+ int line;\r
+ int initial_color;\r
+ bool book_trans_only;\r
+ bool extended_search;\r
+ uint16 moves[1024];\r
+ double probs[1024];\r
+ uint64 keys[1024];\r
+ FILE *output;\r
+} info_t;\r
+\r
+\r
+// variables\r
+\r
+static int MaxPly;\r
+static int MinGame;\r
+static double MinScore;\r
+static bool RemoveWhite, RemoveBlack;\r
+static bool Uniform;\r
+static bool Quiet=FALSE;\r
+\r
+static book_t Book[1];\r
+\r
+// prototypes\r
+\r
+static void book_clear ();\r
+static void book_insert (const char file_name[]);\r
+static void book_filter ();\r
+static void book_sort ();\r
+static void book_save (const char file_name[]);\r
+\r
+static int find_entry (const board_t * board, int move);\r
+static void resize ();\r
+static void halve_stats (uint64 key);\r
+\r
+static bool keep_entry (int pos);\r
+\r
+static int entry_score (const entry_t * entry);\r
+\r
+static int key_compare (const void * p1, const void * p2);\r
+\r
+static void write_integer (FILE * file, int size, uint64 n);\r
+static uint64 read_integer(FILE * file, int size);\r
+\r
+static void read_entry_file(FILE *f, entry_t *entry);\r
+static void write_entry_file(FILE * f, const entry_t * entry);\r
+\r
+// functions\r
+\r
+// book_make()\r
+\r
+void book_make(int argc, char * argv[]) {\r
+\r
+ int i;\r
+ const char * pgn_file;\r
+ const char * bin_file;\r
+\r
+ pgn_file = NULL;\r
+ my_string_set(&pgn_file,"book.pgn");\r
+\r
+ bin_file = NULL;\r
+ my_string_set(&bin_file,"book.bin");\r
+\r
+ MaxPly = 1024;\r
+ MinGame = 3;\r
+ MinScore = 0.0;\r
+ RemoveWhite = FALSE;\r
+ RemoveBlack = FALSE;\r
+ Uniform = FALSE;\r
+\r
+ for (i = 1; i < argc; i++) {\r
+\r
+ if (FALSE) {\r
+\r
+ } else if (my_string_equal(argv[i],"make-book")) {\r
+\r
+ // skip\r
+\r
+ } else if (my_string_equal(argv[i],"-pgn")) {\r
+\r
+ i++;\r
+ if (argv[i] == NULL) my_fatal("book_make(): missing argument\n");\r
+\r
+ my_string_set(&pgn_file,argv[i]);\r
+\r
+ } else if (my_string_equal(argv[i],"-bin")) {\r
+\r
+ i++;\r
+ if (argv[i] == NULL) my_fatal("book_make(): missing argument\n");\r
+\r
+ my_string_set(&bin_file,argv[i]);\r
+\r
+ } else if (my_string_equal(argv[i],"-max-ply")) {\r
+\r
+ i++;\r
+ if (argv[i] == NULL) my_fatal("book_make(): missing argument\n");\r
+\r
+ MaxPly = atoi(argv[i]);\r
+ ASSERT(MaxPly>=0);\r
+\r
+ } else if (my_string_equal(argv[i],"-min-game")) {\r
+\r
+ i++;\r
+ if (argv[i] == NULL) my_fatal("book_make(): missing argument\n");\r
+\r
+ MinGame = atoi(argv[i]);\r
+ ASSERT(MinGame>0);\r
+\r
+ } else if (my_string_equal(argv[i],"-min-score")) {\r
+\r
+ i++;\r
+ if (argv[i] == NULL) my_fatal("book_make(): missing argument\n");\r
+\r
+ MinScore = atof(argv[i]) / 100.0;\r
+ ASSERT(MinScore>=0.0&&MinScore<=1.0);\r
+\r
+ } else if (my_string_equal(argv[i],"-only-white")) {\r
+\r
+ RemoveWhite = FALSE;\r
+ RemoveBlack = TRUE;\r
+\r
+ } else if (my_string_equal(argv[i],"-only-black")) {\r
+\r
+ RemoveWhite = TRUE;\r
+ RemoveBlack = FALSE;\r
+\r
+ } else if (my_string_equal(argv[i],"-uniform")) {\r
+\r
+ Uniform = TRUE;\r
+\r
+ } else {\r
+\r
+ my_fatal("book_make(): unknown option \"%s\"\n",argv[i]);\r
+ }\r
+ }\r
+\r
+ book_clear();\r
+\r
+ printf("inserting games ...\n");\r
+ book_insert(pgn_file);\r
+\r
+ printf("filtering entries ...\n");\r
+ book_filter();\r
+\r
+ printf("sorting entries ...\n");\r
+ book_sort();\r
+\r
+ printf("saving entries ...\n");\r
+ book_save(bin_file);\r
+\r
+ printf("all done!\n");\r
+}\r
+\r
+// book_clear()\r
+\r
+static void book_clear() {\r
+\r
+ int index;\r
+\r
+ Book->alloc = 1;\r
+ Book->mask = (Book->alloc * 2) - 1;\r
+\r
+ Book->entry = (entry_t *) my_malloc(Book->alloc*sizeof(entry_t));\r
+ Book->size = 0;\r
+\r
+ Book->hash = (sint32 *) my_malloc((Book->alloc*2)*sizeof(sint32));\r
+ for (index = 0; index < Book->alloc*2; index++) {\r
+ Book->hash[index] = NIL;\r
+ }\r
+}\r
+\r
+// book_insert()\r
+\r
+static void book_insert(const char file_name[]) {\r
+\r
+ pgn_t pgn[1];\r
+ board_t board[1];\r
+ int ply;\r
+ int result;\r
+ char string[256];\r
+ int move;\r
+ int pos;\r
+\r
+ ASSERT(file_name!=NULL);\r
+\r
+ // init\r
+\r
+ pgn->game_nb=1;\r
+ // scan loop\r
+\r
+ pgn_open(pgn,file_name);\r
+\r
+ while (pgn_next_game(pgn)) {\r
+\r
+ board_start(board);\r
+ ply = 0;\r
+ result = 0;\r
+\r
+ if (FALSE) {\r
+ } else if (my_string_equal(pgn->result,"1-0")) {\r
+ result = +1;\r
+ } else if (my_string_equal(pgn->result,"0-1")) {\r
+ result = -1;\r
+ }\r
+\r
+ while (pgn_next_move(pgn,string,256)) {\r
+\r
+ if (ply < MaxPly) {\r
+\r
+ move = move_from_san(string,board);\r
+\r
+ if (move == MoveNone || !move_is_legal(move,board)) {\r
+ 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
+ }\r
+\r
+ pos = find_entry(board,move);\r
+\r
+ Book->entry[pos].n++;\r
+ Book->entry[pos].sum += result+1;\r
+\r
+ if (Book->entry[pos].n >= COUNT_MAX) {\r
+ halve_stats(board->key);\r
+ }\r
+\r
+ move_do(board,move);\r
+ ply++;\r
+ result = -result;\r
+ }\r
+ }\r
+ pgn->game_nb++;\r
+ if (pgn->game_nb % 10000 == 0) printf("%d games ...\n",pgn->game_nb);\r
+ }\r
+\r
+ pgn_close(pgn);\r
+\r
+ printf("%d game%s.\n",pgn->game_nb,(pgn->game_nb>2)?"s":"");\r
+ printf("%d entries.\n",Book->size);\r
+\r
+ return;\r
+}\r
+\r
+// book_filter()\r
+\r
+static void book_filter() {\r
+\r
+ int src, dst;\r
+\r
+ // entry loop\r
+\r
+ dst = 0;\r
+\r
+ for (src = 0; src < Book->size; src++) {\r
+ if (keep_entry(src)) Book->entry[dst++] = Book->entry[src];\r
+ }\r
+\r
+ ASSERT(dst>=0&&dst<=Book->size);\r
+ Book->size = dst;\r
+\r
+ printf("%d entries.\n",Book->size);\r
+}\r
+\r
+// book_sort()\r
+\r
+static void book_sort() {\r
+\r
+ // sort keys for binary search\r
+\r
+ qsort(Book->entry,Book->size,sizeof(entry_t),&key_compare);\r
+}\r
+\r
+// book_save()\r
+\r
+static void book_save(const char file_name[]) {\r
+\r
+ FILE * file;\r
+ int pos;\r
+\r
+ ASSERT(file_name!=NULL);\r
+\r
+ file = fopen(file_name,"wb");\r
+ if (file == NULL) my_fatal("book_save(): can't open file \"%s\" for writing: %s\n",file_name,strerror(errno));\r
+\r
+ // entry loop\r
+\r
+ for (pos = 0; pos < Book->size; pos++) {\r
+\r
+ ASSERT(keep_entry(pos));\r
+\r
+ write_integer(file,8,Book->entry[pos].key);\r
+ write_integer(file,2,Book->entry[pos].move);\r
+ write_integer(file,2,entry_score(&Book->entry[pos]));\r
+ write_integer(file,2,0);\r
+ write_integer(file,2,0);\r
+ }\r
+\r
+ fclose(file);\r
+}\r
+\r
+// find_entry()\r
+\r
+static int find_entry(const board_t * board, int move) {\r
+\r
+ uint64 key;\r
+ int index;\r
+ int pos;\r
+\r
+ ASSERT(board!=NULL);\r
+ ASSERT(move==MoveNone || move_is_ok(move));\r
+\r
+ ASSERT(move==MoveNone || move_is_legal(move,board));\r
+\r
+ // init\r
+\r
+ key = board->key;\r
+\r
+ // search\r
+\r
+ for (index = key & (uint64) Book->mask; (pos=Book->hash[index]) != NIL; index = (index+1) & Book->mask) {\r
+\r
+ ASSERT(pos>=0&&pos<Book->size);\r
+\r
+ if (Book->entry[pos].key == key && Book->entry[pos].move == move) {\r
+ return pos; // found\r
+ }\r
+ }\r
+\r
+ // not found\r
+\r
+ ASSERT(Book->size<=Book->alloc);\r
+\r
+ if (Book->size == Book->alloc) {\r
+\r
+ // allocate more memory\r
+\r
+ resize();\r
+\r
+ for (index = key & (uint64) Book->mask; Book->hash[index] != NIL; index = (index+1) & Book->mask)\r
+ ;\r
+ }\r
+\r
+ // create a new entry\r
+\r
+ ASSERT(Book->size<Book->alloc);\r
+ pos = Book->size++;\r
+\r
+ Book->entry[pos].key = key;\r
+ Book->entry[pos].move = move;\r
+ Book->entry[pos].n = 0;\r
+ Book->entry[pos].sum = 0;\r
+ Book->entry[pos].colour = board->turn;\r
+\r
+ // insert into the hash table\r
+\r
+ ASSERT(index>=0&&index<Book->alloc*2);\r
+ ASSERT(Book->hash[index]==NIL);\r
+ Book->hash[index] = pos;\r
+\r
+ ASSERT(pos>=0&&pos<Book->size);\r
+\r
+ return pos;\r
+}\r
+\r
+// rebuild_hash_table\r
+\r
+static void rebuild_hash_table(){\r
+ int index,pos;\r
+ for (index = 0; index < Book->alloc*2; index++) {\r
+ Book->hash[index] = NIL;\r
+ }\r
+ for (pos = 0; pos < Book->size; pos++) {\r
+ for (index = Book->entry[pos].key & (uint64) Book->mask; Book->hash[index] != NIL; index = (index+1) & Book->mask)\r
+ ;\r
+ ASSERT(index>=0&&index<Book->alloc*2);\r
+ Book->hash[index] = pos;\r
+ }\r
+}\r
+\r
+static void resize() {\r
+\r
+ int size;\r
+\r
+ ASSERT(Book->size==Book->alloc);\r
+\r
+ Book->alloc *= 2;\r
+ Book->mask = (Book->alloc * 2) - 1;\r
+\r
+ size = 0;\r
+ size += Book->alloc * sizeof(entry_t);\r
+ size += (Book->alloc*2) * sizeof(sint32);\r
+\r
+ if (size >= 1048576) if(!Quiet){\r
+ printf("allocating %gMB ...\n",((double)size)/1048576.0);\r
+ }\r
+\r
+ // resize arrays\r
+\r
+ Book->entry = (entry_t *) my_realloc(Book->entry,Book->alloc*sizeof(entry_t));\r
+ Book->hash = (sint32 *) my_realloc(Book->hash,(Book->alloc*2)*sizeof(sint32));\r
+\r
+ // rebuild hash table\r
+\r
+ rebuild_hash_table();\r
+}\r
+\r
+\r
+// halve_stats()\r
+\r
+static void halve_stats(uint64 key) {\r
+\r
+ int index;\r
+ int pos;\r
+\r
+ // search\r
+\r
+ for (index = key & (uint64) Book->mask; (pos=Book->hash[index]) != NIL; index = (index+1) & Book->mask) {\r
+\r
+ ASSERT(pos>=0&&pos<Book->size);\r
+\r
+ if (Book->entry[pos].key == key) {\r
+ Book->entry[pos].n = (Book->entry[pos].n + 1) / 2;\r
+ Book->entry[pos].sum = (Book->entry[pos].sum + 1) / 2;\r
+ }\r
+ }\r
+}\r
+\r
+// keep_entry()\r
+\r
+static bool keep_entry(int pos) {\r
+\r
+ const entry_t * entry;\r
+ int colour;\r
+ double score;\r
+\r
+ ASSERT(pos>=0&&pos<Book->size);\r
+\r
+ entry = &Book->entry[pos];\r
+\r
+ // if (entry->n == 0) return FALSE;\r
+ if (entry->n < MinGame) return FALSE;\r
+\r
+ if (entry->sum == 0) return FALSE;\r
+\r
+ score = (((double)entry->sum) / ((double)entry->n)) / 2.0;\r
+ ASSERT(score>=0.0&&score<=1.0);\r
+\r
+ if (score < MinScore) return FALSE;\r
+\r
+ colour = entry->colour;\r
+\r
+ if ((RemoveWhite && colour_is_white(colour))\r
+ || (RemoveBlack && colour_is_black(colour))) {\r
+ return FALSE;\r
+ }\r
+\r
+ if (entry_score(entry) == 0) return FALSE; // REMOVE ME?\r
+\r
+ return TRUE;\r
+}\r
+\r
+// entry_score()\r
+\r
+static int entry_score(const entry_t * entry) {\r
+\r
+ int score;\r
+\r
+ ASSERT(entry!=NULL);\r
+\r
+ // score = entry->n; // popularity\r
+ score = entry->sum; // "expectancy"\r
+\r
+ if (Uniform) score = 1;\r
+\r
+ ASSERT(score>=0);\r
+\r
+ return score;\r
+}\r
+\r
+// key_compare()\r
+\r
+static int key_compare(const void * p1, const void * p2) {\r
+\r
+ const entry_t * entry_1, * entry_2;\r
+\r
+ ASSERT(p1!=NULL);\r
+ ASSERT(p2!=NULL);\r
+\r
+ entry_1 = (const entry_t *) p1;\r
+ entry_2 = (const entry_t *) p2;\r
+\r
+ if (entry_1->key > entry_2->key) {\r
+ return +1;\r
+ } else if (entry_1->key < entry_2->key) {\r
+ return -1;\r
+ } else {\r
+ return entry_score(entry_2) - entry_score(entry_1); // highest score first\r
+ }\r
+}\r
+\r
+// write_integer()\r
+\r
+static void write_integer(FILE * file, int size, uint64 n) {\r
+\r
+ int i;\r
+ int b;\r
+\r
+ ASSERT(file!=NULL);\r
+ ASSERT(size>0&&size<=8);\r
+ ASSERT(size==8||n>>(size*8)==0);\r
+\r
+ for (i = size-1; i >= 0; i--) {\r
+ b = (n >> (i*8)) & 0xFF;\r
+ ASSERT(b>=0&&b<256);\r
+ fputc(b,file);\r
+ }\r
+}\r
+\r
+// read_integer()\r
+\r
+static uint64 read_integer(FILE * file, int size) {\r
+ uint64 n;\r
+ int i;\r
+ int b;\r
+ ASSERT(file!=NULL);\r
+ ASSERT(size>0&&size<=8);\r
+ n = 0;\r
+ for (i = 0; i < size; i++) {\r
+ b = fgetc(file);\r
+ if (b == EOF) {\r
+ if (feof(file)) {\r
+ my_fatal("read_integer(): fgetc(): EOF reached\n");\r
+ } else { // error\r
+ my_fatal("read_integer(): fgetc(): %s\n",strerror(errno));\r
+ }\r
+ }\r
+ ASSERT(b>=0&&b<256);\r
+ n = (n << 8) | b;\r
+ }\r
+ return n;\r
+}\r
+\r
+// read_entry_file\r
+\r
+static void read_entry_file(FILE *f, entry_t *entry){\r
+ uint64 n;\r
+ ASSERT(entry!=NULL);\r
+ n = entry->key = read_integer(f,8);\r
+ entry->move = read_integer(f,2);\r
+ entry->count = read_integer(f,2);\r
+ entry->n = read_integer(f,2);\r
+ entry->sum = read_integer(f,2);\r
+ ASSERT(n==entry->key); // test for mingw compiler bug with anon structs\r
+}\r
+\r
+// write_entry_file\r
+\r
+static void write_entry_file(FILE * f, const entry_t * entry) {\r
+ ASSERT(entry!=NULL);\r
+ write_integer(f,8,entry->key);\r
+ write_integer(f,2,entry->move);\r
+ write_integer(f,2,entry->count);\r
+ write_integer(f,2,entry->n);\r
+ write_integer(f,2,entry->sum);\r
+}\r
+\r
+static void print_list(const board_t *board, list_t *list){\r
+ int i;\r
+ uint16 move;\r
+ char move_string[256];\r
+ for (i = 0; i < list_size(list); i++) {\r
+ move = list_move(list,i);\r
+ move_to_san(move,board,move_string,256);\r
+ printf("%s",move_string);\r
+ }\r
+ printf("\n");\r
+}\r
+\r
+// book_load()\r
+// loads a polyglot book\r
+\r
+static void book_load(const char filename[]){\r
+ FILE* f;\r
+ entry_t entry[1];\r
+ int size;\r
+ int i;\r
+ int pos;\r
+ int index;\r
+ ASSERT(filename!=NULL);\r
+ if(!(f=fopen(filename,"rb"))){\r
+ my_fatal("book_load() : can't open file \"%s\" for reading: %s\n",filename,strerror(errno));\r
+ }\r
+ fseek(f,0L,SEEK_END); // superportable way to get size of book!\r
+ size=ftell(f)/16;\r
+ fseek(f,0,SEEK_SET);\r
+ for(i=0L;i<size;i++){\r
+ read_entry_file(f,entry);\r
+ ASSERT(Book->size<=Book->alloc);\r
+ if (Book->size == Book->alloc) {\r
+ // allocate more memoryx\r
+ resize();\r
+ }\r
+ // insert into the book\r
+ pos = Book->size++;\r
+ Book->entry[pos].key = entry->key;\r
+ ASSERT(entry->move!=MoveNone);\r
+ Book->entry[pos].move = entry->move;\r
+ Book->entry[pos].count = entry->count;\r
+ Book->entry[pos].n = entry->n;\r
+ Book->entry[pos].sum = entry->sum;\r
+ Book->entry[pos].colour = ColourNone;\r
+ // find free hash table spot\r
+ for (index = entry->key & (uint64) Book->mask;\r
+ Book->hash[index] != NIL;\r
+ index = (index+1) & Book->mask);\r
+ // insert into the hash table\r
+ ASSERT(index>=0&&index<Book->alloc*2);\r
+ ASSERT(Book->hash[index]==NIL);\r
+ Book->hash[index] = pos;\r
+ ASSERT(pos>=0&&pos<Book->size);\r
+ }\r
+ fclose(f);\r
+}\r
+\r
+// gen_book_moves()\r
+// similar signature as gen_legal_moves\r
+static int gen_book_moves(list_t * list, const board_t * board){\r
+ int first_pos, pos, index;\r
+ entry_t entry[1];\r
+ list_clear(list);\r
+ bool found;\r
+ found=FALSE;\r
+ for (index = board->key & (uint64) Book->mask; (first_pos=Book->hash[index]) != NIL; index = (index+1) & Book->mask) {\r
+ ASSERT(first_pos>=0&&first_pos<Book->size);\r
+ if (Book->entry[first_pos].key == board->key) {\r
+ found=TRUE;\r
+ break; // found\r
+ }\r
+ }\r
+ if(!found) return -1;\r
+ if(Book->entry[first_pos].move==MoveNone) return -1;\r
+ for (pos = first_pos; pos < Book->size; pos++) {\r
+ *entry=Book->entry[pos];\r
+ if (entry->key != board->key) break;\r
+ if (entry->count > 0 &&\r
+ entry->move != MoveNone &&\r
+ move_is_legal(entry->move,board)) {\r
+ list_add_ex(list,entry->move,entry->count);\r
+ }\r
+ }\r
+ return first_pos;\r
+}\r
+\r
+// gen_opp_book_moves()\r
+// moves to which opponent has a reply in book\r
+// similar signature as gen_legal_moves\r
+static void gen_opp_book_moves(list_t * list, const board_t * board){\r
+ int move;\r
+ list_t new_list[1], legal_moves[1];\r
+ board_t new_board[1];\r
+ int i;\r
+ list_clear(list);\r
+ gen_legal_moves(legal_moves,board);\r
+ for (i = 0; i < list_size(legal_moves); i++) {\r
+ move = list_move(legal_moves,i);\r
+ // scratch_board\r
+ memcpy(new_board, board, sizeof(board_t));\r
+ move_do(new_board,move);\r
+ gen_book_moves(new_list,new_board); // wasteful in time but tested!\r
+ if(list_size(new_list)!=0){\r
+ list_add(list,move);\r
+ }\r
+ }\r
+}\r
+\r
+static void print_moves(info_t *info){\r
+ board_t board[1];\r
+ char move_string[256];\r
+ int i;\r
+ int color=White;\r
+ if(!info->output){\r
+ return;\r
+ }\r
+ board_start(board);\r
+ for(i=0;i<info->height;i++){\r
+ if(color==White){\r
+ fprintf(info->output,"%d. ",i/2+1);\r
+ color=Black;\r
+ }else{\r
+ color=White;\r
+ }\r
+ move_to_san(info->moves[i],board,move_string,256);\r
+ fprintf(info->output,"%s", move_string);\r
+ if(color==colour_opp(info->initial_color)){\r
+ fprintf(info->output,"{%.0f%%} ",100*info->probs[i]);\r
+ }else{\r
+ fprintf(info->output," ");\r
+ }\r
+ move_do(board,info->moves[i]);\r
+ }\r
+}\r
+\r
+static int search_book(board_t *board, info_t *info, search_t search){\r
+ list_t list[1];\r
+ board_t new_board[1];\r
+ uint16 move;\r
+ int count;\r
+ int ret;\r
+ int i;\r
+ int offset;\r
+ int pos;\r
+ int size;\r
+ int prob_sum;\r
+ double probs[256];\r
+ for(i=0;i<256;i++){\r
+ probs[i]=0.0; // kill compiler warnings\r
+ }\r
+ for(i=0;i<info->height;i++){\r
+ if(board->key==info->keys[i]){\r
+ if(info->output){\r
+ fprintf(info->output,"%d: ",info->line);\r
+ print_moves(info);\r
+ fprintf(info->output,"{cycle: ply=%d}\n",i);\r
+ }\r
+ info->line++;\r
+ return 1; // end of line because of cycle\r
+ }\r
+ }\r
+ if(!info->book_trans_only || (info->book_trans_only && search==BOOK)){\r
+ info->keys[info->height]=board->key;\r
+ size=Book->size; // hack\r
+ pos=find_entry(board,MoveNone);\r
+ if(size==Book->size){\r
+ if(info->output){\r
+ fprintf(info->output,"%d: ",info->line);\r
+ print_moves(info);\r
+ fprintf(info->output,"{trans: line=%d, ply=%d}\n",\r
+ Book->entry[pos].line,\r
+ Book->entry[pos].height);\r
+ }\r
+ info->line++;\r
+ return 1; // end of line because of transposition\r
+ }else{\r
+ Book->entry[pos].height=info->height;\r
+ Book->entry[pos].line=info->line;\r
+ }\r
+ }\r
+ count=0;\r
+ if(search==BOOK){\r
+ offset=gen_book_moves(list,board);\r
+ if(info->extended_search){\r
+ gen_legal_moves(list,board);\r
+ }\r
+// ASSERT(offset!=-1);\r
+ if(offset!=-1){ // only FALSE in starting position for black book\r
+ Book->entry[offset].colour=board->turn;\r
+ prob_sum=0;\r
+ if(!info->extended_search){\r
+ for(i=0;i<list_size(list);i++){\r
+ prob_sum+=((uint16)list_value(list,i));\r
+ }\r
+ for(i=0;i<list_size(list);i++){\r
+ probs[i]=((double)((uint16)list_value(list,i)))/((double)prob_sum);\r
+ }\r
+ }\r
+ }\r
+ }else{\r
+ gen_opp_book_moves(list,board);\r
+ }\r
+ for (i = 0; i < list_size(list); i++) {\r
+ move = list_move(list,i);\r
+ memcpy(new_board, board, sizeof(board_t));\r
+ ASSERT(move_is_legal(move,new_board));\r
+ move_do(new_board,move);\r
+ ASSERT(search!=opp_search(search));\r
+ info->moves[info->height++]=move;\r
+ if(search==BOOK){\r
+ info->probs[info->height-1]=probs[i];\r
+ }\r
+ ret=search_book(new_board, info, opp_search(search));\r
+ if(ret==0 && search==BOOK){\r
+ if(info->output){\r
+ fprintf(info->output,"%d: ",info->line);\r
+ print_moves(info);\r
+ fprintf(info->output,"\n");\r
+ }\r
+ info->line++;\r
+ ret=1; // end of line book move counts for 1\r
+ }\r
+ info->height--;\r
+ ASSERT(info->height>=0);\r
+ count+=ret;\r
+ }\r
+ return count;\r
+}\r
+\r
+void init_info(info_t *info){\r
+ info->line=1;\r
+ info->height=0;\r
+ info->output=NULL;\r
+ info->initial_color=White;\r
+ info->book_trans_only=FALSE;\r
+}\r
+\r
+// book_clean()\r
+// remove MoveNone entries from book and rebuild hash table\r
+void book_clean(){\r
+ int read_ptr,write_ptr;\r
+ write_ptr=0;\r
+ for(read_ptr=0;read_ptr<Book->size;read_ptr++){\r
+ if(Book->entry[read_ptr].move!=MoveNone){\r
+ Book->entry[write_ptr++]=Book->entry[read_ptr];\r
+ }\r
+ }\r
+ Book->size=write_ptr;\r
+ rebuild_hash_table();\r
+}\r
+\r
+// book_dump()\r
+\r
+void book_dump(int argc, char * argv[]) {\r
+ const char * bin_file=NULL;\r
+ const char * txt_file=NULL;\r
+ char string[StringSize];\r
+ int color=ColourNone;\r
+ board_t board[1];\r
+ info_t info[1];\r
+ int i;\r
+ FILE *f;\r
+ my_string_set(&bin_file,"book.bin");\r
+ for (i = 1; i < argc; i++) {\r
+ if (FALSE) {\r
+ } else if (my_string_equal(argv[i],"dump-book")) {\r
+ // skip\r
+ } else if (my_string_equal(argv[i],"-bin")) {\r
+ i++;\r
+ if (i==argc) my_fatal("book_dump(): missing argument\n");\r
+ my_string_set(&bin_file,argv[i]);\r
+ } else if (my_string_equal(argv[i],"-out")) {\r
+ i++;\r
+ if (i==argc) my_fatal("book_dump(): missing argument\n");\r
+ my_string_set(&txt_file,argv[i]);\r
+ } else if (my_string_equal(argv[i],"-color") || my_string_equal(argv[i],"-colour")) {\r
+ i++;\r
+ if (i == argc) my_fatal("book_dump(): missing argument\n");\r
+ if(my_string_equal(argv[i],"white")){\r
+ color=White;\r
+ }else if (my_string_equal(argv[i],"black")){\r
+ color=Black;\r
+ }else{\r
+ my_fatal("book_dump(): unknown color \"%s\"\n",argv[i]);\r
+ }\r
+ } else {\r
+ my_fatal("book_dump(): unknown option \"%s\"\n",argv[i]);\r
+ }\r
+ }\r
+ if(color==ColourNone){\r
+ my_fatal("book_dump(): you must specify a color\n");\r
+ }\r
+ if(txt_file==NULL){\r
+ snprintf(string,StringSize,"book_%s.txt",color?"white":"black");\r
+ my_string_set(&txt_file,string);\r
+ }\r
+\r
+ book_clear();\r
+ if(!Quiet){printf("loading book ...\n");}\r
+ book_load(bin_file);\r
+ board_start(board);\r
+ init_info(info);\r
+ info->initial_color=color;\r
+ if(!(f=fopen(txt_file,"w"))){\r
+ my_fatal("book_dump(): can't open file \"%s\" for writing: %s",\r
+ txt_file,strerror(errno));\r
+ }\r
+ info->output=f;\r
+ fprintf(info->output,"Dump of \"%s\" for %s.\n",\r
+ bin_file,color==White?"white":"black");\r
+ if(color==White){\r
+ if(!Quiet){printf("generating lines for white...\n");}\r
+ search_book(board,info, BOOK);\r
+ }else{\r
+ if(!Quiet){printf("generating lines for black...\n");}\r
+ search_book(board,info, ALL);\r
+ }\r
+}\r
+\r
+// book_info()\r
+\r
+void book_info(int argc,char* argv[]){\r
+ const char *bin_file=NULL;\r
+ board_t board[1];\r
+ info_t info[1];\r
+ uint64 last_key;\r
+ int pos;\r
+ int white_pos,black_pos,total_pos,white_pos_extended,\r
+ black_pos_extended,white_pos_extended_diff,black_pos_extended_diff;\r
+ int s;\r
+ bool extended_search=FALSE;\r
+ int i;\r
+ Quiet=TRUE;\r
+ my_string_set(&bin_file,"book.bin");\r
+\r
+ for (i = 1; i < argc; i++) {\r
+ if (FALSE) {\r
+ } else if (my_string_equal(argv[i],"info-book")) {\r
+ // skip\r
+ } else if (my_string_equal(argv[i],"-bin")) {\r
+ i++;\r
+ if (i==argc) my_fatal("book_info(): missing argument\n");\r
+ my_string_set(&bin_file,argv[i]);\r
+ } else if (my_string_equal(argv[i],"-exact")) {\r
+ extended_search=TRUE;\r
+ } else {\r
+ my_fatal("book_info(): unknown option \"%s\"\n",argv[i]);\r
+ }\r
+ }\r
+ book_clear();\r
+ if(!Quiet){printf("loading book ...\n");}\r
+ book_load(bin_file);\r
+ s=Book->size;\r
+\r
+ board_start(board);\r
+ init_info(info);\r
+ info->book_trans_only=FALSE;\r
+ info->initial_color=White;\r
+ info->extended_search=FALSE;\r
+ search_book(board,info, BOOK);\r
+ printf("Lines for white : %8d\n",info->line-1);\r
+\r
+\r
+ info->line=1;\r
+ info->height=0;\r
+ info->initial_color=Black;\r
+ book_clean();\r
+ ASSERT(Book->size==s);\r
+ board_start(board);\r
+ search_book(board,info, ALL);\r
+ printf("Lines for black : %8d\n",info->line-1);\r
+\r
+ book_clean();\r
+ ASSERT(Book->size==s);\r
+ white_pos=0;\r
+ black_pos=0;\r
+ total_pos=0;\r
+ last_key=0;\r
+ for(pos=0;pos<Book->size;pos++){\r
+ if(Book->entry[pos].key==last_key){\r
+ ASSERT(Book->entry[pos].colour==ColourNone);\r
+ continue;\r
+ }\r
+ last_key=Book->entry[pos].key;\r
+ total_pos++;\r
+ if(Book->entry[pos].colour==White){\r
+ white_pos++;\r
+ }else if(Book->entry[pos].colour==Black){\r
+ black_pos++;\r
+ }\r
+ }\r
+ printf("Positions on lines for white : %8d\n",white_pos);\r
+ printf("Positions on lines for black : %8d\n",black_pos);\r
+\r
+ \r
+ if(extended_search){\r
+ init_info(info);\r
+ info->book_trans_only=TRUE;\r
+ info->initial_color=White;\r
+ info->extended_search=TRUE;\r
+ book_clean();\r
+ board_start(board);\r
+ search_book(board,info, BOOK);\r
+\r
+ init_info(info);\r
+ info->book_trans_only=TRUE;\r
+ info->initial_color=Black;\r
+ info->extended_search=TRUE;\r
+ book_clean();\r
+ board_start(board);\r
+ search_book(board,info, ALL);\r
+ book_clean();\r
+ ASSERT(Book->size==s);\r
+ white_pos_extended=0;\r
+ black_pos_extended=0;\r
+ last_key=0;\r
+ for(pos=0;pos<Book->size;pos++){\r
+ if(Book->entry[pos].key==last_key){\r
+ ASSERT(Book->entry[pos].colour==ColourNone);\r
+ continue;\r
+ }\r
+ last_key=Book->entry[pos].key;\r
+ if(Book->entry[pos].colour==White){\r
+ white_pos_extended++;\r
+ }else if(Book->entry[pos].colour==Black){\r
+ black_pos_extended++;\r
+ }\r
+ }\r
+ white_pos_extended_diff=white_pos_extended-white_pos;\r
+ black_pos_extended_diff=black_pos_extended-black_pos;\r
+ printf("Unreachable white positions(?) : %8d\n",\r
+ white_pos_extended_diff);\r
+ printf("Unreachable black positions(?) : %8d\n",\r
+ black_pos_extended_diff);\r
+\r
+ }\r
+ if(extended_search){\r
+ printf("Isolated positions : %8d\n",\r
+ total_pos-white_pos_extended-black_pos_extended);\r
+ }else{\r
+ printf("Isolated positions : %8d\n",\r
+ total_pos-white_pos-black_pos);\r
+ }\r
+}\r
+\r
+\r
+\r
+// end of book_make.cpp\r
+\r