14 #if ! defined(MINIMUM)
16 # define SEARCH_DEPTH 2
18 # define MAX_RECORD_LENGTH 1024
19 # define DOT_INTERVAL 10
20 # define MAX_BOOK_PLY 64
21 # define NumBookCluster 256
22 # define NumBookEntry 0x1000
23 # define SIZE_PV_BUFFER 0x100000
24 # define MOVE_VOID 0x80000000U
25 # define Move2S(move) (unsigned short)((move) & 0x7fffU)
29 struct { uint64_t a,b; } cluster[NumBookCluster];
36 book_entry_t *pbook_entry;
39 unsigned int max_games, id;
43 uint64_t result[ NUM_RESULT ];
44 uint64_t num_moves_counted, num_moves, result_norm, num_nodes;
45 double target, target_out_window;
46 unsigned int illegal_moves, max_pos_buf;
50 unsigned int record_moves[ MAX_RECORD_LENGTH ];
51 unsigned int amove_legal[ MAX_LEGAL_MOVES ];
52 unsigned int record_length, pos_buf;
53 unsigned short buf[ SIZE_PV_BUFFER ];
68 uint64_t num_moves_counted;
73 unsigned short buf[ SIZE_PV_BUFFER ];
74 unsigned int pv[ PLY_MAX + 2 ];
79 static unsigned int __stdcall parse1_worker( void *arg );
80 static unsigned int __stdcall parse2_worker( void *arg );
82 static void *parse1_worker( void *arg );
83 static void *parse2_worker( void *arg );
85 static void ini_book( book_entry_t *pbook_entry );
86 static void make_pv( parse1_data_t *pdata, unsigned int record_move );
87 static int read_game( parse1_data_t *pdata );
88 static int read_buf( unsigned short *buf, FILE *pf );
89 static int calc_deriv( parse2_data_t *pdata, int pos_buf, int turn );
90 static int learn_parse1( tree_t * restrict ptree, book_entry_t *pbook_entry,
91 FILE *pf_tmp, record_t *precord,
92 unsigned int max_games, double *ptarget_out_window,
93 double *pobj_norm, int tlp1 );
94 static int learn_parse2( tree_t * restrict ptree, FILE *pf_tmp,
95 int nsteps, double target_out_window,
96 double obj_norm, int tlp2 );
97 static int book_probe_learn( const tree_t * restrict ptree,
98 book_entry_t *pbook_entry, int ply,
100 static int rep_check_learn( tree_t * restrict ptree, int ply );
101 static unsigned int s2move( const tree_t * restrict ptree, unsigned int move,
103 static double func( double x );
104 static double dfunc( double x );
107 learn( tree_t * restrict ptree, int is_ini, int nsteps, unsigned int max_games,
108 int max_iterations, int nworker1, int nworker2 )
111 book_entry_t *pbook_entry;
113 double target_out_window, obj_norm;
114 int iret, niterations;
116 pbook_entry = memory_alloc( sizeof(book_entry_t) * NumBookEntry );
117 if ( pbook_entry == NULL ) { return -2; }
122 fmg_misc = fmg_cap = fmg_drop = fmg_mt = 0;
123 fmg_misc_king = fmg_cap_king = 0;
130 fmg_misc_king = FMG_MISC_KING;
131 fmg_cap_king = FMG_CAP_KING;
134 game_status |= flag_learning;
135 root_alpha = - ( score_max_eval + 1 );
136 root_beta = + ( score_max_eval + 1 );
140 for ( niterations = 1; niterations <= max_iterations; niterations++ )
142 Out( "\n Iteration %03d\n", niterations );
144 iret = record_open( &record, "records.csa", mode_read, NULL, NULL );
145 if ( iret < 0 ) { break; }
147 pf_tmp = file_open( "tmp.bin", "wb" );
148 if ( pf_tmp == NULL )
150 record_close( &record );
155 iret = learn_parse1( ptree, pbook_entry, pf_tmp, &record, max_games,
156 &target_out_window, &obj_norm, nworker1 );
160 record_close( &record );
161 file_close( pf_tmp );
165 iret = file_close( pf_tmp );
168 record_close( &record );
172 pf_tmp = file_open( "tmp.bin", "rb" );
173 if ( pf_tmp == NULL )
175 record_close( &record );
180 iret = learn_parse2( ptree, pf_tmp, nsteps, target_out_window, obj_norm,
184 file_close( pf_tmp );
185 record_close( &record );
189 iret = file_close( pf_tmp );
192 record_close( &record );
196 iret = record_close( &record );
197 if ( iret < 0 ) { break; }
199 if ( ! ( game_status & flag_learning ) )
201 out_warning( "flag_learning is not set." );
205 memory_free( pbook_entry );
206 game_status &= ~flag_learning;
213 learn_parse1( tree_t * restrict ptree, book_entry_t *pbook_entry, FILE *pf_tmp,
214 record_t *precord, unsigned int max_games,
215 double *ptarget_out_window, double *pobj_norm, int nworker )
217 parse1_data_t *pdata[ TLP_MAX_THREADS ];
220 ini_book( pbook_entry );
222 for ( id = 0; id < nworker; id++ )
224 pdata[id] = memory_alloc( sizeof(parse1_data_t) );
225 if ( pdata[id] == NULL ) { return -1; }
227 pdata[id]->ptree = NULL;
228 pdata[id]->pbook_entry = pbook_entry;
229 pdata[id]->pf_tmp = pf_tmp;
230 pdata[id]->precord = precord;
231 pdata[id]->max_games = max_games;
233 pdata[id]->nworker = nworker;
237 for ( id = 1; id < nworker; id++ )
240 pdata[id]->ptree = tlp_atree_work + id;
241 if ( ! _beginthreadex( 0, 0, parse1_worker, pdata[id], 0, 0 ) )
243 str_error = "_beginthreadex() failed.";
249 pdata[id]->ptree = tlp_atree_work + id;
250 if ( pthread_create( &pt, &pthread_attr, parse1_worker, pdata[id] ) )
252 str_error = "pthread_create() failed.";
259 pdata[0]->ptree = ptree;
260 parse1_worker( pdata[0] );
263 while ( tlp_num ) { tlp_yield(); }
266 for ( id = 0; id < nworker; id++ )
268 if ( pdata[id]->info < 0 ) { return -1; }
271 for ( id = 1; id < nworker; id++ )
273 for ( i = 0; i < NUM_RESULT; i++ )
275 pdata[0]->result[i] += pdata[id]->result[i];
277 pdata[0]->num_moves_counted += pdata[id]->num_moves_counted;
278 pdata[0]->num_moves += pdata[id]->num_moves;
279 pdata[0]->num_nodes += pdata[id]->num_nodes;
280 pdata[0]->result_norm += pdata[id]->result_norm;
281 pdata[0]->target += pdata[id]->target;
282 pdata[0]->target_out_window += pdata[id]->target_out_window;
283 pdata[0]->illegal_moves += pdata[id]->illegal_moves;
284 if ( pdata[0]->max_pos_buf < pdata[id]->max_pos_buf )
286 pdata[0]->max_pos_buf = pdata[id]->max_pos_buf;
289 if ( pdata[0]->result_norm == 0 ) { pdata[0]->result_norm = 1; }
290 if ( pdata[0]->num_moves == 0 ) { pdata[0]->num_moves = 1; }
291 *ptarget_out_window = pdata[0]->target_out_window;
292 *pobj_norm = (double)pdata[0]->num_moves;
296 int misc, drop, cap, mt, misc_king, cap_king;
299 Out( " Number of Games : %u\n", precord->games );
300 Out( " Total Moves : %"PRIu64"\n",pdata[0]->num_moves );
301 Out( " Moves Counted : %"PRIu64"\n",pdata[0]->num_moves_counted);
302 Out( " Illegal Moves : %u\n", pdata[0]->illegal_moves );
303 Out( " Nodes Searched : %"PRIu64"\n",pdata[0]->num_nodes );
304 Out( " Max pos_buf : %x\n", pdata[0]->max_pos_buf );
305 Out( " Move Prediction :" );
306 for ( i = 0, dtemp = 0.0; i < NUM_RESULT; i++ )
308 dtemp += (double)pdata[0]->result[i] * 100.0;
309 Out( " %4.1f%%", dtemp / (double)pdata[0]->result_norm );
313 pdata[0]->target /= *pobj_norm;
314 dtemp = *ptarget_out_window / *pobj_norm;
315 Out( " Target : %f (%f)\n", pdata[0]->target, dtemp );
321 misc_king = fmg_misc_king / 2;
322 cap_king = fmg_cap_king / 2;
323 Out( " Futility : misc=%d drop=%d cap=%d mt=%d misc(k)=%d "
324 "cap(k)=%d\n", misc, drop, cap, mt, misc_king, cap_king );
327 for ( id = 0; id < nworker; id++ ) { memory_free( pdata[id] ); }
333 # if defined(_MSC_VER)
334 static unsigned int __stdcall parse1_worker( void *arg )
336 static void *parse1_worker( void *arg )
339 parse1_data_t *pdata;
341 unsigned int record_move;
345 pdata = (parse1_data_t *)arg;
346 ptree = pdata->ptree;
349 if ( pdata->nworker > 1 )
353 if ( pdata->id ) { Out( "hi from thread #%d\n", pdata->id ); }
355 while ( tlp_num < pdata->nworker ) { tlp_yield(); }
359 for ( i = 0; i < NUM_RESULT; i++ ) { pdata->result[i] = 0; }
360 pdata->num_moves_counted = 0;
361 pdata->num_moves = 0;
362 pdata->num_nodes = 0;
363 pdata->max_pos_buf = 0;
364 pdata->result_norm = 0;
365 pdata->record_length = 0;
366 pdata->illegal_moves = 0;
369 pdata->target_out_window = 0.0;
374 for ( imove = 0; imove < (int)pdata->record_length; imove++ )
376 record_move = pdata->record_moves[imove];
378 pdata->buf[ pdata->pos_buf++ ] = Move2S(record_move);
380 if ( record_move & MOVE_VOID ) { record_move &= ~MOVE_VOID; }
381 else { make_pv( pdata, record_move ); }
383 pdata->buf[ pdata->pos_buf++ ] = 0;
385 MakeMove( pdata->root_turn, record_move, 1 );
386 pdata->root_turn = Flip( pdata->root_turn );
387 ptree->move_last[1] = ptree->move_last[0];
388 ptree->nsuc_check[0] = 0;
390 = (unsigned char)( InCheck( pdata->root_turn ) ? 1U : 0 );
394 if ( pdata->nworker > 1 ) { lock( &tlp_lock ); }
398 if ( pdata->record_length )
400 if ( pdata->pos_buf > pdata->max_pos_buf )
402 pdata->max_pos_buf = pdata->pos_buf;
404 pdata->buf[0] = (unsigned short)( pdata->pos_buf / 0x10000U );
405 pdata->buf[1] = (unsigned short)( pdata->pos_buf % 0x10000U );
407 if ( fwrite( pdata->buf, sizeof(unsigned short), pdata->pos_buf,
408 pdata->pf_tmp ) != pdata->pos_buf )
410 str_error = str_io_error;
416 while ( iret >= 0 ) {
417 iret = read_game( pdata );
418 if ( iret == 1 ) { break; } /* end of record */
419 if ( pdata->record_length ) { break; } /* read a record */
423 if ( pdata->nworker > 1 ) { unlock( &tlp_lock ); }
426 if ( iret < 0 ) { break; }
427 if ( iret == 1 ) { break; }
431 if ( pdata->nworker > 1 )
445 make_pv( parse1_data_t *pdata, unsigned int record_move )
450 unsigned int move, pos_buf;
451 int i, imove, record_value, nth, nc, nmove_legal;
452 int value, alpha, beta, depth, ply, tt;
454 record_value = INT_MIN;
457 depth = PLY_INC * SEARCH_DEPTH + PLY_INC / 2;
458 tt = Flip(pdata->root_turn);
459 pos_buf = pdata->pos_buf;
460 ptree = pdata->ptree;
461 ptree->node_searched = 0;
463 ptree->tlp_abort = 0;
465 for ( ply = 0; ply < PLY_MAX; ply++ )
467 ptree->amove_killer[ply].no1 = ptree->amove_killer[ply].no2 = 0U;
468 ptree->killers[ply].no1 = ptree->killers[ply].no2 = 0U;
470 for ( i = 0; i < HIST_SIZE; i++ )
472 ptree->hist_good[i] /= 256U;
473 ptree->hist_tried[i] /= 256U;
476 pmove = GenCaptures ( pdata->root_turn, pdata->amove_legal );
477 pmove = GenNoCaptures ( pdata->root_turn, pmove );
478 pmove = GenDrop ( pdata->root_turn, pmove );
479 pmove = GenCapNoProEx2 ( pdata->root_turn, pmove );
480 pmove = GenNoCapNoProEx2( pdata->root_turn, pmove );
481 nmove_legal = (int)( pmove - pdata->amove_legal );
483 for ( i = 0; pdata->amove_legal[i] != record_move; i++ );
484 move = pdata->amove_legal[0];
485 pdata->amove_legal[0] = pdata->amove_legal[i];
486 pdata->amove_legal[i] = move;
488 for ( imove = 0; imove < nmove_legal; imove++ ) {
489 move = pdata->amove_legal[imove];
492 alpha = record_value - FV_WINDOW;
493 beta = record_value + FV_WINDOW;
494 if ( alpha < root_alpha ) { alpha = root_alpha; }
495 if ( beta > root_beta ) { beta = root_beta; }
502 MakeMove( pdata->root_turn, move, 1 );
503 if ( InCheck(pdata->root_turn) )
505 UnMakeMove( pdata->root_turn, move, 1 );
512 = (unsigned char)( ptree->nsuc_check[0] + 1U );
513 } else { ptree->nsuc_check[2] = 0; }
515 ptree->current_move[1] = move;
516 ptree->pv[1].type = no_rep;
518 value = -search( ptree, -beta, -alpha, tt, depth, 2,
519 node_do_mate | node_do_null | node_do_futile
522 UnMakeMove( pdata->root_turn, move, 1 );
524 if ( abs(value) > score_mate1ply )
526 out_warning( "value is larger than mate1ply!" );
531 func_value = func( value - record_value );
532 pdata->target += func_value;
533 pdata->num_moves += 1U;
534 if ( alpha < value && value < beta )
538 else { pdata->target_out_window += func_value; }
539 if ( value >= record_value ) { nth += 1; }
541 else if ( alpha < value && value < beta )
544 record_value = value;
546 else { break; } /* record move failed high or low. */
548 if ( alpha < value && value < beta )
550 pdata->buf[ pos_buf++ ] = Move2S(move);
551 for ( ply = 2; ply <= ptree->pv[1].length; ply++ )
553 pdata->buf[ pos_buf++ ] = Move2S(ptree->pv[1].a[ply]);
555 pdata->buf[ pos_buf - 1 ] |= 0x8000U;
561 pdata->result_norm += 1;
562 if ( nth-1 < NUM_RESULT ) { pdata->result[nth-1] += 1; }
567 pdata->pos_buf = pos_buf;
568 pdata->num_moves_counted += nc;
570 pdata->num_nodes += ptree->node_searched;
575 read_game( parse1_data_t *pdata )
579 unsigned int record_move;
580 int istatus, iret, imove;
584 if ( pdata->precord->games == pdata->max_games ) { return 1; }
586 if ( pdata->precord->games == 0 ) { Out( " Parse 1 " ); }
588 if ( ! ( (pdata->precord->games+1) % DOT_INTERVAL ) )
590 if ( ! ( (pdata->precord->games+1) % ( DOT_INTERVAL * 10 ) ) )
593 if ( ! ( (pdata->precord->games+1) % ( DOT_INTERVAL * 50 ) ) )
595 Out( "%7d\n ", pdata->precord->games+1 );
601 for ( imove = 0; imove < MAX_RECORD_LENGTH; imove++ )
603 istatus = in_CSA( ptree, pdata->precord, &record_move,
604 flag_nomake_move | flag_detect_hang | flag_nofmargin );
607 pdata->illegal_moves += 1;
610 if ( istatus >= record_eof ) { break; }
613 && ( root_turn != min_posi_no_handicap.turn_to_move
614 || HAND_B != min_posi_no_handicap.hand_black
615 || HAND_W != min_posi_no_handicap.hand_white
616 || memcmp( BOARD, min_posi_no_handicap.asquare, nsquare ) ) )
623 *(pdata->ptree) = *ptree;
624 pdata->root_turn = root_turn;
627 if ( rep_check_learn( ptree, 1 ) == four_fold_rep
628 || ( pdata->precord->moves < MAX_BOOK_PLY
629 && book_probe_learn( ptree, pdata->pbook_entry,
630 pdata->precord->moves, record_move ) ) )
632 pdata->record_moves[ imove ] = record_move | MOVE_VOID;
634 else { pdata->record_moves[ imove ] = record_move; }
636 iret = make_move_root( ptree, record_move, flag_rejections );
637 if ( iret < 0 ) { return iret; }
640 if ( istatus != record_next && istatus != record_eof )
642 istatus = record_wind( pdata->precord );
643 if ( istatus < 0 ) { return istatus; }
646 if ( ! imove && istatus == record_eof ) { return 1; }
647 pdata->record_length = imove;
654 learn_parse2( tree_t * restrict ptree, FILE *pf_tmp, int nsteps,
655 double target_out_window, double obj_norm, int nworker )
657 parse2_data_t *pdata[ TLP_MAX_THREADS ];
660 for ( id = 0; id < nworker; id++ )
662 pdata[id] = memory_alloc( sizeof(parse2_data_t) );
663 if ( pdata[id] == NULL ) { return -1; }
665 pdata[id]->ptree = NULL;
666 pdata[id]->pf_tmp = pf_tmp;
668 pdata[id]->nworker = nworker;
677 for ( id = 1; id < nworker; id++ )
680 pdata[id]->ptree = tlp_atree_work + id;
681 if ( ! _beginthreadex( 0, 0, parse2_worker, pdata[id], 0, 0 ) )
683 str_error = "_beginthreadex() failed.";
689 pdata[id]->ptree = tlp_atree_work + id;
690 if ( pthread_create( &pt, &pthread_attr, parse2_worker, pdata[id] ) )
692 str_error = "pthread_create() failed.";
699 pdata[0]->ptree = ptree;
700 parse2_worker( pdata[0] );
703 while ( tlp_num ) { tlp_yield(); }
706 for ( id = 0; id < nworker; id++ )
708 if ( pdata[id]->info < 0 ) { return -1; }
711 for ( id = 1; id < nworker; id++ )
713 add_param( &pdata[0]->param, &pdata[id]->param );
714 pdata[0]->num_moves_counted += pdata[id]->num_moves_counted;
715 pdata[0]->target += pdata[id]->target;
720 double target, penalty, objective_function;
722 penalty = calc_penalty() / obj_norm;
723 target = ( pdata[0]->target + target_out_window ) / obj_norm;
724 objective_function = target + penalty;
725 Out( " Moves Counted : %d\n", pdata[0]->num_moves_counted );
726 Out( " Objective Func. : %f %f %f\n",
727 objective_function, target, penalty );
731 param_sym( &pdata[0]->param );
733 renovate_param( &pdata[0]->param );
735 if ( istep < nsteps ) { Out( "." ); }
742 for ( id = 0; id < nworker; id++ ) { memory_free( pdata[id] ); }
748 # if defined(_MSC_VER)
749 static unsigned int __stdcall parse2_worker( void *arg )
751 static void *parse2_worker( void *arg )
754 parse2_data_t *pdata;
756 unsigned int record_move;
757 int iret, imove, nbuf, pos_buf, turn;
760 pdata = (parse2_data_t *)arg;
761 ptree = pdata->ptree;
764 if ( pdata->nworker > 1 )
769 while ( tlp_num < pdata->nworker ) { tlp_yield(); }
773 pdata->num_moves_counted = 0;
776 ini_param( &pdata->param );
780 if ( pdata->nworker > 1 ) { lock( &tlp_lock ); }
782 iret = read_buf( pdata->buf, pdata->pf_tmp );
784 if ( pdata->nworker > 1 ) { unlock( &tlp_lock ); }
787 if ( iret <= 0 ) { break; } /* 0: eof, -2: error */
790 iret = ini_game( ptree, &min_posi_no_handicap, flag_nofmargin,
792 if ( iret < 0 ) { break; }
796 for ( imove = 0; pos_buf < nbuf; imove++ )
798 record_move = s2move( ptree, pdata->buf[pos_buf++], turn );
800 if ( pdata->buf[pos_buf] )
802 pos_buf = calc_deriv( pdata, pos_buf, turn );
806 MakeMove( turn, record_move, 1 );
808 ptree->move_last[1] = ptree->move_last[0];
809 ptree->nsuc_check[0] = 0;
811 = (unsigned char)( InCheck( turn ) ? 1U : 0 );
816 if ( pdata->nworker > 1 )
830 read_buf( unsigned short *buf, FILE *pf )
834 size = fread( buf, sizeof(unsigned short), 2, pf );
835 if ( ! size && feof( pf ) ) { return 0; }
838 str_error = str_io_error;
842 size = (size_t)buf[1] + (size_t)buf[0] * 0x10000 - 2;
843 if ( fread( buf+2, sizeof(unsigned short), size, pf ) != size )
845 str_error = str_io_error;
854 calc_deriv( parse2_data_t *pdata, int pos0, int turn0 )
856 double target, dT, sum_dT;
857 tree_t * restrict ptree;
858 const unsigned short *buf;
860 int ply, turn, pv_length, pos, record_value, value;
862 ptree = pdata->ptree;
872 pdata->pv[ply] = s2move( ptree, buf[ pos+ply-1 ] & 0x7fffU, turn );
873 MakeMove( turn, pdata->pv[ply], ply );
875 if ( buf[ pos+ply-1 ] & 0x8000U ) { break; }
880 record_value = evaluate( ptree, ply+1, turn );
881 if ( turn != turn0 ) { record_value = -record_value; }
885 UnMakeMove( turn, pdata->pv[ply], ply );
886 if ( ply == 1 ) { break; }
891 while ( buf[ pos ] ) {
894 pdata->pv[ply] = s2move( ptree, buf[ pos+ply-1 ] & 0x7fffU, turn );
895 MakeMove( turn, pdata->pv[ply], ply );
897 if ( buf[ pos+ply-1 ] & 0x8000U ) { break; }
902 value = evaluate( ptree, ply+1, turn );
903 if ( turn != turn0 ) { value = -value; }
904 target += func( value - record_value );
906 dT = dfunc( value - record_value );
907 if ( turn0 ) { dT = -dT; }
909 inc_param( ptree, &pdata->param, -dT );
913 UnMakeMove( turn, pdata->pv[ply], ply );
914 if ( ply == 1 ) { break; }
924 pdata->pv[ply] = s2move( ptree, buf[ pos0+ply-1 ] & 0x7fffU, turn );
925 MakeMove( turn, pdata->pv[ply], ply );
927 if ( buf[ pos0+ply-1 ] & 0x8000U ) { break; }
931 inc_param( ptree, &pdata->param, sum_dT );
935 UnMakeMove( turn, pdata->pv[ply], ply );
936 if ( ply == 1 ) { break; }
940 pdata->num_moves_counted += nc;
941 pdata->target += target;
950 const double delta = (double)FV_WINDOW / 7.0;
953 if ( x < -FV_WINDOW ) { x = -FV_WINDOW; }
954 else if ( x > FV_WINDOW ) { x = FV_WINDOW; }
955 d = 1.0 / ( 1.0 + exp(-x/delta) );
964 const double delta = (double)FV_WINDOW / 7.0;
965 double dd, dn, dtemp, dret;
967 if ( x <= -FV_WINDOW ) { dret = 0.0; }
968 else if ( x >= FV_WINDOW ) { dret = 0.0; }
970 dn = exp( - x / delta );
972 dd = delta * dtemp * dtemp;
981 s2move( const tree_t * restrict ptree, unsigned int move, int tt )
986 if ( from < nsquare )
989 move |= tt ? (Piece2Move(-BOARD[from])|Cap2Move( BOARD[to]))
990 : (Piece2Move( BOARD[from])|Cap2Move(-BOARD[to]));
997 ini_book( book_entry_t *pbook_entry )
1001 for ( i = 0; i < NumBookEntry; i++ )
1002 for ( j = 0; j < NumBookCluster; j++ ) {
1003 pbook_entry[i].cluster[j].a = (uint64_t)0;
1004 pbook_entry[i].cluster[j].b = (uint64_t)0x1ffU << 41;
1018 book_probe_learn( const tree_t * restrict ptree,
1019 book_entry_t *pbook_entry,
1020 int ply, unsigned int move )
1027 + ((unsigned int)HASH_KEY & (unsigned int)(NumBookEntry-1));
1029 for ( i = 0; i < NumBookCluster; i++ )
1030 if ( p->cluster[i].a == HASH_KEY
1031 && ((unsigned int)p->cluster[i].b & 0x1fffffU) == HAND_B
1032 && ( (((unsigned int)p->cluster[i].b>>21) & 0x1U)
1033 == (unsigned int)root_turn )
1034 && ((unsigned int)(p->cluster[i].b>>22) & 0x7ffffU) == move ) {
1038 for ( i = 0; i < NumBookCluster; i++ )
1039 if ( ( (unsigned int)( p->cluster[i].b >> 41 ) & 0x1ffU )
1040 > (unsigned int)ply ) { break; }
1042 if ( i < NumBookCluster ) {
1043 for ( j = NumBookCluster-1; j > i; j-- ) {
1044 p->cluster[j].a = p->cluster[j-1].a;
1045 p->cluster[j].b = p->cluster[j-1].b;
1048 p->cluster[i].a = HASH_KEY;
1050 = ( (uint64_t)move<<22 ) | ( (uint64_t)ply << 41 )
1051 | (uint64_t)( (root_turn<<21) | HAND_B );
1059 rep_check_learn( tree_t * restrict ptree, int ply )
1063 n = root_nrep + ply - 1;
1064 imin = n - REP_MAX_PLY;
1065 if ( imin < 0 ) { imin = 0; }
1067 for ( i = n-2; i >= imin; i -= 2 )
1068 if ( ptree->rep_board_list[i] == HASH_KEY
1069 && ptree->rep_hand_list[i] == HAND_B ) { return four_fold_rep; }
1074 #endif /* no MINIMUM */