@@ -436,20 +436,20 @@ static int search_shallow(Search *search, const int alpha, bool pass1)
436436 } while ((prioritymoves = moves )); // (23%)
437437 ++ search -> eval .n_empties ;
438438 }
439- // search->board = board0.board;
439+ // search->board = board0.board; // restore in caller
440440 // search->eval.parity = parity0;
441441
442442 assert (SCORE_MIN <= bestscore && bestscore <= SCORE_MAX );
443443 return bestscore ; // (33%)
444444}
445445
446446/**
447- * @brief Evaluate an endgame position with a Null Window Search algorithm. (7..9 empties)
448- *
449- * This function is used when there are still many empty squares on the board. Move
450- * ordering, hash table cutoff, enhanced transposition cutoff, etc. are used in
451- * order to diminish the size of the tree to analyse, but at the expense of a
452- * slower speed.
447+ * @brief Evaluate an endgame position with a Null Window Search algorithm,
448+ * with thread-local lockfree hash. (7..10 empties)
449+
450+ * Lightweight transposition table with thread local (thus lockfree) 1-way hash
451+ * is used for 7..10 empties (and occasionally less from PVS).
452+ * http://www.amy.hi-ho.ne.jp/okuhara/edaxopt.htm#localhash
453453 *
454454 * @param search Search.
455455 * @param alpha Alpha bound.
@@ -458,29 +458,27 @@ static int search_shallow(Search *search, const int alpha, bool pass1)
458458
459459static int NWS_endgame_local (Search * search , const int alpha )
460460{
461- int score , ofssolid , bestscore ;
462- unsigned long long hash_code , solid_opp ;
461+ int score , ofssolid , bestmove , bestscore , lower , upper ;
462+ unsigned long long solid_opp ;
463463 // const int beta = alpha + 1;
464- HashStoreData hash_data ;
464+ Hash * hash_entry ;
465465 Move * move ;
466- long long nodes_org ;
467- V2DI board0 ;
468- Board hashboard ;
466+ // long long nodes_org;
467+ V2DI board0 , hashboard ;
469468 unsigned int parity0 ;
470469 unsigned long long full [5 ];
471470 MoveList movelist ;
472471
473- assert (bit_count (~(search -> board .player |search -> board .opponent )) < DEPTH_MIDGAME_TO_ENDGAME );
472+ assert (bit_count (~(search -> board .player |search -> board .opponent )) < DEPTH_TO_USE_LOCAL_HASH );
474473 assert (SCORE_MIN <= alpha && alpha <= SCORE_MAX );
475474
476- if (search -> stop ) return alpha ;
477-
478475 SEARCH_STATS (++ statistics .n_NWS_endgame );
479476 SEARCH_UPDATE_INTERNAL_NODES (search -> n_nodes );
480477
481478 // stability cutoff
482- hashboard = board0 .board = search -> board ;
479+ board0 . board = hashboard .board = search -> board ;
483480 ofssolid = 0 ;
481+ // search_SC_NWS(search, alpha, &score)
484482 if (USE_SC && alpha >= NWS_STABILITY_THRESHOLD [search -> eval .n_empties ]) { // (7%)
485483 CUTOFF_STATS (++ statistics .n_stability_try ;)
486484 score = SCORE_MAX - 2 * get_stability_fulls (search -> board .opponent , search -> board .player , full );
@@ -492,99 +490,83 @@ static int NWS_endgame_local(Search *search, const int alpha)
492490 // Improvement of Serch by Reducing Redundant Information in a Position of Othello
493491 // Hidekazu Matsuo, Shuji Narazaki
494492 // http://id.nii.ac.jp/1001/00156359/
495- solid_opp = full [4 ] & hashboard .opponent ; // full[4] = all full
496- #ifndef POPCOUNT
493+ solid_opp = full [4 ] & hashboard .board . opponent ; // full[4] = all full
494+ #ifndef POPCOUNT
497495 if (solid_opp ) // (72%)
498- #endif
496+ #endif
499497 {
500- hashboard .player ^= solid_opp ; // normalize solid to player
501- hashboard .opponent ^= solid_opp ;
498+ #ifdef hasSSE2
499+ hashboard .v2 = _mm_xor_si128 (hashboard .v2 , _mm_set1_epi64x (solid_opp ));
500+ #else
501+ hashboard .board .player ^= solid_opp ; // normalize solid to player
502+ hashboard .board .opponent ^= solid_opp ;
503+ #endif
502504 ofssolid = bit_count (solid_opp ) * 2 ; // hash score is ofssolid grater than real
503505 }
504506 }
505507
506- hash_code = board_get_hash_code (& hashboard );
507- PREFETCH (search -> thread_hash . hash + ( hash_code & search -> thread_hash . hash_mask ) );
508+ hash_entry = search -> thread_hash . hash + ( board_get_hash_code (& hashboard . board ) & search -> thread_hash . hash_mask );
509+ // PREFETCH(hash_entry );
508510
509511 search_get_movelist (search , & movelist );
510512
511- if (movelist .n_moves > 1 ) { // (96%)
513+ if (movelist .n_moves > 0 ) {
512514 // transposition cutoff
513- if (hash_get_local (& search -> thread_hash , & hashboard , hash_code , & hash_data .data )) { // (6%)
514- hash_data .data .lower -= ofssolid ;
515- hash_data .data .upper -= ofssolid ;
516- if (search_TC_NWS (& hash_data .data , search -> eval .n_empties , NO_SELECTIVITY , alpha , & score )) // (6%)
517- return score ;
515+ // hash_get(&search->thread_hash, &hashboard.board, hash_code, &hash_data.data)
516+ unsigned char hashmove [2 ] = { NOMOVE , NOMOVE };
517+ if (vboard_equal (hashboard , & hash_entry -> board )) { // (6%)
518+ hashmove [0 ] = hash_entry -> data .move [0 ];
519+ lower = hash_entry -> data .lower - ofssolid ;
520+ upper = hash_entry -> data .upper - ofssolid ;
521+ // search_TC_NWS(&hash_data.data, search->eval.n_empties, NO_SELECTIVITY, alpha, &score)
522+ if (USE_TC /* && (data->wl.c.selectivity >= NO_SELECTIVITY && data->wl.c.depth >= search->eval.n_empties) */ ) {
523+ CUTOFF_STATS (++ statistics .n_hash_try ;)
524+ if (alpha < lower ) {
525+ CUTOFF_STATS (++ statistics .n_hash_high_cutoff ;)
526+ return lower ;
527+ }
528+ if (alpha >= upper ) {
529+ CUTOFF_STATS (++ statistics .n_hash_low_cutoff ;)
530+ return upper ;
531+ }
532+ }
518533 }
519- // else if (ofssolid) // slows down
520- // hash_get_from_board(&search->thread_hash, HBOARD_V(board0), &hash_data.data);
521-
522- movelist_evaluate_fast (& movelist , search , & hash_data .data );
534+ if (movelist .n_moves > 1 )
535+ movelist_evaluate_fast (& movelist , search , hashmove );
523536
524- nodes_org = search -> n_nodes ;
537+ // nodes_org = search->n_nodes;
525538 parity0 = search -> eval .parity ;
526539 bestscore = - SCORE_INF ;
540+ -- search -> eval .n_empties ; // for next move
527541 // loop over all moves
528542 move = & movelist .move [0 ];
529- if (-- search -> eval .n_empties <= DEPTH_TO_SHALLOW_SEARCH ) // for next move (44%)
530- while ((move = move_next_best (move ))) { // (72%)
531- search -> eval .parity = parity0 ^ QUADRANT_ID [move -> x ];
543+ while ((move = move_next_best (move ))) { // (76%)
544+ search -> eval .parity = parity0 ^ QUADRANT_ID [move -> x ];
545+ vboard_update (& search -> board , board0 , move );
546+ if (search -> eval .n_empties <= DEPTH_TO_SHALLOW_SEARCH ) {
532547 search -> empties [search -> empties [move -> x ].previous ].next = search -> empties [move -> x ].next ; // remove - maintain single link only
533- vboard_update (& search -> board , board0 , move );
534548 score = - search_shallow (search , ~alpha , false);
535549 search -> empties [search -> empties [move -> x ].previous ].next = move -> x ; // restore
536- search -> board = board0 .board ;
537-
538- if (score > bestscore ) { // (63%)
539- bestscore = score ;
540- hash_data .data .move [0 ] = move -> x ;
541- if (bestscore > alpha ) break ; // (48%)
542- }
543- }
544- else
545- while ((move = move_next_best (move ))) { // (76%)
546- search -> eval .parity = parity0 ^ QUADRANT_ID [move -> x ];
550+ } else {
547551 empty_remove (search -> empties , move -> x );
548- vboard_update (& search -> board , board0 , move );
549552 score = - NWS_endgame_local (search , ~alpha );
550553 empty_restore (search -> empties , move -> x );
551- search -> board = board0 .board ;
554+ }
555+ search -> board = board0 .board ;
552556
553- if (score > bestscore ) { // (63%)
554- bestscore = score ;
555- hash_data .data .move [0 ] = move -> x ;
556- if (bestscore > alpha ) break ; // (39%)
557- }
557+ if (score > bestscore ) { // (63%)
558+ bestscore = score ;
559+ bestmove = move -> x ;
560+ if (bestscore > alpha ) break ; // (39%)
558561 }
562+ }
559563 ++ search -> eval .n_empties ;
560564 search -> eval .parity = parity0 ;
561565
562566 if (search -> stop ) // (1%)
563567 return alpha ;
564568
565- hash_data .data .wl .c .depth = search -> eval .n_empties ;
566- hash_data .data .wl .c .selectivity = NO_SELECTIVITY ;
567- // hash_data.data.wl.c.cost = last_bit(search->n_nodes - nodes_org);
568- // hash_data.data.move[0] = bestmove;
569- hash_data .alpha = alpha + ofssolid ;
570- hash_data .beta = alpha + ofssolid + 1 ;
571- hash_data .score = bestscore + ofssolid ;
572- hash_store_local (& search -> thread_hash , & hashboard , hash_code , & hash_data );
573-
574- // special cases
575- } else if (movelist .n_moves == 1 ) { // (3%)
576- parity0 = search -> eval .parity ;
577- move = movelist_first (& movelist );
578- search_swap_parity (search , move -> x );
579- empty_remove (search -> empties , move -> x );
580- vboard_update (& search -> board , board0 , move );
581- if (-- search -> eval .n_empties <= DEPTH_TO_SHALLOW_SEARCH ) // (56%)
582- bestscore = - search_shallow (search , ~alpha , false);
583- else bestscore = - NWS_endgame_local (search , ~alpha );
584- ++ search -> eval .n_empties ;
585- empty_restore (search -> empties , move -> x );
586- search -> eval .parity = parity0 ;
587- search -> board = board0 .board ;
569+ hash_store_local (hash_entry , hashboard , alpha + ofssolid , alpha + ofssolid + 1 , bestscore + ofssolid , bestmove );
588570
589571 } else { // (1%)
590572 if (can_move (search -> board .opponent , search -> board .player )) { // pass
@@ -608,7 +590,7 @@ static int NWS_endgame_local(Search *search, const int alpha)
608590}
609591
610592/**
611- * @brief Evaluate an endgame position with a Null Window Search algorithm. (10 ..15 empties)
593+ * @brief Evaluate an endgame position with a Null Window Search algorithm. (11 ..15 empties)
612594 *
613595 * This function is used when there are still many empty squares on the board. Move
614596 * ordering, hash table cutoff, enhanced transposition cutoff, etc. are used in
@@ -628,46 +610,37 @@ int NWS_endgame(Search *search, const int alpha)
628610 Move * move ;
629611 long long nodes_org ;
630612 V2DI board0 ;
631- Board hashboard ;
632613 unsigned int parity0 ;
633- unsigned long long full [5 ];
634614 MoveList movelist ;
635615
636616 assert (bit_count (~(search -> board .player |search -> board .opponent )) < DEPTH_MIDGAME_TO_ENDGAME );
637617 assert (SCORE_MIN <= alpha && alpha <= SCORE_MAX );
638618
619+ if (search -> stop ) return alpha ;
620+
639621 if (search -> eval .n_empties <= DEPTH_TO_USE_LOCAL_HASH )
640622 return NWS_endgame_local (search , alpha );
641623
642- if (search -> stop ) return alpha ;
643-
644624 SEARCH_STATS (++ statistics .n_NWS_endgame );
645625 SEARCH_UPDATE_INTERNAL_NODES (search -> n_nodes );
646626
647627 // stability cutoff
648- hashboard = board0 .board = search -> board ;
649- if (USE_SC && alpha >= NWS_STABILITY_THRESHOLD [search -> eval .n_empties ]) { // (7%)
650- CUTOFF_STATS (++ statistics .n_stability_try ;)
651- score = SCORE_MAX - 2 * get_stability (search -> board .opponent , search -> board .player );
652- if (score <= alpha ) { // (3%)
653- CUTOFF_STATS (++ statistics .n_stability_low_cutoff ;)
654- return score ;
655- }
656- }
628+ if (search_SC_NWS (search , alpha , & score )) return score ;
657629
658- hash_code = board_get_hash_code (& hashboard );
630+ hash_code = board_get_hash_code (& search -> board );
659631 hash_prefetch (& search -> hash_table , hash_code );
660632
661633 search_get_movelist (search , & movelist );
634+ board0 .board = search -> board ;
662635
663636 if (movelist .n_moves > 0 ) { // (96%)
664637 // transposition cutoff
665- if (hash_get (& search -> hash_table , & hashboard , hash_code , & hash_data .data )) { // (6%)
638+ if (hash_get (& search -> hash_table , & search -> board , hash_code , & hash_data .data )) { // (6%)
666639 if (search_TC_NWS (& hash_data .data , search -> eval .n_empties , NO_SELECTIVITY , alpha , & score )) // (6%)
667640 return score ;
668641 }
669642 if (movelist .n_moves > 1 )
670- movelist_evaluate_fast (& movelist , search , & hash_data .data );
643+ movelist_evaluate_fast (& movelist , search , hash_data .data . move );
671644
672645 nodes_org = search -> n_nodes ;
673646 parity0 = search -> eval .parity ;
@@ -702,7 +675,7 @@ int NWS_endgame(Search *search, const int alpha)
702675 hash_data .alpha = alpha ;
703676 hash_data .beta = alpha + 1 ;
704677 hash_data .score = bestscore ;
705- hash_store (& search -> hash_table , & hashboard , hash_code , & hash_data );
678+ hash_store (& search -> hash_table , & search -> board , hash_code , & hash_data );
706679
707680 } else { // (1%)
708681 if (can_move (search -> board .opponent , search -> board .player )) { // pass
0 commit comments