I added code to detect unstoppable passed pawns and drawn endgames, and code to evaluate material trades. The code that evaluates material trades implements the age old advice, “When ahead trade pieces. When behind trade pawns.” A greater bonus is giving for trading pawns, since in many endgames the side with a material advantage cannot win without promoting a pawn.
private const int _unstoppablePassedPawn = 700; // Less than a queen minus a pawn to encourage pawn promotion. | |
private const int _pieceTradesPercent = 25; | |
private const int _pawnTradesPercent = 50; | |
private void GetPassedPawnScore(bool White, int Rank, int File, out int MgPassedPawn, out int EgPassedPawn) | |
{ | |
MgPassedPawn = _mgPassedPawns[Rank]; | |
EgPassedPawn = _egPassedPawns[Rank]; | |
int pawnSquare; | |
int promotionSquare; | |
int enemyKingSquare; | |
int direction; | |
int enemyMinorAndMajorPieces; | |
if (White) | |
{ | |
// White pawn | |
pawnSquare = _board.WhiteRankFiles[Rank][File]; | |
promotionSquare = _board.WhiteRankFiles[8][File]; | |
enemyKingSquare = _board.CurrentPosition.BlackKingSquare; | |
direction = Direction.North; | |
enemyMinorAndMajorPieces = _board.CurrentPosition.BlackMinorPieces + | |
_board.CurrentPosition.BlackMajorPieces; | |
} | |
else | |
{ | |
// Black pawn | |
pawnSquare = _board.BlackRankFiles[Rank][File]; | |
promotionSquare = _board.BlackRankFiles[8][File]; | |
enemyKingSquare = _board.CurrentPosition.WhiteKingSquare; | |
direction = Direction.South; | |
enemyMinorAndMajorPieces = _board.CurrentPosition.WhiteMinorPieces + | |
_board.CurrentPosition.WhiteMajorPieces; | |
} | |
if (enemyMinorAndMajorPieces == 0) | |
{ | |
// Enemy has no minor or major pieces. | |
if (_board.IsPawnPromotionPathFree(pawnSquare, direction)) | |
{ | |
// Pawn is not blocked by own king. | |
bool pawnMove = White == _board.CurrentPosition.WhiteMove; | |
int pawnDistanceToPromotionSquare = _board.GetDistance(pawnSquare, promotionSquare); | |
int kingDistanceToPromotionSquare = _board.GetDistance(enemyKingSquare, promotionSquare); | |
if (!pawnMove) | |
{ | |
// Enemy king can move one square closer to pawn. | |
kingDistanceToPromotionSquare--; | |
} | |
if (kingDistanceToPromotionSquare > pawnDistanceToPromotionSquare) | |
{ | |
// Enemy king cannot stop pawn from promoting. | |
MgPassedPawn = _unstoppablePassedPawn; | |
EgPassedPawn = _unstoppablePassedPawn; | |
} | |
} | |
} | |
} |
private bool IsDrawnEndgame(int MaterialScore) | |
{ | |
// Return true if position is drawn. Do not terminate search based on this method because a sequence | |
of moves could make the game winnable. | |
if ((_board.CurrentPosition.WhitePawns > 0) || (_board.CurrentPosition.BlackPawns > 0)) return false; | |
// Neither side has any pawns. | |
// Material score represents score for side to move. | |
bool whiteWinning = _board.CurrentPosition.WhiteMove ? MaterialScore >= 0 : MaterialScore <= 0; | |
int winningKnights; | |
int winningBishops; | |
int winningRooks; | |
int winningQueens; | |
int winningMinorPieces; | |
int winningMajorPieces; | |
int losingKnights; | |
int losingBishops; | |
int losingRooks; | |
int losingQueens; | |
int losingMinorPieces; | |
int losingMajorPieces; | |
if (whiteWinning) | |
{ | |
// White is ahead in material. | |
winningKnights = _board.CurrentPosition.WhiteKnights; | |
winningBishops = _board.CurrentPosition.WhiteBishops; | |
winningRooks = _board.CurrentPosition.WhiteRooks; | |
winningQueens = _board.CurrentPosition.WhiteQueens; | |
winningMinorPieces = _board.CurrentPosition.WhiteMinorPieces; | |
winningMajorPieces = _board.CurrentPosition.WhiteMajorPieces; | |
losingKnights = _board.CurrentPosition.BlackKnights; | |
losingBishops = _board.CurrentPosition.BlackBishops; | |
losingRooks = _board.CurrentPosition.BlackRooks; | |
losingQueens = _board.CurrentPosition.BlackQueens; | |
losingMinorPieces = _board.CurrentPosition.BlackMinorPieces; | |
losingMajorPieces = _board.CurrentPosition.BlackMajorPieces; | |
} | |
else | |
{ | |
// Black is ahead in material. | |
winningKnights = _board.CurrentPosition.BlackKnights; | |
winningBishops = _board.CurrentPosition.BlackBishops; | |
winningRooks = _board.CurrentPosition.BlackRooks; | |
winningQueens = _board.CurrentPosition.BlackQueens; | |
winningMinorPieces = _board.CurrentPosition.BlackMinorPieces; | |
winningMajorPieces = _board.CurrentPosition.BlackMajorPieces; | |
losingKnights = _board.CurrentPosition.WhiteKnights; | |
losingBishops = _board.CurrentPosition.WhiteBishops; | |
losingRooks = _board.CurrentPosition.WhiteRooks; | |
losingQueens = _board.CurrentPosition.WhiteQueens; | |
losingMinorPieces = _board.CurrentPosition.WhiteMinorPieces; | |
losingMajorPieces = _board.CurrentPosition.WhiteMajorPieces; | |
} | |
if ((winningRooks == 2) && (winningMajorPieces == 2) && (winningMinorPieces == 0)) | |
{ | |
// Winning side has two rooks. | |
if ((losingQueens == 1) && (losingMajorPieces == 1) && (losingMinorPieces == 0)) | |
{ | |
// Losing side has queen. | |
return true; | |
} | |
if ((losingRooks == 1) && (losingMajorPieces == 1)) | |
{ | |
if ((losingBishops == 1) && (losingKnights == 1)) | |
{ | |
// Losing side has rook, bishop, and knight. | |
return true; | |
} | |
if (losingMinorPieces == 1) | |
{ | |
// Losing side has rook and minor piece. | |
return true; | |
} | |
} | |
} | |
if ((winningQueens == 1) && (winningMajorPieces == 1) && (winningMinorPieces == 0)) | |
{ | |
// Winning side has queen. | |
if ((losingRooks == 2) && (losingMajorPieces == 2) && (losingMinorPieces == 0)) | |
{ | |
// Losing side has two rooks. | |
return true; | |
} | |
if ((losingRooks == 1) && (losingMajorPieces == 1) && (losingMinorPieces == 1)) | |
{ | |
// Losing side has rook and minor piece. | |
return true; | |
} | |
} | |
if ((winningRooks == 1) && (winningMajorPieces == 1) && (winningMinorPieces == 1)) | |
{ | |
// Winning side has rook and minor piece. | |
if ((losingRooks == 1) && (losingMajorPieces == 1) && (losingMinorPieces == 0)) | |
{ | |
// Losing side has rook. | |
return true; | |
} | |
} | |
if ((winningBishops == 1) && (winningKnights == 1) && (winningMajorPieces == 0)) | |
{ | |
// Winning side has bishop and knight. | |
if ((losingRooks == 1) && (losingMajorPieces == 1) && (losingMinorPieces == 0)) | |
{ | |
// Losing side has rook. | |
return true; | |
} | |
} | |
if ((winningRooks == 1) && (winningMajorPieces == 1) && (winningMinorPieces == 0)) | |
{ | |
// Winning side has rook. | |
if ((losingRooks == 1) && (losingMajorPieces == 1) && (losingMinorPieces == 0)) | |
{ | |
// Losing side has rook. | |
return true; | |
} | |
if ((losingMinorPieces == 1) && (losingMajorPieces == 0)) | |
{ | |
// Losing side has minor piece. | |
return true; | |
} | |
} | |
if ((winningKnights == 2) && (winningMinorPieces == 2) && (winningMajorPieces == 0)) | |
{ | |
// Winning side has two knights. | |
if ((losingMinorPieces <= 1) && (losingMajorPieces == 0)) | |
{ | |
// Losing side has one or zero minor pieces. | |
return true; | |
} | |
} | |
return false; | |
} |
private int EvaluateMaterialTrades(int MaterialScore) | |
{ | |
// Material score represents score for side to move. | |
bool whiteWinning = _board.CurrentPosition.WhiteMove ? MaterialScore >= 0 : MaterialScore <= 0; | |
int absoluteMaterialScore = Math.Abs(MaterialScore); | |
// When ahead trade pieces. | |
// When behind trade pawns. | |
int losingMissingPieces; | |
int winningMissingPawns; | |
if (whiteWinning) | |
{ | |
// White is ahead in material. | |
losingMissingPieces = 7 - _board.CurrentPosition.BlackMinorPieces - | |
_board.CurrentPosition.BlackMajorPieces; | |
winningMissingPawns = 8 - _board.CurrentPosition.WhitePawns; | |
} | |
else | |
{ | |
// Black is ahead in material. | |
losingMissingPieces = 7 - _board.CurrentPosition.WhiteMinorPieces - | |
_board.CurrentPosition.WhiteMajorPieces; | |
winningMissingPawns = 8 - _board.CurrentPosition.BlackPawns; | |
} | |
int losingMissingPiecesScore = (absoluteMaterialScore * losingMissingPieces * _pieceTradesPercent) / 700; | |
int winningMissingPawnsScore = (absoluteMaterialScore * winningMissingPawns * _pawnTradesPercent) / 800; | |
int materialTrades = losingMissingPiecesScore - winningMissingPawnsScore; | |
return whiteWinning ? materialTrades : -materialTrades; | |
} |
This added 39 Elo to the playing strength of MadChess 2.0 Beta.
MadChess 2.0 2193 : 800 (+399,=168,-233), 60.4 % vs. : games ( +, =, -), (%) : Diff, SD, CFS (%) Glass 1.6 : 50 ( 4, 7, 39), 15.0 : -223, 27, 0.0 Sungorus 1.4 : 50 ( 18, 7, 25), 43.0 : -117, 12, 0.0 ZCT 0.3.2450 : 50 ( 13, 12, 25), 38.0 : -15, 14, 14.3 FireFly v2.6.0 : 50 ( 18, 19, 13), 55.0 : -13, 15, 19.0 Jazz v444 : 50 ( 19, 15, 16), 53.0 : +9, 14, 73.6 Beowulf 2.4 : 50 ( 21, 13, 16), 55.0 : +31, 20, 93.9 Wing 2.0 : 50 ( 22, 11, 17), 55.0 : +71, 15, 100.0 Brainless 1.0 : 50 ( 26, 11, 13), 63.0 : +82, 19, 100.0 BikJump v2.01 : 50 ( 22, 10, 18), 54.0 : +93, 13, 100.0 Matheus-2.3 : 50 ( 21, 17, 12), 59.0 : +115, 13, 100.0 Monarch 1.7 : 50 ( 28, 11, 11), 67.0 : +142, 15, 100.0 BigLion 2.23w : 50 ( 32, 12, 6), 76.0 : +170, 13, 100.0 Sharper 0.17 : 50 ( 37, 5, 8), 79.0 : +184, 13, 100.0 Faile 1.4 : 50 ( 36, 12, 2), 84.0 : +213, 12, 100.0 Jabba13032012 : 50 ( 41, 1, 8), 83.0 : +242, 13, 100.0 Roce 0.0390 : 50 ( 41, 5, 4), 87.0 : +311, 13, 100.0
Feature | Category | Date | Rev1 | WAC2 | Elo Rating3 | Improvement |
---|---|---|---|---|---|---|
Unstoppable Pawns Draws, Material Trades |
Evaluation | 2015 Jan 31 | 52 | 270 | 2193 | +39 |
Late Move Pruning | Search | 2015 Jan 10 | 44 | 273 | 2154 | +39 |
History Heuristic Late Move Reductions |
Search | 2015 Jan 04 | 40 | 275 | 2115 | +50 |
Killer Moves | Search | 2015 Jan 03 | 38 | 275 | 2065 | +61 |
Futility Pruning | Search | 2014 Dec 29 | 37 | 256 | 2004 | +54 |
Null Move Quiescence Recaptures |
Search | 2014 Dec 28 | 34 | 242 | 1950 | +46 |
King Safety | Evaluation | 2014 Dec 24 | 32 | 225 | 1904 | +27 |
Piece Mobility | Evaluation | 2014 Dec 16 | 29 | 225 | 1877 | +64 |
Draw By Repetition Bug | Evaluation | 2014 Dec 10 | 27 | 225 | 1813 | +47 |
Passed Pawns | Evaluation | 2014 Dec 09 | 26 | 225 | 1766 | +72 |
Time Management | Search | 2014 Dec 08 | 25 | 231 | 1694 | +24 |
Delay Move Generation Aspiration Window Bug |
Search | 2014 Dec 02 | 23 | 231 | 1670 | +44 |
MVV / LVA Move Order Draw By Insufficient Material Move List Overflow Bug |
Search | 2014 Dec 01 | 22 | 235 | 1626 | +30 |
Tapered Evaluation MG and EG Piece Location |
Evaluation | 2014 Nov 29 | 21 | 234 | 1596 | +107 |
Alpha / Beta Negamax Aspiration Windows Quiescence, Hash Material, Piece Squares |
Baseline | 2014 Nov 25 | 20 | 236 | 1489 | – |
- Subversion source code revision
- Win At Chess position test, 3 seconds per position
- Bullet chess, 2 min / game + 1 sec / move