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