I can't make a very fast compile of it, it is only 32 bit but that should be enough for a fun engine. The changes are also small so maybe they could be merged in some way if they would turn out not to be bad, but I already said the thing is bad...
I only made it because I wanted to try out some new idea in nullmove and test if it could be built into the new Stockfish. But by itself I don't think the first idea, that was just more or less dreamt up for the occasion, will ever be worth incorporating into other programs. The idea was that if the nullmove follows a threat, the threat can be carried out without resistance because the opponent passed. Then you can not reduce the node, it is too tactical. But maybe if there would be only one threatmove and you then exclude that move from the possible 'free' moves, and when the other moves do not refute the nullmove, it would still be okay to reduce. The drawback is that you need an exclusion search in addition to the normal nullmove search, and you know there is at least one threat in the position. The first implemention I had was wrong, the exclusion search was never triggered and the search was identical to Stockfish 2.3, but much slower because the compile is not as fast as Jim's After a few corrections it was at least different, but not really as fast as Stockfish, not counting the compilation. I wanted it to be a bit like Ferrarifish, but then with a different search, so the futility pruning is a bit tightened, maybe with a little more risk. This change actually has a more visible impact than my change in nullmove, which is actually also an attempt at more positional play, especially in the verification search or a slight help with Zugzwang situations (I use a PV search there, so that is partially not using nullmove anymore, and the search tries to improve previous results of the verification in that node, so very extravagant for nodes that are likely to be pruned. But maybe in a few cases it will pay off, that's what I'm counting on.)
As a final change I also copied most of the material imbalance table from Rainbow Serpent, but I think that is more or less pink noise or something like that. A bit like the changed uniforms Spock and Kirk were wearing in the wrong universe.
That's it. Only three changes to Stockfish from the other universe. This is the change in nullmove.
Stockfish's nullmove:
Code: Select all
// Step 8. Null move search with verification search (is omitted in PV nodes)
if ( !PvNode
&& !ss->skipNullMove
&& depth > ONE_PLY
&& !inCheck
&& refinedValue >= beta
&& abs(beta) < VALUE_MATE_IN_MAX_PLY
&& pos.non_pawn_material(pos.side_to_move()))
{
ss->currentMove = MOVE_NULL;
// Null move dynamic reduction based on depth
Depth R = 3 * ONE_PLY + depth / 4;
// Null move dynamic reduction based on value
if (refinedValue - PawnValueMg > beta)
R += ONE_PLY;
pos.do_null_move<true>(st);
(ss+1)->skipNullMove = true;
nullValue = depth-R < ONE_PLY ? -qsearch<NonPV>(pos, ss+1, -beta, -alpha, DEPTH_ZERO)
: - search<NonPV>(pos, ss+1, -beta, -alpha, depth-R);
(ss+1)->skipNullMove = false;
pos.do_null_move<false>(st);
if (nullValue >= beta)
{
// Do not return unproven mate scores
if (nullValue >= VALUE_MATE_IN_MAX_PLY)
nullValue = beta;
if (depth < 6 * ONE_PLY)
return nullValue;
// Do verification search at high depths
ss->skipNullMove = true;
Value v = search<NonPV>(pos, ss, alpha, beta, depth-R);
ss->skipNullMove = false;
if (v >= beta)
return nullValue;
}
else
{
// The null move failed low, which means that we may be faced with
// some kind of threat. If the previous move was reduced, check if
// the move that refuted the null move was somehow connected to the
// move which was reduced. If a connection is found, return a fail
// low score (which will cause the reduced move to fail high in the
// parent node, which will trigger a re-search with full depth).
threatMove = (ss+1)->currentMove;
if ( depth < ThreatDepth
&& (ss-1)->reduction
&& threatMove != MOVE_NONE
&& connected_moves(pos, (ss-1)->currentMove, threatMove))
return beta - 1;
}
}
Code: Select all
// Step 8. Null move search with verification search (is omitted in PV nodes)
if ( !PvNode
&& !ss->skipNullMove
&& depth > ONE_PLY
&& !inCheck
&& refinedValue >= beta
&& abs(beta) < VALUE_MATE_IN_MAX_PLY
&& pos.non_pawn_material(pos.side_to_move()))
{
ss->currentMove = MOVE_NULL;
// Null move dynamic reduction based on depth
Depth R = 3 * ONE_PLY + depth / 4;
// Null move dynamic reduction based on value
if (refinedValue - PawnValueMg > beta)
R += ONE_PLY;
pos.do_null_move<true>(st);
(ss+1)->skipNullMove = true;
nullValue = depth-R < ONE_PLY ? -qsearch<NonPV>(pos, ss+1, -beta, -alpha, DEPTH_ZERO)
: - search<NonPV>(pos, ss+1, -beta, -alpha, depth-R);
if (nullValue < beta)
{// 'Nullexclusion Search'
threatMove = (ss)->currentMove;
CheckInfo ci(pos);
if ( threatMove != MOVE_NONE
&& pos.pl_move_is_legal(threatMove, ci.pinned)
&& depth-R >= SingularExtensionDepth[false]
&& !excludedMove // Recursive singular search is not allowed
&& connected_moves(pos, (ss-2)->currentMove, threatMove)
&& tte
&& ttMove != MOVE_NONE
&& can_return_tt(tte, depth-R-ONE_PLY, ttValue, beta))
{
nullExclusionsearch = true;
ss->excludedMove = threatMove;
nullValue = -search<NonPV>(pos, ss+1, -beta, -alpha, depth-R);
ss->excludedMove = MOVE_NONE;
}
} // Nullexclusion Search
(ss+1)->skipNullMove = false;
pos.do_null_move<false>(st);
if (nullValue >= beta)
{
// Do not return unproven mate scores
if (nullValue >= VALUE_MATE_IN_MAX_PLY)
nullValue = beta;
if ( depth < 6 * ONE_PLY
&& !nullExclusionsearch)
return nullValue;
// Do verification search at high depths
// [In case of a 'Nullexclusion search'
// we try to extend the hashresult]
CheckInfo ci(pos);
if ( (nullExclusionsearch || (tte && ttMove != MOVE_NONE && can_return_tt(tte, depth-R-ONE_PLY, ttValue, beta)))
&& pos.pl_move_is_legal(ttMove, ci.pinned))
{
pos.do_move(ttMove, st, ci, pos.move_gives_check(ttMove, ci));
verificationValue = - search<PV>(pos, ss+1, -(ttValue), -(ttValue - 1), depth-R);
if (verificationValue < ttValue)
verificationValue = - search<PV>(pos, ss+1, -beta, -alpha, depth-R);
pos.undo_move(ttMove);
if (verificationValue >= beta)
TT.store(posKey, value_to_tt(verificationValue, ss->ply), BOUND_LOWER, depth-R, ttMove, tte->static_value(), tte->static_value_margin());
}
if (verificationValue < beta)
{
ss->skipNullMove = true;
verificationValue = search<NonPV>(pos, ss, alpha, beta, depth-R);
ss->skipNullMove = false;
if (verificationValue >= beta)
TT.store(posKey, value_to_tt(verificationValue, ss->ply), BOUND_LOWER, depth-R, (ss)->currentMove, ss->eval, ss->evalMargin);
}
if (verificationValue >= beta)
return nullValue;
}
else
{
// The null move failed low, which means that we may be faced with
// some kind of threat. If the previous move was reduced, check if
// the move that refuted the null move was somehow connected to the
// move which was reduced. If a connection is found, return a fail
// low score (which will cause the reduced move to fail high in the
// parent node, which will trigger a re-search with full depth).
if ( depth < ThreatDepth
&& (ss-1)->reduction
&& threatMove != MOVE_NONE
&& connected_moves(pos, (ss-1)->currentMove, threatMove))
return beta - 1;
}
}
A link to the source will follow shortly, but a post for it has not been created yet.