Add XBoard protocol drivers
[bonanza.git] / book.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <math.h>
4 #include <string.h>
5 #include <limits.h>
6 #include <float.h>
7 #include "shogi.h"
8
9 /*
10   Opening Book Data Structure: Index BookData
11
12     Index:         IndexEntry.. (NUM_SECTION times)
13
14       IndexEntry:  SectionPointer SectionSize
15
16     BookData:      Section.. (NUM_SECTION times)
17
18       Section:     [DataEntry]..
19
20         DataEntry: Header Move...
21
22
23 - SectionPointer
24   4 byte:  position of the section in character
25
26 - SectionSize
27   2 byte:  size of the section in character
28  
29 - Header
30   1 byte:  number of bytes for the DataEntry
31   8 byte:  hash key
32   
33 - Move
34   2 byte:  a book move
35   2 byte:  frequency
36  */
37
38 #define BK_SIZE_INDEX     6
39 #define BK_SIZE_HEADER    9
40 #define BK_SIZE_MOVE      4
41 #define BK_MAX_MOVE       32
42
43 #if ( BK_SIZE_HEADER + BK_SIZE_MOVE * BK_MAX_MOVE > UCHAR_MAX )
44 #  error "Maximum size of DataEntry is larger than UCHAR_MAX"
45 #endif
46
47 typedef struct { unsigned short smove, freq; } book_move_t;
48
49 typedef struct { int from, to; } ft_t;
50
51 static int book_read( uint64_t key, book_move_t *pbook_move,
52                       unsigned int *pposition );
53 static uint64_t book_hash_func( const tree_t * restrict ptree,int *pis_flip );
54 static unsigned int bm2move( const tree_t * restrict ptree, unsigned int bmove,
55                              int is_flip );
56 static ft_t flip_ft( ft_t ft, int turn, int is_flip );
57 static int normalize_book_move( book_move_t * restrict pbook_move, int moves );
58
59
60 int
61 book_on( void )
62 {
63   int iret = file_close( pf_book );
64   if ( iret < 0 ) { return iret; }
65
66   pf_book = file_open( str_book, "rb+" );
67   if ( pf_book == NULL ) { return -2; }
68
69   return 1;
70 }
71
72
73 int
74 book_off( void )
75 {
76   int iret = file_close( pf_book );
77   if ( iret < 0 ) { return iret; }
78
79   pf_book = NULL;
80
81   return 1;
82 }
83
84
85 int
86 book_probe( tree_t * restrict ptree )
87 {
88   book_move_t abook_move[ BK_MAX_MOVE+1 ];
89   uint64_t key;
90   double dscore, drand;
91   unsigned int move, position, freq_lower_limit;
92   int is_flip, i, j, moves, ply;
93
94   key   = book_hash_func( ptree, &is_flip );
95
96   moves = book_read( key, abook_move, &position );
97   if ( moves <= 0 ) { return moves; }
98
99 #if ! defined(MINIMUM) || ! defined(NDEBUG)
100   for ( j = i = 0; i < moves; i++ ) { j += abook_move[i].freq; }
101   if ( j != USHRT_MAX )
102     {
103       str_error = "normalization error (book.bin)";
104       return -1;
105     }
106 #endif
107
108   /* decision of book move based on pseudo-random number */
109   if ( game_status & flag_puzzling ) { j = 0; }
110   else {
111     drand = (double)rand64() / (double)UINT64_MAX;
112
113     if ( game_status & flag_narrow_book )
114       {
115 #if defined(BK_ULTRA_NARROW)
116         freq_lower_limit = abook_move[0].freq;
117 #else
118         freq_lower_limit = abook_move[0].freq / 2U;
119 #endif
120
121         for ( i = 1; i < moves; i++ )
122           {
123             if ( abook_move[i].freq < freq_lower_limit ) { break; }
124           }
125         moves = i;
126         normalize_book_move( abook_move, moves );
127       }
128
129     for ( j = moves-1; j > 0; j-- )
130       {
131         dscore = (double)( abook_move[j].freq ) / (double)USHRT_MAX;
132         if ( drand <= dscore ) { break; }
133         drand -= dscore;
134       }
135     if ( ! abook_move[j].freq ) { j = 0; }
136   }
137
138   /* show results */
139   if ( ! ( game_status & ( flag_pondering | flag_puzzling ) ) )
140     {
141 #ifdef XBOARD
142 #  define SIGN "#"
143 #else
144 #  define SIGN
145 #endif
146       Out( SIGN "    move     freq\n" );
147       OutCsaShogi( "info" );
148       for ( i = 0; i < moves; i++ )
149         {
150           const char *str;
151           
152           dscore = (double)abook_move[i].freq / (double)USHRT_MAX;
153           move = bm2move( ptree, (unsigned int)abook_move[i].smove, is_flip );
154           str  = str_CSA_move( move );
155           
156           Out( "  %c %s  %5.1f\n", i == j ? '*' : ' ', str, dscore * 100.0 );
157           OutCsaShogi( " %s(%.0f%%)", str, dscore * 100.0 );
158         }
159       OutCsaShogi( "\n" );
160     }
161
162   move = bm2move( ptree, (unsigned int)abook_move[j].smove, is_flip );
163   if ( ! is_move_valid( ptree, move, root_turn ) )
164     {
165       out_warning( "BAD BOOK MOVE!! " );
166       return 0;
167     }
168
169   ply = record_game.moves;
170   if ( game_status & flag_pondering ) { ply++; }
171   if ( ply < HASH_REG_HIST_LEN )
172     {
173       history_book_learn[ ply ].key_book    = key;  
174       history_book_learn[ ply ].move_probed = move;
175       history_book_learn[ ply ].key_probed  = (unsigned int)HASH_KEY;  
176       history_book_learn[ ply ].hand_probed = HAND_B;
177       history_book_learn[ ply ].data        = (unsigned int)is_flip << 30;
178       if ( game_status & flag_narrow_book )
179         {
180           history_book_learn[ ply ].data |= 1U << 29;
181         }
182     }
183
184   ptree->current_move[1] = move;
185
186   return 1;
187 }
188
189
190 static int
191 book_read( uint64_t key, book_move_t *pbook_move, unsigned int *pposition )
192 {
193   uint64_t book_key;
194   const unsigned char *p;
195   unsigned int position, size_section, size, u;
196   int ibook_section, moves;
197   unsigned short s;
198
199   ibook_section = (int)( (unsigned int)key & (unsigned int)( NUM_SECTION-1 ) );
200
201   if ( fseek( pf_book, BK_SIZE_INDEX*ibook_section, SEEK_SET ) == EOF )
202     {
203       str_error = str_io_error;
204       return -2;
205     }
206   
207   if ( fread( &position, sizeof(int), 1, pf_book ) != 1 )
208     {
209       str_error = str_io_error;
210       return -2;
211     }
212   
213   if ( fread( &s, sizeof(unsigned short), 1, pf_book ) != 1 )
214     {
215       str_error = str_io_error;
216       return -2;
217     }
218   size_section = (unsigned int)s;
219   if ( size_section > MAX_SIZE_SECTION )
220     {
221       str_error = str_book_error;
222       return -2;
223     }
224
225   if ( fseek( pf_book, (long)position, SEEK_SET ) == EOF )
226     {
227       str_error = str_io_error;
228       return -2;
229     }
230   if ( fread( book_section, sizeof(unsigned char), (size_t)size_section,
231               pf_book ) != (size_t)size_section )
232     {
233       str_error = str_io_error;
234       return -2;
235     }
236   
237   size       = 0;
238   p          = book_section;
239   *pposition = position;
240   while ( book_section + size_section > p )
241     {
242       size     = (unsigned int)p[0];
243       book_key = *(uint64_t *)( p + 1 );
244       if ( book_key == key ) { break; }
245       p          += size;
246       *pposition += size;
247     }
248   if ( book_section + size_section <= p ) { return 0; }
249
250   for ( moves = 0, u = BK_SIZE_HEADER; u < size; moves++, u += BK_SIZE_MOVE )
251     {
252       pbook_move[moves].smove  = *(unsigned short *)(p+u+0);
253       pbook_move[moves].freq   = *(unsigned short *)(p+u+2);
254     }
255
256   return moves;
257 }
258
259
260 static ft_t
261 flip_ft( ft_t ft, int turn, int is_flip )
262 {
263   int ito_rank, ito_file, ifrom_rank, ifrom_file;
264
265   ito_rank = airank[ft.to];
266   ito_file = aifile[ft.to];
267   if ( ft.from < nsquare )
268     {
269       ifrom_rank = airank[ft.from];
270       ifrom_file = aifile[ft.from];
271     }
272   else { ifrom_rank = ifrom_file = 0; }
273
274   if ( turn )
275     {
276       ito_rank = rank9 - ito_rank;
277       ito_file = file9 - ito_file;
278       if ( ft.from < nsquare )
279         {
280           ifrom_rank = rank9 - ifrom_rank;
281           ifrom_file = file9 - ifrom_file;
282         }
283     }
284
285   if ( is_flip )
286     {
287       ito_file = file9 - ito_file;
288       if ( ft.from < nsquare ) { ifrom_file = file9 - ifrom_file; }
289     }
290
291   ft.to = ito_rank * nfile + ito_file;
292   if ( ft.from < nsquare ) { ft.from = ifrom_rank * nfile + ifrom_file; }
293
294   return ft;
295 }
296
297
298 static unsigned int
299 bm2move( const tree_t * restrict ptree, unsigned int bmove, int is_flip )
300 {
301   ft_t ft;
302   unsigned int move;
303   int is_promote;
304
305   ft.to      = I2To(bmove);
306   ft.from    = I2From(bmove);
307   ft         = flip_ft( ft, root_turn, is_flip );
308   is_promote = I2IsPromote(bmove);
309
310   move  = (unsigned int)( is_promote | From2Move(ft.from) | ft.to );
311   if ( ft.from >= nsquare ) { return move; }
312
313   if ( root_turn )
314     {
315       move |= Cap2Move(BOARD[ft.to]);
316       move |= Piece2Move(-BOARD[ft.from]);
317     }
318   else {
319     move |= Cap2Move(-BOARD[ft.to]);
320     move |= Piece2Move(BOARD[ft.from]);
321   }
322
323   return move;
324 }
325
326
327 static uint64_t
328 book_hash_func( const tree_t * restrict ptree, int *pis_flip )
329 {
330   uint64_t key, key_flip;
331   unsigned int hand;
332   int i, iflip, irank, ifile, piece;
333
334   key = 0;
335   hand = root_turn ? HAND_W : HAND_B;
336   i = I2HandPawn(hand);    if ( i ) { key ^= b_hand_pawn_rand[i-1]; }
337   i = I2HandLance(hand);   if ( i ) { key ^= b_hand_lance_rand[i-1]; }
338   i = I2HandKnight(hand);  if ( i ) { key ^= b_hand_knight_rand[i-1]; }
339   i = I2HandSilver(hand);  if ( i ) { key ^= b_hand_silver_rand[i-1]; }
340   i = I2HandGold(hand);    if ( i ) { key ^= b_hand_gold_rand[i-1]; }
341   i = I2HandBishop(hand);  if ( i ) { key ^= b_hand_bishop_rand[i-1]; }
342   i = I2HandRook(hand);    if ( i ) { key ^= b_hand_rook_rand[i-1]; }
343
344   hand = root_turn ? HAND_B : HAND_W;
345   i = I2HandPawn(hand);    if ( i ) { key ^= w_hand_pawn_rand[i-1]; }
346   i = I2HandLance(hand);   if ( i ) { key ^= w_hand_lance_rand[i-1]; }
347   i = I2HandKnight(hand);  if ( i ) { key ^= w_hand_knight_rand[i-1]; }
348   i = I2HandSilver(hand);  if ( i ) { key ^= w_hand_silver_rand[i-1]; }
349   i = I2HandGold(hand);    if ( i ) { key ^= w_hand_gold_rand[i-1]; }
350   i = I2HandBishop(hand);  if ( i ) { key ^= w_hand_bishop_rand[i-1]; }
351   i = I2HandRook(hand);    if ( i ) { key ^= w_hand_rook_rand[i-1]; }
352
353   key_flip = key;
354
355   for ( irank = rank1; irank <= rank9; irank++ )
356     for ( ifile = file1; ifile <= file9; ifile++ )
357       {
358         if ( root_turn )
359           {
360             i     = ( rank9 - irank ) * nfile + file9 - ifile;
361             iflip = ( rank9 - irank ) * nfile + ifile;
362             piece = -(int)BOARD[nsquare-i-1];
363          &