Volker Annuss wrote:Try this one:
[d]2r4k/5Pp1/6P1/8/8/RP5K/2PPPPP1/8 b - - 0 1
It looks like a persistant stalemate threat but it is mated in 18.
By chance my program doesn't recognize this as a persistent stalemate threat. This is because it doesn't see the blackjking as trapped, as squares around aren't all attacked (one is occupied by a blocked pawn).
There's something I don't understand with this position. After playing: Rc3+ d3 Rxd3+ f3 Rxf3+ Kh2 Rh3+ Kg1 Rh1+ Kf2 Rf1+ Ke3 Rf3+ Kd2 Rd3+ Kc1 (where my engine agrees with GNU Chess), we reach this position:
[d]7k/5Pp1/6P1/8/8/RP1r4/2P1P1P1/2K5 b - - 0 1
Now, why would black be forced to play Rd8 here ? I don't see what stops him from continuing this game of rook contact checks to the white king. I would naively play Rd1+ here.
But the mate is there, and DiscoCheck agrees with GNU Chess from here (mate in a max of 10 moves), and plays Rd8. I just want to understand it from a human point of view, because I just don;'t see how black rook runs out of contact checks.
Black can continue contact checks but he has to capture Pb3 to continue the threat. Then the whole rank 3 will be clear with no pawns. After the king zig-zags through the openings back to h2, black has no choice but to contact check at h3 at which point the white rook suddenly awakens and gives a checkmate by capturing the rook. I am sure there is a PV to it but thats how humans understand it.
Daniel Shawul wrote:Black can continue contact checks but he has to capture Pb3 to continue the threat. Then the whole rank 3 will be clear with no pawns. After the king zig-zags through the openings back to h2, black has no choice but to contact check at h3 at which point the white rook suddenly awakens and gives a checkmate by capturing the rook. I am sure there is a PV to it but thats how humans understand it.
int r = (1-phase)*(e->op[us]-e->op[them]) + phase*(e->eg[us]-e->eg[them]);
int c = persistent_stalemate(B);
if (c == NoColor)
return r;
else if (c == B->turn)
return max(0, r);
else
return min(0, r);
Better with comments, and also slightly optimized the early exit conditions
static int persistent_stalemate(const Board *B)
/* This code detects persistent stalemate threats (crazy rook/queen patterns). The return value is
* the color that has a persistent stalemate (ie. the side that "threatens to be stalemated"). When
* there is no such threat (most often), the early exit routes are hopefully fast, and the return
* value is logically NoColor. */
{
/* Rule 1: King is busted (squares around are all attacked) */
unsigned us;
if (!(KAttacks[B->king_pos[White]] & ~B->st->attacks[Black][NoPiece]))
us = White;
else if (!(KAttacks[B->king_pos[Black]] & ~B->st->attacks[White][NoPiece]))
us = Black;
else
return NoColor;
const uint64_t pawns = B->b[us][Pawn];
/* Rule 2: We have at most one piece, and this piece is a Rook or a Queen */
if (several_bits(B->all[us] & ~pawns & ~B->b[us][King]) || !get_RQ(B, us))
return NoColor;
/* Rule 3: we have no mobile pawn */
bool mobile_pawns = false;
unsigned them = opp_color(us);
if (pawns) {
if (shift_bit(pawns, us ? -8 : 8) & ~B->st->occ)
mobile_pawns = true;
else if (shift_bit(pawns & ~FileA_bb, us ? -9 : 7) & B->all[them])
mobile_pawns = true;
else if (shift_bit(pawns & ~FileH_bb, us ? -7 : 9) & B->all[them])
mobile_pawns = true;
}
if (!mobile_pawns) {
/* At this point:
* 1. our King is busted
* 2. we have at most one piece, which is a Roomk or a Queen
* 3. we have no mobile pawn
* If this Rook or Queen can deliver check, then it's a persistent stalemate threat */
unsigned ksq = B->king_pos[them];
if ((B->st->attacks[us][Rook] | B->st->attacks[us][Queen]) & rook_attack(ksq, B->st->occ))
return us;
if (B->st->attacks[us][Queen] & bishop_attack(ksq, B->st->occ))
return us;
}
return NoColor;
}
I'm now going to experiment with stalemate detection in the qsearch.
Just so you know, the above didn't work: elo-wise it was equal at 100,000 nodes/move, but at longer time control 6sec+0.15sec it was proven to be a regression (score was about 2 stdev below 50% after 1000 games).
There are 3 factors to consider
1/ the extra computation slows down a little: negative effect
2/ some nodes are correctly flagged: positive effect
3/ some nodes are incorrectly flagged (the rule is not 100% correct): negative effect
1/ was negligible, as I made sure the code is fast and has early exit routes
2/ looks nice in certain test positions, but their occurrence in real games, or more precisely in searched nodes (not just the moves played but all the nodes searched), is infinitesimal.
3/ is more important than 2/. Overlooking 2/ at certain nodes is not as severe as incorrectly flagging as a draw nodes where the apparently persistent stalemate threat is actually refuted.
So, it's probably safer to use the QS for that and detect stalemate in the QS. However such detection has to be performed at the beginning of the QS, before the stand pat, and before the move loop, both of which often trigger a fail high. So my guess is that it's going to be relatively costly in speed, and it's unikely to be compensated by the benefit of understanding certain rare draws. Also writing a function that determines whether a position is stalemate without doing move generation, and thinking about all possible cases is complex and therefore slow (+risk of bugs). As pointed out in 3/ we don't want to incorrectly flag positions as stalemates when they're not, while overlooking certain ones is not as severe.
IOW 3/ has to be totally eradicated, as if it occurs, even if relatively much less often than 2/, it can be disastrous. I can think of ways to do a stalemate check that is a sufficient but not necessary condition: king can't move & no mobile pawns & no pieces. Tht should be fast to compute, and could make sense to put at the beginning of the QS.
Anyway, it's no my to do list now, and I'll do some testing with the QS method.
More stupid thoughts. Do you have a routine for detecting stalemate, or is it done in the search? If the former then you could recycle the code, if the latter you could bring the score closer to zero.
Also, could you *please* be uci compatible and use 'score mate x' rather than 'score cp (hugenumber-x)'?