- // Static null move pruning. We're betting that the opponent doesn't have
- // a move that will reduce the score by more than FutilityMargins[int(depth)]
- // if we do a null move.
- if ( !isCheck
- && allowNullmove
- && depth < RazorDepth
- && staticValue - futility_margin(depth, 0) >= beta)
- return staticValue - futility_margin(depth, 0);
-
- // Null move search
- if ( allowNullmove
+ // Step 6. Razoring
+ if ( refinedValue < beta - razor_margin(depth)
+ && ttMove == MOVE_NONE
+ && ss[ply - 1].currentMove != MOVE_NULL
+ && depth < RazorDepth
+ && !isCheck
+ && !value_is_mate(beta)
+ && !pos.has_pawn_on_7th(pos.side_to_move()))
+ {
+ Value rbeta = beta - razor_margin(depth);
+ Value v = qsearch(pos, ss, rbeta-1, rbeta, Depth(0), ply, threadID);
+ if (v < rbeta)
+ // Logically we should return (v + razor_margin(depth)), but
+ // surprisingly this did slightly weaker in tests.
+ return v;
+ }
+
+ // Step 7. Static null move pruning
+ // We're betting that the opponent doesn't have a move that will reduce
+ // the score by more than futility_margin(depth) if we do a null move.
+ if ( nullStatus == ALLOW_NULLMOVE
+ && depth < RazorDepth
+ && !isCheck
+ && !value_is_mate(beta)
+ && ok_to_do_nullmove(pos)
+ && refinedValue >= beta + futility_margin(depth, 0))
+ return refinedValue - futility_margin(depth, 0);
+
+ // Step 8. Null move search with verification search
+ // When we jump directly to qsearch() we do a null move only if static value is
+ // at least beta. Otherwise we do a null move if static value is not more than
+ // NullMoveMargin under beta.
+ if ( nullStatus == ALLOW_NULLMOVE