7 static int CONV save_result( tree_t * restrict ptree, int value, int beta,
11 static int CONV mpv_set_bound( int alpha );
12 static int CONV mpv_find_min( int *pnum );
13 static int CONV mpv_add_result( tree_t * restrict ptree, int value );
14 static void CONV mpv_sub_result( unsigned int move );
15 static void CONV mpv_out( tree_t * restrict ptree, int turn,
19 #if defined(NO_STDOUT) && defined(NO_LOGGING)
20 # define NextRootMove(a,b) next_root_move(a)
21 static int CONV next_root_move( tree_t * restrict ptree );
23 # define NextRootMove(a,b) next_root_move(a,b)
24 static int CONV next_root_move( tree_t * restrict ptree, int turn );
28 search_wrapper( tree_t * restrict ptree, int alpha, int beta, int turn,
29 int depth, int ply, unsigned int state_node )
33 #if defined(DFPN_CLIENT)
34 /* try beta cut using results from DFPN server */
35 if ( root_move_list[root_index].dfpn_cresult == dfpn_client_win )
37 if ( ! root_index ) { Out( "- best move is proofed, skip.\n" ); }
38 ptree->pv[1].a[1] = MOVE_LAST;
39 ptree->pv[1].length = 1;
40 ptree->pv[1].depth = 0;
41 ptree->pv[1].type = no_rep;
43 return score_matelong;
47 value = search( ptree, alpha, beta, turn, depth, ply, state_node );
49 #if defined(DFPN_CLIENT)
50 /* catch a signal, i.e., the first move has to be ignored */
51 if ( game_status & flag_skip_root_move )
55 Out( "- %s is proofed while searching.\n", str_CSA_move(MOVE_LAST) );
58 Out( "- %s (best move) is proofed while searching.\n",
59 str_CSA_move(MOVE_LAST) );
62 game_status &= ~flag_skip_root_move;
65 ptree->pv[1].a[1] = MOVE_LAST;
66 ptree->pv[1].length = 1;
67 ptree->pv[1].depth = 0;
68 ptree->pv[1].type = no_rep;
70 return score_matelong;
79 searchr( tree_t * restrict ptree, int alpha, int beta, int turn, int depth )
81 uint64_t root_nodes_start;
82 int value, first_move;
83 int new_depth, extension, ply, state_node_new;
93 ptree->move_last[1] = ptree->move_last[0];
95 while( NextRootMove( ptree, turn ) )
97 root_nodes_start = ptree->node_searched;
99 if ( usi_mode != usi_off )
101 csa2usi( ptree, str_CSA_move(MOVE_CURR), str_usi );
105 MakeMove( turn, MOVE_CURR, 1 );
107 assert( ! InCheck( turn ) );
109 state_node_new = ( node_do_mate | node_do_null | node_do_futile
110 | node_do_recap | node_do_recursion
112 if ( InCheck(Flip(turn)) )
114 ptree->check_extension_done++;
115 ptree->nsuc_check[2] = (unsigned char)( ptree->nsuc_check[0] + 1U );
116 extension = EXT_CHECK - PLY_INC;
119 extension = - PLY_INC;
120 ptree->nsuc_check[2] = 0;
126 if ( root_mpv ) { bound = alpha; }
128 new_depth = depth + extension;
129 value = -search_wrapper( ptree, -beta, -alpha, Flip(turn),
130 new_depth, 2, state_node_new );
133 UnMakeMove( turn, MOVE_CURR, 1 );
137 if ( value <= alpha )
139 UnMakeMove( turn, MOVE_CURR, 1 );
148 bound = mpv_set_bound( alpha );
149 if ( depth + extension >= PLY_INC || ptree->nsuc_check[2] )
151 new_depth = depth + extension;
153 value = -search_wrapper( ptree, -bound-1, -bound, Flip(turn),
154 new_depth, 2, state_node_new );
155 if ( ! root_abort && bound < value )
157 new_depth = depth + extension;
158 value = -search_wrapper( ptree, -beta, -bound, Flip(turn),
159 new_depth, 2, state_node_new );
163 UnMakeMove( turn, MOVE_CURR, 1 );
168 value = -search_quies( ptree, -beta, -bound, Flip(turn), 2, 1 );
173 int depth_reduced = 0;
174 new_depth = depth + extension;
177 if ( 2*PLY_INC <= new_depth
178 && ! ptree->nsuc_check[ply]
179 && ! UToCap(MOVE_CURR)
180 && ! ( I2IsPromote(MOVE_CURR)
181 && I2PieceMove(MOVE_CURR) != silver ) )
183 unsigned int key = phash( MOVE_CURR, turn );
184 unsigned int good = ptree->hist_good[key] + 1;
185 unsigned int triedx8 = ( ptree->hist_tried[key] + 2 ) * 8U;
187 if ( good *160U < triedx8 ) { depth_reduced = PLY_INC * 3/2; }
188 else if ( good * 50U < triedx8 ) { depth_reduced = PLY_INC * 2/2; }
189 else if ( good * 19U < triedx8 ) { depth_reduced = PLY_INC * 1/2; }
191 depth_reduced = PLY_INC;
192 new_depth -= depth_reduced;
195 value = -search_wrapper( ptree, -alpha-1, -alpha, Flip(turn),
196 new_depth, 2, state_node_new );
197 if ( ! root_abort && alpha < value && depth_reduced )
199 new_depth += depth_reduced;
200 value = -search_wrapper( ptree, -alpha-1, -alpha, Flip(turn),
201 new_depth, 2, state_node_new );
205 UnMakeMove( turn, MOVE_CURR, 1 );
211 #if defined(DFPN_CLIENT)
212 if ( dfpn_client_sckt != SCKT_NULL
213 && 4 < iteration_depth
214 && dfpn_client_best_move != MOVE_CURR )
216 dfpn_client_best_move = MOVE_CURR;
217 lock( &dfpn_client_lock );
218 dfpn_client_out( "BEST MOVE %s\n",
219 str_CSA_move(dfpn_client_best_move) );
220 unlock( &dfpn_client_lock );
223 MnjOut( "pid=%d move=%s v=%dl n=% " PRIu64 "%s\n",
224 mnj_posi_id, str_CSA_move(MOVE_CURR), alpha+1,
225 ptree->node_searched,
226 ( mnj_depth_stable <= iteration_depth ) ? " stable" : "" );
229 if ( usi_mode != usi_off )
231 USIOut( "info depth %d score cp %d nodes %" PRIu64 " pv %s\n",
232 iteration_depth, alpha+1, ptree->node_searched,
237 new_depth = depth + extension;
239 value = -search_wrapper( ptree, -beta, -alpha, Flip(turn),
240 new_depth, 2, state_node_new );
247 UnMakeMove( turn, MOVE_CURR, 1 );
248 pv_close( ptree, 2, pv_fail_high );
249 time_last_result = time_last_check;
250 root_value = alpha+1;
251 ptree->pv[0] = ptree->pv[1];
254 dvalue = -(double)alpha;
261 str = str_CSA_move(MOVE_CURR);
262 Out( " %7.2f 1.%c%s [%c0!]\n",
263 dvalue / 100.0, ch, str, ch );
264 if ( game_status & flag_pondering )
266 OutCsaShogi( "info%+.2f %c%s %c%s [%c0!]\n",
267 dvalue / 100.0, ach_turn[Flip(turn)],
268 str_CSA_move(ponder_move),
272 OutCsaShogi( "info%+.2f %c%s [%c0!]\n",
273 dvalue / 100.0, ch, str, ch );
280 UnMakeMove( turn, MOVE_CURR, 1 );
282 root_move_list[root_index].nodes
283 = ptree->node_searched - root_nodes_start;
286 if ( root_mpv && value < beta )
288 mpv_sub_result( MOVE_CURR );
289 if ( bound < value && mpv_add_result( ptree, value ) < 0 )
291 game_status |= flag_search_error;
299 if ( save_result( ptree, value, beta, turn ) < 0 )
301 game_status |= flag_search_error;
304 if ( beta <= value ) { return value; }
308 if ( root_abort ) { return 0; }
315 out_pv( tree_t * restrict ptree, int value, int turn, unsigned int time )
326 is_out = ( 4 < iteration_depth || abs(value) > score_max_eval ) ? 1 : 0;
329 if ( root_mpv ) { is_out = 0; }
333 if ( usi_mode != usi_off )
343 str = str_time_symple( time );
344 dvalue = (double)( turn ? -value : value ) / 100.0;
345 OutCsaShogi( "info%+.2f", dvalue );
346 if ( game_status & flag_pondering )
348 OutCsaShogi( " %c%s", ach_turn[Flip(turn)],
349 str_CSA_move(ponder_move) );
352 if ( ptree->pv[0].length )
354 if ( root_move_list[root_index].status & flag_first )
356 Out( " %2d %6s %7.2f ", iteration_depth, str, dvalue );
358 else { Out( " %6s %7.2f ", str, dvalue ); }
362 for ( ply = 1; ply <= ptree->pv[0].length; ply++ )
366 if ( ply > 1 && ! ( (ply-1) % 5 ) )
370 str = str_CSA_move( ptree->pv[0].a[ply] );
371 OutCsaShogi( " %c%s", ach_turn[tt], str );
372 Out( "%2d.%c%-7s", ply, ach_turn[tt], str );
375 if ( usi_mode != usi_off && ply <= 4 )
378 csa2usi( ptree, str_CSA_move(ptree->pv[0].a[ply]), str_usi );
379 ipv += snprintf( str_pv + ipv, 256 - ipv, " %s", str_usi );
384 MakeMove( tt, ptree->pv[0].a[ply], ply );
389 if ( ptree->pv[0].type == hash_hit )
394 for ( ; ply < PLY_MAX; ply++ )
397 ptree->amove_hash[ply] = 0;
398 value_type = hash_probe( ptree, ply, 0, tt, -score_bound,
399 score_bound, &dummy );
400 if ( ! ( value_type == value_exact
401 && value == HASH_VALUE
402 && is_move_valid( ptree, ptree->amove_hash[ply], tt ) ) )
406 ptree->pv[0].a[ply] = ptree->amove_hash[ply];
407 for ( i = 1; i < ply; i++ )
408 if ( ptree->pv[0].a[i] == ptree->pv[0].a[ply] ) { goto rep_esc; }
412 if ( ply > 1 && ! ( (ply-1) % 5 ) )
416 str = str_CSA_move(ptree->pv[0].a[ply]);
417 OutCsaShogi( " %c%s", ach_turn[tt], str );
418 Out( "%2d:%c%-7s", ply, ach_turn[tt], str );
421 if ( usi_mode != usi_off && ply <= 4 )
424 csa2usi( ptree, str_CSA_move(ptree->pv[0].a[ply]), str_usi );
425 ipv += snprintf( str_pv + ipv, 256 - ipv, " %s", str_usi );
430 MakeMove( tt, ptree->pv[0].a[ply], ply );
433 UnMakeMove( tt, ptree->amove_hash[ply], ply );
436 ptree->pv[0].length++;
443 if ( is_out && ptree->pv[0].type != no_rep )
445 if ( (((ply-1) % 5) == 0) && (ply != 1) )
450 switch ( ptree->pv[0].type )
452 case perpetual_check: str = "PER. CHECK"; break;
453 case four_fold_rep: str = "REPETITION"; break;
454 case black_superi_rep:
455 case white_superi_rep: str = "SUPERI. POSI."; break;
456 case prev_solution: str = "PREV. SEARCH"; break;
457 case hash_hit: str = "HASH HIT"; break;
458 case book_hit: str = "BOOK HIT"; break;
459 case pv_fail_high: str = "FAIL HIGH"; break;
460 case mate_search: str = "MATE SEARCH"; break;
462 if ( str != NULL ) { Out( " <%s>", str ); }
464 for ( ply--; ply >= 1; ply-- )
468 UnMakeMove( tt, ptree->pv[0].a[ply], ply );
475 USIOut( "info depth %d score cp %d nodes %" PRIu64 " pv%s\n",
476 iteration_depth, value, ptree->node_searched, str_pv );
482 save_result( tree_t * restrict ptree, int value, int beta, int turn )
484 root_move_t root_move_temp;
487 if ( get_elapsed( &time_last_result ) < 0 ) { return -1; }
492 root_move_temp = root_move_list[index];
493 for ( ; index > 0; index-- )
495 root_move_list[index] = root_move_list[index-1];
497 root_move_list[0] = root_move_temp;
500 if ( beta <= value ) { pv_close( ptree, 2, pv_fail_high ); }
502 ptree->pv[0] = ptree->pv[1];
507 #if defined(DFPN_CLIENT)
508 if ( dfpn_client_sckt != SCKT_NULL
509 && 4 < iteration_depth
510 && dfpn_client_best_move != ptree->pv[1].a[1] )
512 dfpn_client_best_move = ptree->pv[1].a[1];
513 lock( &dfpn_client_lock );
514 dfpn_client_out( "BEST MOVE %s\n",
515 str_CSA_move(dfpn_client_best_move) );
516 unlock( &dfpn_client_lock );
519 MnjOut( "pid=%d move=%s v=%de n=%" PRIu64 "%s\n",
520 mnj_posi_id, str_CSA_move(ptree->pv[1].a[1]), value,
521 ptree->node_searched,
522 ( mnj_depth_stable <= iteration_depth ) ? " stable" : "" );
524 out_pv( ptree, value, turn, time_last_result - time_start );
532 #if defined(NO_STDOUT) && defined(NO_LOGGING)
533 next_root_move( tree_t * restrict ptree )
535 next_root_move( tree_t * restrict ptree, int turn )
541 for ( i = 0; i < n; i++ )
543 if ( root_move_list[i].status & flag_searched ) { continue; }
544 root_move_list[i].status |= flag_searched;
545 ptree->current_move[1] = root_move_list[i].move;
548 #if ! ( defined(NO_STDOUT) && defined(NO_LOGGING) )
549 if ( iteration_depth > 5 )
551 const char *str_move;
554 str_move = str_CSA_move(ptree->current_move[1]);
555 snprintf( str, 9, "%d/%d", i+1, root_nmove );
556 if ( root_move_list[i].status & flag_first )
558 Out( "(%2d) %7s* 1.%c%s \r",
559 iteration_depth, str, ach_turn[turn], str_move );
562 Out( " %7s* 1.%c%s \r",
563 str, ach_turn[turn], str_move );
577 mpv_out( tree_t * restrict ptree, int turn, unsigned int time )
579 int mpv_out, ipv, best;
581 best = (int)mpv_pv[0].a[0] - 32768;
582 mpv_out = ( iteration_depth > 3 || abs(best) > score_max_eval ) ? 1 : 0;
584 for ( ipv = 0; mpv_pv[ipv].length; ipv++ )
588 int tt, is_out, value, ply;
590 assert( ipv < mpv_num*2 );
592 value = (int)mpv_pv[ipv].a[0] - 32768;
593 if ( mpv_out && value > best - mpv_width && ipv < mpv_num )
601 dvalue = (double)( turn ? -value : value ) / 100.0;
602 if ( is_out && ! ipv ) { OutCsaShogi( "info" ); }
603 if ( is_out && ipv ) { OutCsaShogi( ":" ); }
605 OutCsaShogi( "%+.2f", dvalue );
606 if ( game_status & flag_pondering )
608 OutCsaShogi( " %c%s", ach_turn[Flip(turn)],
609 str_CSA_move(ponder_move) );
612 str = str_time_symple( time );
613 ply = mpv_pv[ipv].depth;
614 if ( ! ipv ) { Out( "o%2d %6s %7.2f ", ply, str, dvalue ); }
615 else { Out( " %2d %7.2f ", ply, dvalue ); }
618 for ( ply = 1; ply <= mpv_pv[ipv].length; ply++ )
622 if ( ply > 1 && ! ( (ply-1) % 5 ) )
626 str = str_CSA_move(mpv_pv[ipv].a[ply]);
627 OutCsaShogi( " %c%s", ach_turn[tt], str );
628 Out( "%2d.%c%-11s", ply, ach_turn[tt], str );
631 assert( is_move_valid( ptree, mpv_pv[ipv].a[ply], tt ) );
632 MakeMove( tt, mpv_pv[ipv].a[ply], ply );
637 if ( mpv_pv[ipv].type == hash_hit )
642 for ( ; ply < PLY_MAX; ply++ )
645 ptree->amove_hash[ply] = 0;
646 value_type = hash_probe( ptree, ply, 0, tt, -score_bound,
647 score_bound, &dummy );
648 if ( ! ( value_type == value_exact
649 && value == HASH_VALUE
650 && is_move_valid(ptree,ptree->amove_hash[ply],tt) ) )
654 mpv_pv[ipv].a[ply] = ptree->amove_hash[ply];
655 for ( i = 1; i < ply; i++ )
656 if ( mpv_pv[ipv].a[i]
657 == mpv_pv[ipv].a[ply] ) { goto rep_esc; }
661 if ( ply > 1 && ! ( (ply-1) % 5 ) )
665 str = str_CSA_move(mpv_pv[ipv].a[ply]);
666 OutCsaShogi( " %c%s", ach_turn[tt], str );
667 Out( "%2d:%c%-11s", ply, ach_turn[tt], str );
670 MakeMove( tt, mpv_pv[ipv].a[ply], ply );
673 UnMakeMove( tt, ptree->amove_hash[ply], ply );
676 mpv_pv[ipv].length++;
683 if ( is_out && mpv_pv[ipv].type != no_rep )
685 if ( (((ply-1) % 5) == 0) && (ply != 1) )
690 switch ( mpv_pv[ipv].type )
692 case perpetual_check: str = "PER. CHECK"; break;
693 case four_fold_rep: str = "REPETITION"; break;
694 case black_superi_rep:
695 case white_superi_rep: str = "SUPERI. POSI."; break;
696 case prev_solution: str = "PREV. SEARCH"; break;
697 case hash_hit: str = "HASH HIT"; break;
698 case book_hit: str = "BOOK HIT"; break;
699 case pv_fail_high: str = "FAIL HIGH"; break;
700 case mate_search: str = "MATE SEARCH"; break;
702 if ( str != NULL ) { Out( " <%s>", str ); }
704 for ( ply--; ply >= 1; ply-- )
707 UnMakeMove( tt, mpv_pv[ipv].a[ply], ply );
710 if ( is_out ) { Out( "\n" ); }
713 if ( mpv_out ) { OutCsaShogi( "\n" ); }
718 mpv_sub_result( unsigned int move )
722 for ( i = 0; mpv_pv[i].length; i++ )
724 assert( i < mpv_num*2 );
725 assert( i < root_nmove*2 );
726 assert( mpv_pv[i].depth <= iteration_depth );
727 assert( -score_bound < (int)mpv_pv[i].a[0]-32768 );
728 assert( (int)mpv_pv[i].a[0]-32768 < score_bound );
730 if ( mpv_pv[i].a[1] == move ) { break; }
733 for ( ; mpv_pv[i].length; i++ )
735 assert( i < mpv_num*2 );
736 assert( i < root_nmove*2 );
737 assert( mpv_pv[i].depth <= iteration_depth );
738 assert( -score_bound < (int)mpv_pv[i].a[0]-32768 );
739 assert( (int)mpv_pv[i].a[0]-32768 < score_bound );
741 mpv_pv[i] = mpv_pv[i+1];
747 mpv_add_result( tree_t * restrict ptree, int value )
753 vmin = mpv_find_min( &num );
754 assert( num <= mpv_num );
755 assert( num < root_nmove );
756 assert( -score_bound < value );
757 assert( value < score_bound );
759 /* remove the weakest pv if all of slots are full */
760 if ( num == mpv_num )
762 for ( i = 0; mpv_pv[i].length; i++ )
764 assert( i < mpv_num*2 );
765 assert( i < root_nmove*2 );
766 assert( mpv_pv[i].depth <= iteration_depth );
767 assert( -score_bound < (int)mpv_pv[i].a[0]-32768 );
768 assert( (int)mpv_pv[i].a[0]-32768 < score_bound );
770 if ( mpv_pv[i].depth == iteration_depth
771 && mpv_pv[i].a[0] == (unsigned int)(vmin+32768) ) { break; }
773 assert( i != mpv_num*2 );
774 assert( mpv_pv[i].length );
776 assert( i < mpv_num*2 );
777 assert( i < root_nmove*2 );
778 mpv_pv[i] = mpv_pv[i+1];
780 } while ( mpv_pv[i].length );
784 for ( i = 0; mpv_pv[i].length; i++ )
786 assert( i < mpv_num*2 );
787 assert( i < root_nmove*2 );
788 assert( -score_bound < (int)mpv_pv[i].a[0]-32768 );
789 assert( (int)mpv_pv[i].a[0]-32768 < score_bound );
791 if ( mpv_pv[i].a[0] < (unsigned int)(value+32768) ) { break; }
795 pv.a[0] = (unsigned int)(value+32768);
797 assert( i < mpv_num*2 );
798 assert( i < root_nmove*2 );
803 } while ( pv.length );
805 if ( get_elapsed( &time ) < 0 ) { return -1; }
807 mpv_out( ptree, root_turn, time - time_start );
814 mpv_set_bound( int alpha )
816 int bound, num, value;
818 bound = alpha - mpv_width;
819 if ( bound < -score_bound ) { bound = -score_bound; }
821 value = mpv_find_min( &num );
822 assert( num <= mpv_num );
823 assert( num < root_nmove );
825 assert( -score_bound < value );
826 assert( value < score_bound );
827 if ( num == mpv_num && bound < value ) { bound = value; }
834 mpv_find_min( int *pnum )
837 unsigned int a[ MPV_MAX_PV+1 ], u, utemp;
841 for ( i = 0; mpv_pv[i].length; i++ )
843 assert( i < mpv_num*2 );
844 assert( i < root_nmove*2 );
845 assert( mpv_pv[i].depth <= iteration_depth );
847 if ( mpv_pv[i].depth == iteration_depth )
850 assert( -score_bound < (int)u-32768 );
851 assert( (int)u-32768 < score_bound );
853 for ( num = 0; u <= a[num]; num++ );
855 assert( num < mpv_num );
856 assert( num < root_nmove );
866 if ( pnum ) { *pnum = num; }
868 if ( num ) { return (int)a[num-1] - 32768; }