diff --git a/evaluate_endgame.go b/evaluate_endgame.go index 130a4d3..6e2d280 100644 --- a/evaluate_endgame.go +++ b/evaluate_endgame.go @@ -15,12 +15,16 @@ func (e *Evaluation) evaluateEndgame() int { } func (e *Evaluation) inspectEndgame() { - markdown := e.material.endgame(e) - if markdown == ExistingScore { + switch markdown := e.material.endgame(e); markdown { + case ExistingScore: return - } else if markdown == DrawScore { + case DrawScore: e.score = Score{0, 0} - } else { + case WhiteWinning: + e.score = Score{WhiteWinning, WhiteWinning} + case BlackWinning: + e.score = Score{-BlackWinning, -BlackWinning} + default: mul, div := markdown >> 16, markdown & 0xFFFF if div != 0 { if mul != 0 { @@ -129,23 +133,67 @@ func (e *Evaluation) drawishBishops() int { } func (e *Evaluation) kingAndPawnVsKingAndPawn() int { - if e.score.endgame != 0 { - color := e.strongerSide() - rival := color ^ 1 - pawns := e.position.outposts[pawn(color)] - if rank(color, pawns.first()) < A5H5 || (pawns & (maskFile[0] | maskFile[7])).any() { - pawns = e.position.outposts[pawn(rival)] // Save rival's pawn mask. - e.position.outposts[pawn(rival)] = maskNone // Remove rival's pawn. - kpkScore := e.kingAndPawnVsBareKing() // Evaluate using KPK bitbase. - e.position.outposts[pawn(rival)] = pawns // Restore rival's pawn mask. - switch kpkScore { // Return KPK bitbase evaluation. - case WhiteWinning: - return e.fraction(kpkScore, e.score.endgame) - case BlackWinning: - return e.fraction(kpkScore, -e.score.endgame) - default: - return DrawScore + if e.score.endgame == 0 { + return ExistingScore + } + + p := e.position + + // Check if the side making a move has unstoppable pawn. + color := p.color + rival := color ^ 1 + piece := pawn(color) + pawns := p.outposts[piece] + square := pawns.first() + + if (p.outposts[rival] & maskInFront[color][square]).empty() { + // Pick square rule bitmask for the pawn. If defending king has the right + // to move then pick extended square mask. + mask := Bitmask(0) + if p.color == color { + mask = maskSquare[color][square] + } else { + mask = maskSquareEx[color][square] + } + if (mask & p.outposts[king(rival)]).empty() { + if color == White { + return WhiteWinning } + return BlackWinning + } + } + + + // Try to evaluate the endgame using KPK bitbase. If the opposite side is not loosing + // without the pawn it's unlikely the game is lost with the pawn present. + if rank(color, pawns.first()) < A5H5 || (pawns & (maskFile[0] | maskFile[7])).any() { + + // Temporarily remove rival's pawn. + piece = pawn(rival) + pawns = p.outposts[piece] // <- Save: rival's pawn bitmask. + square = pawns.first() // <- Save: rival's pawn square. + + p.outposts[piece] = maskNone + p.pieces[square] = Piece(0) + + // Temporarily adjust score so that when e.strongerSide() gets called + // by kingAndPawnVsBareKing() it returns side to move. + score := e.score.endgame // <- Save: endgame score. + if color == White { + e.score.endgame = 1 + } else { + e.score.endgame = -1 + } + + // When we're done restore original endgame score and rival's pawn. + defer func() { + e.score.endgame = score // -> Restore: endgame score. + p.pieces[square] = piece // -> Restore: rival's pawn square. + p.outposts[piece] = pawns // -> Restore: rival's pawn bitmask. + }() + + if e.kingAndPawnVsBareKing() == DrawScore { + return DrawScore } } diff --git a/evaluate_endgame_test.go b/evaluate_endgame_test.go index 1f234f4..584098b 100644 --- a/evaluate_endgame_test.go +++ b/evaluate_endgame_test.go @@ -209,3 +209,23 @@ func TestEndgame340(t *testing.T) { score := NewGame(`Kd1,h3`, `M,Kb8,a4`).start().Evaluate() expect.Eq(t, score, 0) } + +func TestEndgame350(t *testing.T) { + score := NewGame(`Kf5,h3`, `Kd5,h4`).start().Evaluate() + expect.Eq(t, score, 17) +} + +func TestEndgame360(t *testing.T) { + score := NewGame(`Kf5,h3`, `M,Kd5,h4`).start().Evaluate() + expect.Eq(t, score, -7) +} + +func TestEndgame370(t *testing.T) { + score := NewGame(`Kh6,h3`, `Kf6,h4`).start().Evaluate() + expect.Eq(t, score, 0) +} + +func TestEndgame380(t *testing.T) { + score := NewGame(`Kf1,h3`, `M,Kh1,h4`).start().Evaluate() + expect.Eq(t, score, 0) +} diff --git a/evaluate_pawns_test.go b/evaluate_pawns_test.go index b78f7e2..4fb27ed 100644 --- a/evaluate_pawns_test.go +++ b/evaluate_pawns_test.go @@ -125,7 +125,7 @@ func TestEvaluatePawns510(t *testing.T) { game := NewGame(`Kg1,f2,g2,h2,Qa3,Na4`, `Kg8,f5,g6,h7,Qa6,Na5`) // h2,g2,h2 vs F5,G6,h7 score := game.start().Evaluate() - expect.Eq(t, score, 27) + expect.Eq(t, score, 95) } func TestEvaluatePawns520(t *testing.T) {