You are on page 1of 65

Chess Programming Part VI: Evaluation Functions

Published Oct 08 2000 10:07 PM in Artificial Intelligence


By Franois Dominic Larame
Download attached article resource
It's been six months, and i know sometimes it must have felt like i would never
shut up, but here we are: the sixth and last installment of my chess programming
series. Better yet: my Java chess program (see attached resource file), primitive
though it may be, is now complete, and i shipped it to Gamedev along with this,
which proves that i know (a little bit of) what I've been talking about.
This month's topic, the evaluation function, is unique in a very real sense: while
search techniques are pretty much universal and move generation can be
deducted from a game's rules and no more, evaluation requires a deep and
thorough analysis of strategy. As you can well imagine, it is impossible to assess
a position's relative strengths if we don't know what features are likely to lead to
victory for one player or the other. Therefore, many of the concepts discussed
here may apply to other games in very different fashion, or not at all; it is your
job as programmer to know your game and decide what the evaluator should
take into consideration.
Without further delay, let us take a look at some board evaluation metrics and at
how they can be used to evaluate a chess position.
Material Balance
To put it simply, material balance is an account of which pieces are on the board
for each side. According to chess literature, a queen may be worth 900 points, a
rook 500, a bishop 325, a knight 300 and a pawn 100; the king has infinite value.
Computing material balance is therefore a simple matter: a side's material value
is equal to
MB = Sum( Np * Vp )
where Np is the number of pieces of a certain type on the board and Vp is that
piece's value. If you have more material on the board than your opponent, you
are in good shape.
Sounds simple, doesn't it? Yet, it is by far the overwhelming factor in any chess
board evaluation function. CHESS 4.5's creators estimate that an enormous
advantage in position, mobility and safety is worth less than 1.5 pawns. In fact, it
is quite possible to play decent chess without considering anything else!
Sure, there are positions where you should sacrifice a piece (sometimes even
your queen) in exchange for an advantage in momentum. These, however, are
best discovered through search: if a queen sacrifice leads to mate in 3 moves,
your search function will find the mate (provided it looks deep enough) without
requiring special code. Think of the nightmares if you were forced to write
special-case code in your evaluator to determine when a sacrifice is worth the
trouble!
Few programs use a material evaluation function as primitive as the one i

indicated earlier. Since the computation is so easy, it is tempting to add a few


more features into it, and most people do it all the time. For example, it is a wellknown fact that once you are ahead on material, exchanging pieces of equal
value is advantageous. Exchanging a single pawn is often a good idea, because it
opens up the board for your rooks, but you would still like to keep most of your
pawns on the board until the endgame to provide defense and an opportunity for
queening. Finally, you don't want your program to panic if it plays a gambit (i.e.,
sacrifices a pawn) from its opening book, and therefore you may want to build a
"contempt factor" into the material balance evaluation; this allows your program
to think it's ahead even though it is behind by 150 points of material or more, for
example.
Note that, while material balance is highly valuable in chess and in checkers, it is
deceiving in Othello. Sure, you must control more squares than the opponent at
the end of the game to win, but it is often better to limit his options by having as
few pieces on the board as possible during the middlegame. And in other games,
like Go-Moku and all other Connect-N variations, material balance is irrelevant
because no pieces are ever captured.
Mobility and Board Control
One of the characteristics of checkmate is that the victim has no legal moves left.
Intuitively, it also seems better to have a lot of options available: a player is more
likely to be able to find a good line of play if he has 30 legal moves to choose
from than if he is limited to 3.
In chess, mobility is easily assessed: count the number of legal moves for each
side given the position, and you are done. However, this statistic has proven to
be of little value. Why? For one thing, many chess moves are pointless. It may be
legal to make your rook patrol the back rank one square at a time, but it is rarely
productive. For another, trying to limit the opponent's mobility at all costs may
lead the program to destroy its own defensive position in search of "pointless
checks": since there are rarely more than 3-4 legal ways to evade check in any
given position, a mobility-oriented program would be likely to make incautious
moves to put the opponent in check, and after a while, it may discover that it has
accomplished nothing and has dispersed its forces all over the board.
Still, a few sophisticated mobility evaluation features are always a good thing. My
program penalizes "bad bishops", i.e., bishops whose movement is hampered by
a large number of pawns on squares of the same color, as well as knights sitting
too close to the edges of the board. As another example, rooks are more valuable
on open and semi-open files, i.e., files where there are no pawns (or at least no
friendly ones).
A close relative of mobility is board control. In chess, a side controls a square if it
has more pieces attacking it than the opponent. It is usually safe to move to a
controlled square, and hazardous to move to one controlled by the opponent.
(There are exceptions: moving your queen to a square attacked by an enemy
pawn is rarely a good idea, no matter how many ways you can capture that pawn
once it has done its damage. You may also voluntarily put a piece in harm's way
to lead a defender away from an even more valuable spot.) In chess, control of
the center is a fundamental goal of the opening. However, control is somewhat
difficult to compute: it requires maintaining a database of all squares attacked by

all pieces on the board at any given time. Many programs do it; mine doesn't.
While mobility is less important than it seems to the chess programmer, it is
extremely important in Othello (where the side with the fewest legal moves in the
endgame is usually in deep trouble). As for board control, it is the victory
condition of Go.
Development
An age-old maxim of chess playing is that minor pieces (bishops and knights)
should be brought into the battle as quickly as possible, that the King should
castle early and that rooks and queens should stay quiet until it is time for a
decisive attack. There are many reasons for this: knights and bishops (and
pawns) can take control of the center, support the queen's attacks, and moving
them out of the way frees the back rank for the more potent rooks. Later on in
the game, a rook running amok on the seventh rank (i.e., the base of operations
for the opponent's pawns) can cause a tremendous amount of damage.
My program uses several factors to measure development. First, it penalizes any
position in which the King's and Queen's pawns have not moved at all. It also
penalizes knights and bishops located on the back rank where they hinder rook
movement, tries to prevent the queen from moving until all other pieces have,
and gives a big bonus to positions where the king has safely castled (and smaller
bonuses to cases where it hasn't castled yet but hasn't lost the right to do so)
when the opponent has a queen on the board. As you can see, the development
factor is important in the opening but quickly loses much of its relevance; after
10 moves or so, just about everything that can be measured here has already
happened.
Note that favoring development can be dangerous in a game like Checkers. In
fact, the first player to vacate one of the squares on his back rank is usually in
trouble; avoiding development of these important defensive pieces is a very good
idea.
Pawn Formations
Chess grandmasters often say that pawns are the soul of the game. While this is
far from obvious to the neophyte, the fact that great players often resign over the
loss of a single pawn clearly indicates that they mean it!
Chess literature mentions several types of pawn features, some valuable, some
negative. My program looks at the following:
Doubled or tripled pawns. Two or more pawns of the same color on the
same file are usually bad, because they hinder each others movement.
Pawn rams. Two opposing pawns "butting heads" and blocking each others
forward movement constitute an annoying obstacle.
Passed pawns. Pawns which have advanced so far that they can no longer
be attacked or rammed by enemy pawns are very strong, because they
threaten to reach the back rank and achieve promotion.
Isolated pawns. A pawn which has no friendly pawns on either side is
vulnerable to attack and should seek protection.

Eight pawns. Having too many pawns on the board restricts mobility;
opening at least one file for rook movement is a good idea.

A final note on pawn formations: a passed pawn is extremely dangerous if it has a


rook standing behind it, because any piece that would capture the pawn is dead
meat. My program therefore scores a passed pawn as even more valuable if there
is a rook on the same file and behind the pawn.
King Safety and Tropism
We have already touched on king safety earlier: in the opening and middle game,
protecting the king is paramount, and castling is the best way to do it.
However, in the endgame, most of the pieces on both sides are gone, and the
king suddenly becomes one of your most effective offensive assets! Leaving him
behind a wall of pawns is a waste of resources.
As for "tropism", it is a measure of how easy it is for a piece to attack the
opposing king, and is usually measured in terms of distance. The exact rules used
to compute tropism vary by piece type, but they all amount to this: the closer you
are to the opposing king, the more pressure you put on it.
Picking the Right Weights
Now that we have identified the features we would like to measure, how do we
assign relative weights to each?
That, my friends, is a million-dollar question. People can (and do) spend years
fiddling with the linear combination of features in their evaluation functions,
sometimes giving a little more importance to mobility, sometimes deemphasizing safety, etc. i wish there were an absolute solution, but there isn't.
This is still very much a matter for trial and error. If your program plays well
enough, great. If not, try something else, and play it against the old version; if it
wins most of the time, your new function is an improvement.
Three things you may want to keep in mind:
It is very difficult to refine an evaluation function enough to gain as much
performance as you would from an extra ply of search. When in doubt,
keep the evaluator simple and leave as much processing power as possible
to alphabeta.
Unless you are after the World Championship, your evaluator does not
need to be all-powerful!
If your game plays really fast, you may want to try evolving an appropriate
evaluator using a genetic algorithm. For chess, though, this would likely
require thousands of years!
Next Month
Well, there ain't no next month. This is it.
If i wanted to drag this series even longer, i could write about opening books,

endgame libraries, specialized chess hardware and a zillion other things. i could, i
could. But i won't. Some of these topics i reserve for the book chapter i will be
writing on this very topic later this Fall. Others i just don't know enough about to
contribute anything useful. And mostly, I'm just too lazy to bother.
Still, i hope you enjoyed reading this stuff, and that you learned a useful thing or
two or three. If you did, look me up next year at the GDC or at E3, and praise me
to whomever grants freelance design and programming contracts in your
company, will ya?
Cheers!
Franois Dominic Larame, October 2000
Download attached article resource
Considering Game Phase

Game Phases
Opening
Middlegame
Endgame
Evaluation Discontinuity
Tapered Eval (a score is interpolated between opening and endgame based
on game stage/pieces)

Computer Chess Information and Resources

Filter by APML
Accueil
Archive
Contact
Souscrire
Log in
<< Piece Square Table | Performance Reconstruction >>
Chess Board Evaluation
by aberent 20. octobre 2008 07:26
From what information I could gather on the internet, it seems that most experts
agree that the evaluation function of a chess engine has the greatest capability

to dictate the strength of your chess game. Rybka, currently considered to be the
top computer chess engine in the world, is not famous for its search speed but
rather for its advanced chess board evaluation written by an international chess
master. Furthermore the evaluation function can make your move searching
faster by allowing your chess engine to search better moves first.
Unfortunately for you Rybkas evaluation function is not available. To make
things worse I am not considered by anyone to be a chess master.
Chess Piece Evaluation
As seen in the Chess Piece Class, the following values are assigned to chess
pieces:
Pawn
Knight
Bishop
Rook
Queen
King

100
320
325
500
975
32767

About the numbers.


Writing your chess board evaluation method, think about the numbers as
percentages of a pawn. In my chess engine a pawn is worth 100 points. If you
award 10 points to a tactical position you are telling the chess engine that this
position is worth 1/10th of a pawn. The problem can arise when a move
combines several tactical advantages for the moving side and several tactical
penalties for the opponent. This can result in unnecessary pawn sacrifice for set
of minor tactical advantages. When writing your evaluation function always keep
this in mind.
The Chess Board Score is represented by a signed integer. The higher the score
the better it is for White. The lower the score the better it is for black. Using a
single integer makes everything a bit faster since I can always just reference one
variable.
For example if the value of a single pawn is 100 points and there was only one
single black pawn on the chess board the score would be -100. If there were two
pawns, one white and one black the score would be 0. If white had 2 pawns and
black 1 pawn the score would be 100.
On to the code
Chess Bin Board evaluation is implemented as a static class with two static
methods.
internal static class Evaluation
We first declare our Piece Square Tables discussed in the previous post:

private static readonly short[] PawnTable = new short[]


{
0, 0, 0, 0, 0, 0, 0, 0,
50, 50, 50, 50, 50, 50, 50, 50,
10, 10, 20, 30, 30, 20, 10, 10,
5, 5, 10, 27, 27, 10, 5, 5,
0, 0, 0, 25, 25, 0, 0, 0,
5, -5,-10, 0, 0,-10, -5, 5,
5, 10, 10,-25,-25, 10, 10, 5,
0, 0, 0, 0, 0, 0, 0, 0
};
private static readonly short[] KnightTable = new short[]
{
-50,-40,-30,-30,-30,-30,-40,-50,
-40,-20, 0, 0, 0, 0,-20,-40,
-30, 0, 10, 15, 15, 10, 0,-30,
-30, 5, 15, 20, 20, 15, 5,-30,
-30, 0, 15, 20, 20, 15, 0,-30,
-30, 5, 10, 15, 15, 10, 5,-30,
-40,-20, 0, 5, 5, 0,-20,-40,
-50,-40,-20,-30,-30,-20,-40,-50,
};
private static readonly short[] BishopTable = new short[]
{
-20,-10,-10,-10,-10,-10,-10,-20,
-10, 0, 0, 0, 0, 0, 0,-10,
-10, 0, 5, 10, 10, 5, 0,-10,
-10, 5, 5, 10, 10, 5, 5,-10,
-10, 0, 10, 10, 10, 10, 0,-10,
-10, 10, 10, 10, 10, 10, 10,-10,
-10, 5, 0, 0, 0, 0, 5,-10,
-20,-10,-40,-10,-10,-40,-10,-20,
};
private static readonly short[] KingTable = new short[]
{
-30, -40, -40, -50, -50, -40, -40, -30,
-30, -40, -40, -50, -50, -40, -40, -30,
-30, -40, -40, -50, -50, -40, -40, -30,
-30, -40, -40, -50, -50, -40, -40, -30,
-20, -30, -30, -40, -40, -30, -30, -20,
-10, -20, -20, -20, -20, -20, -20, -10,
20, 20, 0, 0, 0, 0, 20, 20,
20, 30, 10, 0, 0, 10, 30, 20
};
private static readonly short[] KingTableEndGame = new short[]
{
-50,-40,-30,-20,-20,-30,-40,-50,
-30,-20,-10, 0, 0,-10,-20,-30,
-30,-10, 20, 30, 30, 20,-10,-30,

-30,-10, 30, 40, 40, 30,-10,-30,


-30,-10, 30, 40, 40, 30,-10,-30,
-30,-10, 20, 30, 30, 20,-10,-30,
-30,-30, 0, 0, 0, 0,-30,-30,
-50,-30,-30,-30,-30,-30,-30,-50
};
The first method will evaluate the score for a single chess piece on the board.
private static int EvaluatePieceScore(Square square, byte position, bool castled,
bool endGamePhase,ref byte bishopCount,
ref bool insufficientMaterial)
We declare a single variable representing the score for the current chess piece.
This score will always start at 0 and go up if the position is evaluated to be
better, and go down if the position is evaluated to be worse. When the score is
returned to the main Evaluation Function, it will be subtracted for black and
added for white.
int score = 0;
We also declare one local variable that will hold a position we will use to look up
the Pieces Piece Square Table value. As you saw above the Piece Square Table is
an array of 64 elements, basically representing the chess board from whites
perspective. In that case we can simply use whites position value to lookup its
Piece Square Table value. However if the chess piece is black, we need to invert
its position to use the same tables for both white and black.
byte index = position;
if (square.Piece.PieceColor == ChessPieceColor.Black)
{
index = (byte)(63-position);
}
Each piece type has a value. For example a pawn is worth 100 points, a Rook
500 etc. We add that value to the score.
score += square.Piece.PieceValue;
During move generation we record how many pieces are protecting each piece on
the board. This is done by adding the protecting pieces PieceProtectionValue to
the protected pieces ProtectedValue. In the evaluation method we add this
Protected Value to the score. I know this sounds confusing. The trick here is
that I don't want to simply count the number of pieces protecting or attacking
another piece. Rather I want to give more points for being attacked or protected
by a Pawn and fewer points for being attacked or protected by a minor or major
piece.
score += square.Piece.DefendedValue;
Similarly during move generation we added each attacking pieces Piece Attack
Value to the attacked pieces Attacked Value. We now subtract the attacked value
from the score. The idea here is that we reward the computer for protecting its
pieces and penalize it for having the pieces attacked.

score -= square.Piece.AttackedValue;
Furthermore if the chess piece is getting attacked and it is not protected then will
consider that piece EnPrise, meaning we are about to lose it. There is an
additional penalty applied by subtracting the Attack Value from the Score again.
//Double Penalty for Hanging Pieces
if (square.Piece.DefendedValue < square.Piece.AttackedValue)
{
score -= ((square.Piece.AttackedValue - square.Piece.DefendedValue)* 10);
}
}
We will also add score for mobility. This discourages trapped pieces and blocked
pawns.
if (square.Piece.ValidMoves != null)
{
score += square.Piece.ValidMoves.Count;
}
The remainder of the code is chess piece specific, starting with Pawns.
The following code will perform 3 Evaluations:
Remove some points for pawns on the edge of the board. The idea is that since
a pawn of the edge can only attack one way it is worth 15% less.
Give an extra bonus for pawns that are on the 6th and 7th rank as long as they
are not attacked in any way.
Add points based on the Pawn Piece Square Table Lookup.
We will also keep track in what column we find each pawn. This will not be a
pawn count but a value of what that pawn means in that column if we later find
him to be isolated passed or doubled. This information will be used later to score
isolated passed and doubled pawns.
if (square.Piece.PieceType == ChessPieceType.Pawn)
{
insufficientMaterial = false;
if (position % 8 == 0 || position % 8 == 7)
{
//Rook Pawns are worth 15% less because they can only attack one way
score -= 15;
}
//Calculate Position Values
score += PawnTable[index];
if (square.Piece.PieceColor == ChessPieceColor.White)
{
if (whitePawnCount[position % 8] > 0)
{

//Doubled Pawn
score -= 16;
}
if (position >= 8 && position <= 15)
{
if (square.Piece.AttackedValue == 0)
{
whitePawnCount[position % 8] += 200;
if (square.Piece.DefendedValue != 0)
whitePawnCount[position % 8] += 50;
}
}
else if (position >= 16 && position <= 23)
{
if (square.Piece.AttackedValue == 0)
{
whitePawnCount[position % 8] += 100;
if (square.Piece.DefendedValue != 0)
whitePawnCount[position % 8] += 25;
}
}
whitePawnCount[position % 8]+=10;
}
else
{
if (blackPawnCount[position % 8] > 0)
{
//Doubled Pawn
score -= 16;
}
if (position >= 48 && position <= 55)
{
if (square.Piece.AttackedValue == 0)
{
blackPawnCount[position % 8] += 200;
if (square.Piece.DefendedValue != 0)
blackPawnCount[position % 8] += 50;
}
}
//Pawns in 6th Row that are not attacked are worth more points.
else if (position >= 40 && position <= 47)
{
if (square.Piece.AttackedValue == 0)
{
blackPawnCount[position % 8] += 100;

if (square.Piece.DefendedValue != 0)
blackPawnCount[position % 8] += 25;
}
}
blackPawnCount[position % 8] += 10;
}
}
Knights also get a value from the Piece Square Table. However knights are worth
less in the end game since it is difficult to mate with a knight hence they lose 10
points during the end game.
else if (square.Piece.PieceType == ChessPieceType.Knight)
{
score += KnightTable[index];
//In the end game remove a few points for Knights since they are worth less
if (endGamePhase)
{
score -= 10;
}
}
Opposite to Knights, Bishops are worth more in the end game, also we add a
small bonus for having 2 bishops since they complement each other by
controlling different ranks.
else if (square.Piece.PieceType == ChessPieceType.Bishop)
{
bishopCount++;
if (bishopCount >= 2)
{
//2 Bishops receive a bonus
score += 10;
}
//In the end game Bishops are worth more
if (endGamePhase)
{
score += 10;
}
score += BishopTable[index];
}
We want to encourage Rooks not to move out of their corner positions before
castling has occurred. Also if a rook is present on the board we will set a variable
called Insufficient Material to false. This allows us to catch a tie scenario if
insufficient material is present on the board.

else if (square.Piece.PieceType == ChessPieceType.Rook)


{
insufficientMaterial = false;
if (square.Piece.Moved && castled == false)
{
score -= 10;
}
}
Furthermore we want to discourage Queen movement in the beginning of the
game so we give a small penalty if the Queen has moved.
else if (square.Piece.PieceType == ChessPieceType.Queen)
{
insufficientMaterial = false;
if (square.Piece.Moved && !endGamePhase)
{
score -= 10;
}
}
Finally in order to encourage the computer to castle we will remove points if
castling is no longer possible. Furthermore we will also take away points if the
king has less than 2 moves. The idea here is that if the king has only one move,
we are possibly one move away from a mate.
else if (square.Piece.PieceType == ChessPieceType.King)
{
if (square.Piece.ValidMoves.Count < 2)
{
score -= 5;
}
if (endGamePhase)
{
score += KingTableEndGame[index];
}
else
{
score += KingTable[index];
if (square.Piece.Moved && castled == false)
{
score -= 30;
}
}
}
At the end our method simply returns the score:
return score;
The second method in the Board Evaluation Class is a static method that accepts
the chess board and the currently moving side. This is the main Evaluation
Function used in the chess engine. It will evaluate board specific events such as

check and mate as well as loop through all of the chess pieces on the board and
call the above described Evaluate Piece Score for each piece.
internal static void EvaluateBoardScore(Board board)
At the beginning of the evaluation the score will always be set to 0.
board.Score = 0;
We will also declare a variable called insufficient material that will be set to true
only if enough chess pieces (material) are found to prevent the Insufficient
Material Tie Rule.
bool insufficientMaterial = true;
The evaluation method will first examine board wide specific events. Is there a
check, stale mate, has any side castled etc. Most of the work on figuring out
these events has already been done in the Chess Piece Motion or Chess Board
classes; all we are doing now is summing up the score.
if (board.StaleMate)
{
return;
}
if (board.FiftyMoveCount >= 50)
{
return;
}
if (board.RepeatedMoveCount >= 3)
{
return;
}
Next we give bonuses and penalties for board level scenarios such as King
Checks, Mates and Castles.
if (board.BlackMate)
{
board.Score = 32767;
return;
}
if (board.WhiteMate)
{
board.Score = -32767;
return;
}
if (board.BlackCheck)
{
board.Score += 75;
if (board.EndGamePhase)
board.Score += 10;
}
else if (board.WhiteCheck)
{
board.Score -= 75;

if (board.EndGamePhase)
board.Score -= 10;
}
if (board.BlackCastled)
{
board.Score -= 40;
}
if (board.WhiteCastled)
{
board.Score += 40;
}
//Add a small bonus for tempo (turn)
if (board.WhoseMove == ChessPieceColor.White)
{
board.Score += 10;
}
else
{
board.Score -= 10;
}
The following two integers will keep track of how many bishops and knights are
remaining on the board. This will allow us to give an additional bonus if a player
has both bishops. Also if there 2 are 2 knights we can't call the Insufficient
Material Tie rule.
byte blackBishopCount = 0;
byte whiteBishopCount = 0;
byte knightCount = 0;
The following integer will calculate remaining material on the board. We will use
this later on to make the decision if we are currently in the middle or end game.
End game evaluation can differ from the middle game. For example in the
middle game Knights are very useful since they can hop behind enemy lines and
take out vulnerable pieces, however in an end game Knights have a hard time
placing the king in a corner for a mate so their value is slightly diminished. Also
castling in an end game has a diminished bonus.
int RemainingPieces = 0;
We also need to keep track how many pawns exist in each column so that later
we can figure out if we have any isolated and doubled pawns.
blackPawnCount = new short[8];
whitePawnCount = new short[8];
The next step of the evaluation is a loop through all of the chess pieces on the
chess board and call the EvaluatePieceScore method defined above.
for (byte x = 0; x < 64; x++)
{
Square square = board.Squares[x];
if (square.Piece == null)
continue;
//Calculate Remaining Material for end game determination
remainingPieces++;

if (square.Piece.PieceColor == ChessPieceColor.White)
{
board.Score += EvaluatePieceScore(square, x, board.WhiteCastled,
board.EndGamePhase,
ref whiteBishopCount, ref insufficientMaterial);
}
else if (square.Piece.PieceColor == ChessPieceColor.Black)
{
board.Score -= EvaluatePieceScore(square, x, board.BlackCastled,
board.EndGamePhase,
ref blackBishopCount, ref insufficientMaterial);
}
if (square.Piece.PieceType == ChessPieceType.Knight)
{
knightCount++;
if (knightCount > 1)
{
insufficientMaterial = false;
}
}
if ((blackBishopCount + whiteBishopCount) > 1)
{
insufficientMaterial = false;
}
}
Next section does will handle the remaining board level events, such as calling a
tie if there is insufficient material on the chess board.
After looping through all of the chess pieces we also know how many pieces are
remaining on the chess board. If there are less than 10 pieces we will mark the
chess board as being in an endgame. This way the next evaluation will change
slightly based on the end game rules defined.
if (insufficientMaterial)
{
board.Score = 0;
board.StaleMate = true;
board.InsufficientMaterial = true;
return;
}
if (remainingPieces < 10)
{
board.EndGamePhase = true;
if (board.BlackCheck)
{
board.Score += 10;
}

else if (board.WhiteCheck)
{
board.Score -= 10;
}
}
The last section of code uses the previously stored pawn position information and
figures out if there are any doubled and isolated pawns. Each one of these pawns
are given a strong penalty.
//Black Isolated Pawns
if (blackPawnCount[0] >= 1 && blackPawnCount[1] == 0)
{
board.Score += 12;
}
if (blackPawnCount[1] >= 1 && blackPawnCount[0] == 0 &&
blackPawnCount[2] == 0)
{
board.Score += 14;
}
if (blackPawnCount[2] >= 1 && blackPawnCount[1] == 0 &&
blackPawnCount[3] == 0)
{
board.Score += 16;
}
if (blackPawnCount[3] >= 1 && blackPawnCount[2] == 0 &&
blackPawnCount[4] == 0)
{
board.Score += 20;
}
if (blackPawnCount[4] >= 1 && blackPawnCount[3] == 0 &&
blackPawnCount[5] == 0)
{
board.Score += 20;
}
if (blackPawnCount[5] >= 1 && blackPawnCount[4] == 0 &&
blackPawnCount[6] == 0)
{
board.Score += 16;
}
if (blackPawnCount[6] >= 1 && blackPawnCount[5] == 0 &&
blackPawnCount[7] == 0)
{
board.Score += 14;
}
if (blackPawnCount[7] >= 1 && blackPawnCount[6] == 0)
{
board.Score += 12;
}
//White Isolated Pawns
if (whitePawnCount[0] >= 1 && whitePawnCount[1] == 0)
{
board.Score -= 12;
}

if (whitePawnCount[1] >= 1
whitePawnCount[2] == 0)
{
board.Score -= 14;
}
if (whitePawnCount[2] >= 1
whitePawnCount[3] == 0)
{
board.Score -= 16;
}
if (whitePawnCount[3] >= 1
whitePawnCount[4] == 0)
{
board.Score -= 20;
}
if (whitePawnCount[4] >= 1
whitePawnCount[5] == 0)
{
board.Score -= 20;
}
if (whitePawnCount[5] >= 1
whitePawnCount[6] == 0)
{
board.Score -= 16;
}
if (whitePawnCount[6] >= 1
whitePawnCount[7] == 0)
{
board.Score -= 14;
}
if (whitePawnCount[7] >= 1
{
board.Score -= 12;
}

&& whitePawnCount[0] == 0 &&

&& whitePawnCount[1] == 0 &&

&& whitePawnCount[2] == 0 &&

&& whitePawnCount[3] == 0 &&

&& whitePawnCount[4] == 0 &&

&& whitePawnCount[5] == 0 &&

&& whitePawnCount[6] == 0)

//Black Passed Pawns


if (blackPawnCount[0] >= 1 && whitePawnCount[0]
{
board.Score -= blackPawnCount[0];
}
if (blackPawnCount[1] >= 1 && whitePawnCount[1]
{
board.Score -= blackPawnCount[1];
}
if (blackPawnCount[2] >= 1 && whitePawnCount[2]
{
board.Score -= blackPawnCount[2];
}
if (blackPawnCount[3] >= 1 && whitePawnCount[3]
{
board.Score -= blackPawnCount[3];
}
if (blackPawnCount[4] >= 1 && whitePawnCount[4]
{

== 0)

== 0)

== 0)

== 0)

== 0)

board.Score -= blackPawnCount[4];
}
if (blackPawnCount[5] >= 1 && whitePawnCount[5] == 0)
{
board.Score -= blackPawnCount[5];
}
if (blackPawnCount[6] >= 1 && whitePawnCount[6] == 0)
{
board.Score -= blackPawnCount[6];
}
if (blackPawnCount[7] >= 1 && whitePawnCount[7] == 0)
{
board.Score -= blackPawnCount[7];
}
//White Passed Pawns
if (whitePawnCount[0] >= 1 && blackPawnCount[1]
{
board.Score += whitePawnCount[0];
}
if (whitePawnCount[1] >= 1 && blackPawnCount[1]
{
board.Score += whitePawnCount[1];
}
if (whitePawnCount[2] >= 1 && blackPawnCount[2]
{
board.Score += whitePawnCount[2];
}
if (whitePawnCount[3] >= 1 && blackPawnCount[3]
{
board.Score += whitePawnCount[3];
}
if (whitePawnCount[4] >= 1 && blackPawnCount[4]
{
board.Score += whitePawnCount[4];
}
if (whitePawnCount[5] >= 1 && blackPawnCount[5]
{
board.Score += whitePawnCount[5];
}
if (whitePawnCount[6] >= 1 && blackPawnCount[6]
{
board.Score += whitePawnCount[6];
}
if (whitePawnCount[7] >= 1 && blackPawnCount[7]
{
board.Score += whitePawnCount[7];
}

== 0)

== 0)

== 0)

== 0)

== 0)

== 0)

== 0)

== 0)

This concludes the Chess Piece Evaluation. The ChessBin evaluation function is
by no means complete. I will be spending some additional time on this
evaluation function soon and post any updates to this page.

In my experience modifying the evaluation function is extremely dangerous.


Even small changes can significantly weaken your chess engine. The problem is
that we are dealing here with a system of values that relate to each other in
various and complicated ways. It is difficult to tell ahead of time how a small
change in a bonus or penalty will affect how the engine will interpret this change
in various situations. When modifying the chess engine evaluation function is
best to always test against the previous version.
I always play the new version of my chess engine against the previously released
one. This means that you should save different version of your chess game as
you go along. Furthermore if you find your chess engine made a blunder, save
the game, correct the mistake in your code then load the save game to see if that
solved it. Over time you will have a few save games that will allow you to quickly
test your new code. I found that often new code re-introduced some old bug I
solved in an older save game and actually made the chess engine weaker.
If you want to get started on creating your own chess engine download my C#
Chess Game Starter Kit.
Actuellement not 4.7 par 3 personne(s)

Currently 4.7/5 Stars.


1
2
3
4
5

Tags: chess engine, computer chess, board evaluation, chess board evaluation
Chess Bin Engine
Submit to DotNetKicks...
Permalink | Commentaires (3)
Billets lis
Completing the chess engineThe second part of the chess engine class listing,
bringing in move searching as well as end game sc...Chess Board
RepresentationDescription of the Board representation for the Chess Bin Chess
EngineChess Piece Valid MovesGenerates only valid moves for each chess piece
on the board based on the current chess board.
Commentaires
18/04/2009 06:50:58 #

Hi,
Thanks for your great description and explanation...

However, it this evaluation class also included in your chess starter kit?
Because I could not find it there.
Thanks in advance.
Kevin
22/04/2009 10:40:15 #

The evaluation class is not posted in the Chess Game Starter Kit, but all of the
source code is here. Sorry but I want you to do some work
aberent
04/05/2010 09:52:00 #

Hi there,
I was wondering, why do you assign the pieces particular the values
Knight 320
Bishop 325
Rook
500
Queen 975
while in chess litature they have the values knight(300), bishop (300, and maybe
333 because bishoppair is stronger than knight+bishop, or two knights), rook
(500), queen (900)? Maybe another idea is to make this values variable, because
minor pieces are for example in the begining better than mayor pieces, because
they can be develop much quicker than a rook or queen.
I try to make an own chessengine, thats why my interests in your code =D.
Thanks in advance
Xander
Created and Maintained by Adam Berent
www.adamberent.com

Online Chess
Play Online Chess against other players and my Chess Engine!
Recent Posts
Transposition Table and Zobrist Hashing
Performance Reconstruction Phase Three

Quiescence Search and Extensions


Some Performance Optimization Advice
ForsythEdwards Notation
ForsythEdwards Notation
Completing the chess engine
Move Searching Alpha Beta Part 2
Performance Reconstruction Phase Two
Move Searching and Alpha Beta

Downloads
Free Chess Game Download
Chess Game Starter Kit
Miscellaneous
Computer Chess Links
Contact Information
Daily Chess Puzzle
Development of My Chess Engine
Vintage Chess Computers
Vintage Chess Computers
Fidelity Marauder Chess Challenger
Tandy Computerized Chess
Rybka s evaluation
Evaluation
Rybka's evaluation has been the subject of much speculation ever since its
appearance. Various theories have been put forth about the inner workings of the
evaluation, but with the publication of Strelka, it was shown just how wrong
everyone was. It is perhaps ironic that Rybka's evaluation is its most similar part
to Fruit; it contains, in my opinion, the most damning evidence of all.
General Differences
Simply put, Rybka's evaluation is virtually identical to Fruit's. There are a few
important changes though, that should be kept in mind when viewing this
analysis.
Most obviously, the translation to Rybka's bitboard data structures. In
some instances, such as in the pawn evaluation, the bitboard version will
behave slightly differently than the original. But the high-level functionality
is always equivalent in these cases; the changes are brought about
because of a more natural representation in bitboards, or for a slight speed
gain. In other cases the code has been reorganized a bit; this should be
seen more as an optimization than as a real change, since the end result is
the same.
All of the endgame and draw recognition logic in Fruit has been replaced
by a large material table in Rybka. This serves mostly the same purpose as
the material hash table in Fruit, since it has an evaluation and a flags field.
All of the weights have been tuned. Due to the unnatural values of Rybka's
evaluation parameters, they were mostly likely tuned in some automated

fashion. However, there are a few places where the origin of the values in
Fruit is still apparent: piece square tables, passed pawn scores, and the
flags in the material table.
Evaluation Detail
In the following pages I will go into more depth about the details of each aspect
of the evaluations and their similarities and differences.
Pawn evaluation: pawn_get_info()
Piece evaluation: eval_piece()
King Safety/Shelter: eval_king()
Passed Pawns: eval_passer()
Patterns: eval_pattern()
Material

Pawn Evaluation
Rybka's pawn evaluation is very simple. It is again, virtually identical to Fruit's.
The bitboard structure allows for a much more efficient calculation though. The
comparison between them is also very simple.
Pawn Hash Table
First, we will compare the entries for the pawn hash table. Both entries have a
32-bit hashkey, two signed 16-bit scores for opening and endgame, two 8-bit filewise bitmasks for passed pawn files, and a 16 bit pad. In Fruit, the rest is used by
16 bits of flags (of which only 2 bits are actually used) and two 8-bit squares that
are used for draw recognition. Rybka does not have draw recognition (it is
replaced with the material table), so this information is useless. In Rybka, those 4
bytes are replaced by 12 bytes grouped into 3x2 16-bit scores. These scores are
cached king shelter scores, which are discussed here.
Terms
The evaluation terms discussed below use a side-by-side comparison as always
with Fruit and approximate Rybka code. See also the decompilation notes. In
Fruit's pawn structure evaluation the patterns are computed first (doubled,
isolated, etc.), and the scores are computed afterwards. The Rybka decompilation
uses a more compact style, which likely matches the original Rybka code closer
(based on the assembly output). Also, virtually all of Rybka has white and black
coded separately. The white code is used here for the examples. Also, note the
similarity of giving an extra penalty, only in the opening, for some features if the
pawn is on an open file. The endgame score subtraction for backward pawns is
made outside of the if-block in Rybka, but the meaning is still identical. The
change is almost certainly made by the optimizer anyways, since for isolated
pawns the subtraction is inside the if-block.
Also, it should be noted that each evaluation uses the exact same terms in the
exact same order: doubled, isolated, backwards, passers, candidates. I want to
stress that all of the differences shown below are very minor implementational
details, that would be quite natural given a translation of Fruit to bitboards.

Overall, the pawn evaluations of each program are essentially identical.


Doubled Pawns
Doubled pawns are the first and simplest pattern. In Fruit, we look behind the
given pawn for a friendly pawn. In Rybka, we look ahead. These are of course
equivalent. Rybka also has a score of zero for doubled pawns in the opening.
Fruit

Rybka

if ((board->pawn_file[me][file] &
BitLT[rank]) != 0)
doubled = true;
...
if (doubled) {
opening[me] -=
DoubledOpening;
endgame[me] -=
DoubledEndgame;
}

if (MaskPawnDoubled[square] &
Board.pieces[WP])
endgame -= DoubledEndgame;

Isolated Pawns
Isolated pawns come next. In Fruit, the variable t1 represents the bitwise OR of
the rank-wise bitmasks of friendly pawns on adjacent files. So if t1==0, that
means that no pawns are on either of the adjacent files to this pawn. Rybka's
MaskPawnIsolated works the same way.
Fruit

Rybka

if (t1 == 0)
isolated = true;
...
if (isolated) {
if (open) {
opening[me] -= IsolatedOpeningOpen;
endgame[me] -= IsolatedEndgame;
} else {
opening[me] -= IsolatedOpening;
endgame[me] -= IsolatedEndgame;
}
}

if ((MaskPawnIsolated[square] &
Board.pieces[WP]) == 0) {
if (open_file) {
opening -= IsolatedOpeningOpen;
endgame -= IsolatedEndgame;
} else {
opening -= IsolatedOpening;
endgame -= IsolatedEndgame;
}
}

Backward Pawns
Backward pawns are next, and are one of the more complicated pawn terms. t1 is
as discussed above. t2 is the rank-wise bitmask of all pawns on the same file as
the pawn. First, we test whether the pawn is behind all friendly pawns that might
be on adjacent files: t1 & BitLE[rank] in Fruit, (MaskPawnProtectedW[square] &
Board.pieces[WP]) == 0 in Rybka. Next, we test if the pawn is "really backward".
This basically means that the pawn can't advance one square to meet a friendly
pawn. We also have to check for pawns on the second rank, because they could
possibly advance twice to meet another pawn. We see if there is a pawn blocking
the advance, or an opponent pawn attacking the advance square. In Rybka this is
a bit different, because any pawn (not just those on the second rank) can
advance twice to escape backwardness, and because it is not checked whether
there is another pawn blocking the pawn from advancing.

Fruit

Rybka

if ((t1 & BitLE[rank]) == 0) {


backward = true;
// really backward?
if ((t1 & BitRank1[rank]) != 0) {
ASSERT(rank+2<=Rank8);
if (((t2 & BitRank1[rank])
| ((BitRev[board->pawn_file[opp][file-1]]
|
BitRev[board->pawn_file[opp][file+1]])
&
BitRank2[rank])) == 0) {
backward = false;
}
} else if (rank == Rank2 && ((t1 &
BitEQ[rank+2]) != 0)) {
ASSERT(rank+3<=Rank8);
if (((t2 & BitRank2[rank])
| ((BitRev[board->pawn_file[opp][file-1]]
|
BitRev[board->pawn_file[opp][file+1]])
&
BitRank3[rank])) == 0) {
backward = false;
}
}
}
...
if (backward) {
if (open) {
opening[me] -= BackwardOpeningOpen;
endgame[me] -= BackwardEndgame;
} else {
opening[me] -= BackwardOpening;
endgame[me] -= BackwardEndgame;
}
}

if ((MaskPawnProtectedW[square] &
Board.pieces[WP]) == 0) {
if ((MaskPawnAttacksW1[square] &
Board.pieces[BP]) ||
((MaskPawnAttacksW2[square] &
Board.pieces[BP]) &&
((MaskPawnAttacks[White][square] &
Board.pieces[WP])==0))) {
if (open_file)
opening -= BackwardOpeningOpen;
else
opening -= BackwardOpening;
endgame -= BackwardEndgame;
}
}

Passed Pawns
Passed pawns are simply detected at this point, and follow the standard
definition. The dynamic evaluation of pawns is discussed here. For now, we store
an 8-bit mask for each side with the files containing passed pawns.
Fruit

Rybka

if (((BitRev[board->pawn_file[opp][file1]] |
BitRev[board->pawn_file[opp][file+1]])
&
BitGT[rank]) == 0) {
passed = true;
passed_bits[me] |= BIT(file);
}

if ((MaskPawnPassedW[square] &
Board.pieces[BP]) == 0)
wp_pass_file |= PawnPassedFile[file];

Candidate Passed Pawns


Candidate passed pawns have a similar definition in both programs. The pawn
must be on an open file. Then we take the count of all defender pawns (friendly
pawns behind) and the count of all attacker pawns (enemy pawns in front). If
there are an equal or greater number of defenders, the pawn is a candidate.
There is an exception in Fruit though--if the pawn has enough defenders, we also
check the count of direct defenders and attackers, that is, pawns that are already
attacking our pawn. The number of direct defenders must also be greater or
equal to the number of direct attackers. While the definition is almost identical,
the scores here are where we get an early glimpse of the real similarities. The
scoring is discussed more here.
Fruit
n = 0;
n += BIT_COUNT(board->pawn_file[me]
[file-1]&BitLE[rank]);
n += BIT_COUNT(board->pawn_file[me]
[file+1]&BitLE[rank]);
n -= BIT_COUNT(BitRev[board>pawn_file[opp][file-1]]&BitGT[rank]);
n -= BIT_COUNT(BitRev[board>pawn_file[opp][file+1]]&BitGT[rank]);
if (n >= 0) {
// safe?
n = 0;
n += BIT_COUNT(board->pawn_file[me]
[file-1]&BitEQ[rank-1]);
n += BIT_COUNT(board->pawn_file[me]
[file+1]&BitEQ[rank-1]);
n -= BIT_COUNT(BitRev[board>pawn_file[opp][file1]]&BitEQ[rank+1]);
n -= BIT_COUNT(BitRev[board>pawn_file[opp]
[file+1]]&BitEQ[rank+1]);
if (n >= 0) candidate = true;
...

Rybka

if (open_file) {
mask1 = MaskPawnProtectedW[square]
& Board.pieces[WP];
mask2 = MaskPawnPassedW[square] &
Board.pieces[BP];
if (popcnt(mask1) >= popcnt(mask2)) {
opening += CandidateOpening[rank];
endgame += CandidateEndgame[rank];
}
}

if (candidate) {
opening[me] +=
quad(CandidateOpeningMin,CandidateO
peningMax,rank);
endgame[me] +=
quad(CandidateEndgameMin,CandidateE
ndgameMax,rank);
}

Piece Evaluation
Rybka and Fruit evaluate pieces next. This evaluation is very simple, primarily
based on mobility. There are a few more bonuses besides that. Firstly, we will
examine the mobility evaluation of both programs to show their equivalence.

Mobility
The mobility calculations of Fruit and Rybka seem different, but Rybka's turns
out to be a simple bitboard translation of Fruit's.
Fruit's mobility is based on the MobUnit[][] array. It is indexed by the color of
the piece we're evaluating the mobility for, and then by the piece type of the
piece that's being attacked. While this seems complicated, the initialization turns
out to be very simple: one point is given for attacking an empty piece or an
opponent piece, and no points are given for attacking friendly pieces. This point
total is added to an offset and is then multiplied by a weight. Here is the bishop
mobility to illustrate this point:
Bishop Mobility in Fruit
mob = -BishopUnit;
for (to = from-17; capture=board->square[to], THROUGH(capture); to -= 17) mob
+= MobMove;
mob += unit[capture];
// Other directions...
op[me] += mob * BishopMobOpening;
eg[me] += mob * BishopMobEndgame;
Note also that Fruit has a constant added to each piece (BishopUnit for
bishops, etc.). Since this is just a constant, it can be added into the piece value
(by subtracting BishopUnit*BishopMobOpening for opening, etc.), thus it is not
important to the semantics of the code.
This type of calculation is very easily expressed in bitboards using a mask and
a population count. Rybka's mobility evaluation is indeed a direct translation of
Fruit's code to bitboards. The set of squares attacked by the piece (bishop in this
case), but which are not friendly pieces, are given one point each, counted using
the popcnt() function. Note that this number is the same as "mob" as used in
Fruit. This is then multiplied by the weights for opening and endgame for each
piece and added to the totals.
The attack bitboards that are calculated for mobility in Rybka are also used for
King Safety evaluation
Bishop Mobility in Rybka
attacks = bishop_attacks(square);
// evaluate king safety here...
mob = popcnt(attacks & ~own_pieces);
opening += mob * BishopMobOpening;
endgame += mob * BishopMobEndgame;
More Piece Evaluation
Besides mobility, Fruit and Rybka only have a few basic terms for piece
evaluation. An explanation of the bonuses for each piece follows.

Minors
In both Rybka and Fruit, knights and bishops are evaluated only based on
mobility. No other terms are used.
Rooks
In Fruit and Rybka, there are two main rook bonuses: open file and seventh
rank. Open files are fairly simple, but have a rather uncommon formulation that
Fruit and Rybka share. Also, like mobility above, Fruit adds in a constant for every
rook (to balance the open file scores between positive and negative). This can be
added in to the piece score, and can be ignored for the analysis here.
In both Rybka and Fruit, we start by checking if there is a friendly pawn on the
same file. In Rybka, however, we only check for pawns in front of the rook. This is
a "semi-open" file. If there aren't any, we then check for an enemy pawn on the
same file.
Fruit

op[me] -= RookOpenFileOpening / 2;
eg[me] -= RookOpenFileEndgame / 2;

Rybka
static const int
RookSemiOpenFileOpening = 64;
static const int
RookSemiOpenFileEndgame = 256;
static const int RookOpenFileOpening =
1035;
static const int RookOpenFileEndgame =
428;

rook_file = SQUARE_FILE(from);
if (board->pawn_file[me][rook_file] ==
0) {
op[me] += RookSemiOpenFileOpening;
eg[me] += RookSemiOpenFileEndgame; file_bb = mask_open_file_w[square];
if ((Board.pieces[WP] & file_bb) == 0) {
if (board->pawn_file[opp][rook_file] == opening += RookSemiOpenFileOpening;
0) {
endgame +=
op[me] += RookOpenFileOpening RookSemiOpenFileEndgame;
RookSemiOpenFileOpening;
eg[me] += RookOpenFileEndgame if ((Board.pieces[BP] & file_bb) == 0) {
RookSemiOpenFileEndgame;
opening += RookOpenFileOpening }
RookSemiOpenFileOpening;
endgame += RookOpenFileEndgame ...
RookSemiOpenFileEndgame;
}
}
...
}
To extend the open file evaluation, we check if the open file is one of the three
surrounding files of the opponent king. In Fruit, this is done by checking if the filedistance between the rook and king is less than or equal to one. In Rybka, the
same calculation is done with a bitboard mask, by ANDing the file of the rook with
the opponent king's attack set. If the rook is on the same file as the king, we add
an additional bonus.
Fruit

Rybka

if ((mat_info->cflags[opp] &

static const int

MatKingFlag) != 0) {
king = KING_POS(board,opp);
king_file = SQUARE_FILE(king);
delta = abs(rook_file-king_file);
if (delta <= 1) {
op[me] += RookSemiKingFileOpening;
if (delta == 0)
op[me] += RookKingFileOpening RookSemiKingFileOpening;
}
}

RookSemiKingFileOpening = 121;
static const int RookKingFileOpening =
974;
if ((flags & MatBlackKingFlag) &&
(bking_moves & file_bb)) {
opening += RookSemiKingFileOpening;
if (Board.pieces[BK] & file_bb)
opening += RookKingFileOpening RookSemiKingFileOpening;
}

Finally, we check for a rook on the seventh rank. In order for the rook to get
the bonus, either the opponent must have pawns on the second rank, or their
king on the first rank.
Fruit

Rybka

if (PAWN_RANK(from,me) == Rank7) {
if ((pawn_info->flags[opp] &
BackRankFlag) != 0
||
PAWN_RANK(KING_POS(board,opp),me)
== Rank8) {
op[me] += Rook7thOpening;
eg[me] += Rook7thEndgame;
}
}

if (RankOf(square) == Rank7) {
if ((Board.pieces[BP] & MaskRank7) ||
(Board.pieces[BK] & MaskRank8)) {
endgame += Rook7thEndgame;
opening += Rook7thOpening;
}
}

Queens
Besides mobility, the only evaluation term for queens is the seventh rank
bonus. This bonus is calculated in the exact same way as for rooks above, but
with a different value.
Conclusion
From looking at the piece evaluation of both engines, we find that they are
almost identical. As with most evaluation terms, Rybka's weights have been
tuned differently. The only other difference in the piece evaluation is that, in
Rybka, open files for rooks are based only on the pawns in front of the rook. Open
files for rooks in Fruit are based on every pawn on the file. Of course, this
difference is fairly trivial, and will not make a difference most of the time.
King Evaluation
Rybka's and Fruit's king evaluation are both very similar. Rybka's is a heavily
optimized version though.

Using King Safety


First, we have to actually determine whether to use king safety or not. In both
programs, we keep a flag for each side in the material table. This flag has the
same meaning in both programs: if the opponent has a queen, and at least one
other piece (rook, bishop, or knight), then we must calculate king safety for that
side.
Attacks
For the main part of the king safety evaluation, we calculate which pieces are
attacking the king and its surrounding area. This requires generating the attacks
for each piece. Whereas in Fruit these attacks are calculated in anindependent
function, Rybka saves time by using the attacks for each piece while calculating
mobility. The functions are equivalent, but Rybka spends only have the time
generating attacks. Rybka has separate code for each piece as well.
The piece_attack_king() function in Fruit detects whether a piece attacks any of
the (up to) 8 squares surrounding the king, while bking_area in Rybka is a
bitboard of those surrounding squares. If the attacks bitboard intersects with this
bitboard, then the piece attacks the king area. Note that in both programs, only
the surrounding squares count, not the actual square of the king.
We keep two counters for this calculation: the number of pieces that attack the
opponent king's area, and a sum of weights of those pieces. The weights are
different in Rybka of course, but both programs have zero weight for pawns.
Fruit

Rybka (knights)

if
(piece_attack_king(board,piece,from,kin
g)) {
piece_nb++;
attack_tot += KingAttackUnit[piece];
}

attacks = knight_attacks(square);
mob_w |= attacks;
if (attacks & bking_area) {
piece_nb++;
attack_tot += KingAttackUnit[Knight];
}
// mobility calculation here

Once we've gone through all the piece attacks, we need to get our final score.
This is done with an array lookup of weights indexed by the number of pieces
attacking the king, multiplied by the sum of the weights of those pieces. Fruit
keeps a separate factor KingAttackOpening that is also multiplied in. Rybka
effectively keeps this factor inside the constant KingAttackUnit table. This should
be seen as simply a speed optimization.
Fruit

Rybka

op[colour] -= (attack_tot *
KingAttackOpening *
KingAttackWeight[piece_nb]) / 256;

opening -= (KingAttackWeight[piece_nb]
* attack_tot) / 32;

Shelter
After we have calculated the king attacks, we evaluate the king's shelter. The
king shelter in both Rybka and Fruit measures the safety of the king position
based on the positions of the pawns on the (up to) 3 files adjacent to the king.

This comparison is rather tricky, since Rybka's evaluation is heavily optimized.


First, we will look at Fruit. Fruit takes three scores: the shelter score for the
king's current position, and two "castling" scores. The castling scores are the
shelter scores for the castling target squares if the king is able to castle in that
direction. From these scores we take two penalties. The first penalty is the score
for the current position, and the second penalty is the best of all three scores.
This means that if the current king position has a bad shelter, but we can castle
into a position with a good shelter, the penalty shouldn't be too bad. We then
take the average of the two penalties to get our final shelter score.
Rybka does the same thing, but in a way that can be optimized for storage in
the pawn hash table. The difference is that when we're first getting our score, our
king square is generalized to either C1, E1, or G1 (or the reverse square for
black). This is done with the SquareWing table. If the king is on the A, B, or C
files. we take the score as if it was on C1; for the D and E files, E1; and for the F,
G, and H files G1. The two castling scores are based on C1 and G1 of course.
Since there are only three possible squares the king shelter can be evaluated for,
Rybka can store the 3 values for each side in the pawn hash table. We see in the
below code that Rybka's entry->white_king_shelter[KingSide] is equivalent to
Fruit's shelter_square(board,G1,me), etc.
Fruit

Rybka

penalty_1 =
shelter_square(board,KING_POS(board,m
e),me);
penalty_2 = penalty_1;

wing = SquareWing[wking_square];
penalty_1 = entry>white_king_shelter[wing];
penalty_2 = penalty_1;

if ((board->flags &
FlagsWhiteKingCastle) != 0) {
tmp = shelter_square(board,G1,me);
if (tmp < penalty_2) penalty_2 = tmp;
}

if ((Board.flags &
FlagsWhiteKingCastle) != 0) {
tmp = entry>white_king_shelter[KingSide];
if (tmp < penalty_2) penalty_2 = tmp;
}

if ((board->flags &
FlagsWhiteQueenCastle) != 0) {
tmp = shelter_square(board,B1,me);
if (tmp < penalty_2) penalty_2 = tmp;
}
penalty = (penalty_1 + penalty_2) / 2;
op[me] -= (penalty * ShelterOpening) /
256;

if ((Board.flags &
FlagsWhiteQueenCastle) != 0) {
tmp = entry>white_king_shelter[QueenSide];
if (tmp < penalty_2) penalty_2 = tmp;
}
opening -= (penalty_1 + penalty_2) / 2;

Shelter Values
We have seen that the way Rybka and Fruit evaluate shelter for both the king
position and the two castled positions, and how they are combined. Now we have
to look at how they evaluate the shelter for each position.
We need to look at the three adjacent files to a square. In Fruit, for each of
these files we take the furthest back pawn that is still in front of the king on that
file. The penalty scales quadratically from 36 to 0 going in reverse. So a pawn on
the 2nd rank gets a penalty of 0, a pawn on the 3rd rank gets a penalty of 11, 4th
rank gets 20, etc. This bonus is different in Rybka, and is simply a table.

shelter_file() in Fruit

Rybka equivalent
const int shelter_value[5] = { 1121, 0,
214, 749, 915 };

dist = BIT_FIRST(board>pawn_file[colour][file]&BitGE[rank]);
dist = Rank8 - dist;
penalty = 36 - dist * dist;

if (pawns_on_file) {
dist = min_rank;
dist = MIN(4, min_rank);
} else
dist = 0;
penalty = shelter_value[dist];

Next we have pawn storms. This is basically the same as pawn shelters, but we
look at the opponent's pawns. Fruit's way of calculating it is a bit different, while
Rybka uses essentially the same method as it uses for shelter files. We add the
score for each file into the penalty.
storm_file() in Fruit
penalty = 0;
switch (dist) {
case Rank4:
penalty = StormOpening * 1;
break;
case Rank5:
penalty = StormOpening * 3;
break;
case Rank6:
penalty = StormOpening * 6;
break;
}

Rybka equivalent
const int storm_value[5] = { 0, 0, 2334,
653, 310 };
if (pawns_on_file) {
dist = min_rank;
dist = MIN(4, min_rank);
} else
dist = 0;
penalty = shelter_value[dist];

We then combine the shelter scores of the three files by multiplying the center
score by 2 and adding the other two. We also apply the "weak back rank" bonus if
all three pawns are on the second rank (possibly exposing the king to back rank
mates). Rybka does the same addition for the bonuses, and also applies the back
rank bonus. The storm scores are simply added in, with no doubling in the center.
shelter_square() in Fruit

Rybka equivalent

penalty +=
shelter_file(board,file,rank,colour) * 2;
if (file != FileA)
penalty += shelter_file(board,file1,rank,colour);
if (file != FileH)
penalty +=
shelter_file(board,file+1,rank,colour);

penalty +=
shelter_file(board,file,rank,colour) * 2;
if (file != FileA)
penalty += shelter_file(board,file1,rank,colour);
if (file != FileH)
penalty +=
shelter_file(board,file+1,rank,colour);

if (penalty == 0) penalty = 11;

if (penalty == 0) penalty = 794;

penalty += storm_file(board,file,colour); penalty += storm_file(board,file,colour);


if (file != FileA)
if (file != FileA)

penalty += storm_file(board,file1,colour);
if (file != FileH)
penalty +=
storm_file(board,file+1,colour);

penalty += storm_file(board,file1,colour);
if (file != FileH)
penalty +=
storm_file(board,file+1,colour);

All of this shelter evaluation code in Rybka above is an equivalent; it doesn't


appear in the Rybka binary. It is there simply to illustrate what is in the
precomputed tables. These precomputed tables are used during the pawn
evaluation to quickly evaluate shelters. Rybka makes two bitmasks, representing
the friendly and enemy pawns in front of the king. We take the 4x3 rectangle of
the closest pawns in front of the king. For instance, with the king on G1, we take
the pawn positions on the rectangle F5-H5-H2-F2. This creates two masks of 12
bits each to index a table of 4096 entries. In each entry we store the value
computed above in Rybka's shelter_square()-equivalent function.
Passed Pawn Evaluation
Rybka's passed pawn evaluation was originally thought to be extremely
complex. In reality though, it's really very simple. Here I will show that the passed
pawn evaluation is equivalent to Fruit's, except for different weights, using a
quick approximation of the static exchange evaluation, and the division of the
free pawn bonus into three separate bonuses.
Quad
In showing the equivalence between Fruit and Rybka's passed pawn
evaluation, it is first necessary to understand the quad() function in Fruit. quad()
calculates a bonus for a passed pawn based on a minimum and a maximum, with
the final score being based on the rank of the pawn. It does this using a lookup
table with a value from 0 to 256. This is used as a ratio (over 256) which is how
far between the minimum and maximum the final score is. If the pawn is on the
7th rank, it gets the full bonus; if it is on the 2nd or 3rd, it gets the minimum. On
ranks 4-6 it gets somewhere in between.
quad() in Fruit
for (rank = 0; rank < RankNb; rank++) Bonus[rank] = 0;
Bonus[Rank4]
Bonus[Rank5]
Bonus[Rank6]
Bonus[Rank7]

=
=
=
=

26;
77;
154;
256;

...
int quad(int y_min, int y_max, int x) {
int y;
y = y_min + ((y_max - y_min) * Bonus[x] + 128) / 256;
return y;
}

In Fruit, there are several bonuses for a passed pawn. The endgame score has
a fixed minimum, and all the bonuses for the pawn simply increase the
maximum. This is equivalent to adding a set endgame bonus with quad()
(between PassedEndgameMin and PassedEndgameMax) and using quad() for
each subsequent bonus with a minimum of 0 and a max of the bonus. This has
been implemented in an optimized (and slightly confusing) way in Fruit. To
illustrate this, here is Fruit's evaluation and a simplified version. They
both produce the same output, but the simplified version is a bit slower.
Endgame passed pawn evaluation in
Fruit

Simplified version

min = PassedEndgameMin;
max = PassedEndgameMax;
delta = max - min;
...
// misc. bonuses
delta += bonus;
...

eg[att] +=
quad(PassedEndgameMin,PassedEndga
meMax,rank);
...
// misc. bonuses
eg[att] += quad(0,bonus,rank);

eg[att] += min;
if (delta > 0) eg[att] +=
quad(0,delta,rank);
Opening
The opening value for passed pawns is very simple in both programs: we
simply add a fixed bonus based on the rank.
Fruit

Rybka

op[att] +=
quad(PassedOpeningMin,PassedOpening opening += PassedOpening[rank];
Max,rank);

Endgame
Rybka and Fruit both have the same basic structure in the endgame passed
pawn scoring: calculate a minimum and a maximum value and interpolate the
real value based on the rank of the pawn. See above for details regarding Fruit;
Rybka works the same way. We start out initializing the minimum:
Fruit

Rybka

min = PassedEndgameMin;
...
eg[att] += min;

endgame += PassedEndgame[rank];

Dangerous Bonuses
Next, we add the "dangerous" bonuses to the endgame maximum. There are
actually a few of these: if the opponent has no pieces, we detect whether the
passer is unstoppable, or if our king is in a position to protect it while promoting.
We then check if the passer is free; that is, it can walk to the promotion square
without being blocked or captured.
The unstoppable passer is simply a passer that isn't blocked by a friendly piece
and the opponent king is outside its "square". The king passer is a passer on the
6th or 7th rank which the king defends while simultaneously defending the
promotion square. These defintions are exact bitboard equivalents in Rybka, and
they both receive the same bonus of UnstoppablePasser. This bonus is not based
on rank like the other bonuses, but is simply the value of a queen minus the
value of a pawn.
Next is the free pawn. The opponent has pieces in this case to potentially keep
our pawn from promoting, so we need to check if it can escape. In Fruit, the
square in front of the pawn must be empty, and the pawn must be able to
advance safely there. We use the static exchange evaluation (SEE) to make sure
that even if the square is attacked by the opponent, we can recapture on the
square. This check is only done on the square directly in front of the pawn in
Fruit, but since Rybka is bitboard based, we can quickly do the same calculation
for all squares in front of the pawn up to the promotion square. To do this we
make an approximation of the SEE that is usually equivalent. We simply make
sure that for every square in the promotion path that is attacked by the
opponent, we also have a piece defending that square. There are some cases
where this might not be the same as being able to advance safely (according to
SEE), but they are rather unusual (two knights attacking, one queen defending).
There is also one more slight difference here: in Fruit, to be a free passer, all of
the above conditions must apply. In Rybka, we break the conditions down and
award partial bonuses if only some of the conditions are met.
Fruit

Rybka

if ((Board.pieces[BQ] | Board.pieces[BR] |
Board.pieces[BB] | Board.pieces[BN]) ==
0) {
if (white_unstoppable_passer(square) ||
white_king_passer(square))
if (board->piece_size[def] <= 1
&& (unstoppable_passer(board,sq,att) || endgame += UnstoppablePasser;
} else {
king_passer(board,sq,att))) {
if ((mob & Board.pieces[White]) == 0)
delta += UnstoppablePasser;
endgame +=
} else if (free_passer(board,sq,att)) {
PassedUnblockedOwn[rank];
delta += FreePasser;
if ((mob & Board.pieces[Black]) == 0)
}
endgame +=
PassedUnblockedOpp[rank];
if (((~mob_w) & mob & mob_b) == 0)
endgame += PassedFree[rank];
}
King Distance
Both Rybka and Fruit now apply a bonus based on the distances of both kings
to the square in front of the pawn. These bonuses are applying the same way,
two bonuses, one for each king, simply multiplied by the distance of that king to

the square in front of the pawn. The bonuses in Rybka are also based on the rank
of the pawn.
Fruit

Rybka

delta -=
pawn_att_dist(sq,KING_POS(board,att),at
t) * AttackerDistance;
delta +=
pawn_def_dist(sq,KING_POS(board,def),a
tt) * DefenderDistance;

endgame -=
pawn_att_dist(square,wking_square,Whit
e) * PassedAttDistance[rank];
endgame +=
pawn_def_dist(square,bking_square,Whit
e) * PassedDefDistance[rank];

Values
If the above mentioned similarities in the evaluations were all that were there,
this might be simply a coincidence. The exact same set of terms are used, and
the same method of accumulating opening and endgame scores is used
(interpolating between maximum and minimum based on rank, fixed score for
opening, bonuses increase maximum endgame score). The free passer bonuses
are separated, though, and the semantics for adding bonuses are changed (albeit
into a mathematically equivalent method). But we haven't yet looked at the
values for the pawns. As discussed above, Fruit's bonuses are based on the
Bonus array, with values {0..., 26, 77, 154, 256, 0}, where rank 4 is 26, 5 is 77,
etc. Once we look at Rybka's values, we see that they are based on the same
Bonus array, and are simply precalculated outputs of the quad() function. Rybka's
values and their Fruit equivalents (see the simplified Fruit code above) are shown
below.
Also, note that in the Rybka code, the equivalent rank 8 value of the Bonus
array is 256 (like rank 7) instead of 0 as in Fruit. This difference is completely
meaningless however, since there can never be a pawn on the 8th rank.
Rybka

Fruit Equivalent

int PassedOpening[8] = { 0, 0, 0, 489,


1450, 2900, 4821, 4821 };
int PassedEndgame[8] = { 146, 146,
146, 336, 709, 1273, 2020, 2020 };
int PassedUnblockedOwn[8] = { 0, 0, 0,
26, 78, 157, 262, 262 };
int PassedUnblockedOpp[8] = { 0, 0, 0,
133, 394, 788, 1311, 1311 };
int PassedFree[8] = { 0, 0, 0, 101, 300,
601, 1000, 1000 };
int PassedAttDistance[8] = { 0, 0, 0, 66,
195, 391, 650, 650 };
int PassedDefDistance[8] = { 0, 0, 0,
131, 389, 779, 1295, 1295 };

int
int
int
int
int
int
int
int
int

PassedOpeningMin = 0;
PassedOpeningMax = 4821;
PassedEndgameMin = 146;
PassedEndgameMax = 2020;
PassedUnblockedOwnMax = 262;
PassedUnblockedOppMax = 1311;
FreePasserMax = 1000;
AttackerDistanceMax = 650;
DefenderDistanceMax = 1295;

We also need to take a look at the candidate bonus. This bonus is done
statically and stored in the pawn hash table, as discussed here, but we haven't
looked at the values yet. We see that in Fruit candidates are scored using the
same quad() function. And sure enough, Rybka's scores are based on the same
array.

Rybka
int CandidateOpening[8] = { 0, 0, 0,
382, 1131, 2263, 3763, 3763 };
int CandidateEndgame[8] = { 18, 18,
18, 181, 501, 985, 1626, 1626 };

Fruit Equivalent
int CandidateOpeningMin = 0;
int CandidateOpeningMax = 3763;
int CandidateEndgameMin = 18;
int CandidateEndgameMax = 1626;

Pattern Evaluation
Fruit and Rybka both have a small pattern evaluation. These simply help
prevent a few basic positional blunders that are hard to evaluate properly without
special code. There are only three separate patterns, and all are present in the
same form in both programs. The only functional difference between the two
pattern evaluations is that Rybka does not divide the trapped bishop bonus by 2
in the case where Fruit does.
Trapped Bishop
The first part of the eval_pattern() function looks at trapped bishops. By Fruit's
definition, a trapped bishop is simply a bishop on A6, A7, or B8 with an opponent
pawn diagonally in front of it, for example a white bishop on B8 and a black pawn
on C7. This is to prevent the common error like Bxa7 a6, where the bishop
captures a pawn but then becomes useless. The mirror image also holds, so a
white bishop on G8 with a black pawn on F7 is also trapped, and a black bishop
on B1 with a white pawn on C2 is trapped. If the bishop is on A6, the penalty is
halved. Rybka's trapped bishop evaluation has the same definition, with the only
difference being that trapped bishops on A6/H6/A3/H3 get the full bonus instead
of just half. This allows for a very quick calculation in bitboards, where all the
possible squares to be trapped on are calculated simultaneously. Rybka tests for
a pawn on B5, B6, or C7 with a pawn diagonally behind it (as well as the mirror
images as noted) with just 3 64-bit operations.
Fruit

Rybka

if ((board->square[A7] == WB &&
board->square[B6] == BP)
|| (board->square[B8] == WB && board>square[C7] == BP)) {
*opening -= TrappedBishop;
*endgame -= TrappedBishop;
}
if ((board->square[H7] == WB &&
board->square[G6] == BP)
|| (board->square[G8] == WB && board>square[F7] == BP)) {
*opening -= TrappedBishop;
*endgame -= TrappedBishop;
}
if (board->square[A6] == WB && board>square[B5] == BP) {
*opening -= TrappedBishop / 2;
*endgame -= TrappedBishop / 2;

if (((Board.pieces[WB] >> 7) &


Board.pieces[BP] & MaskB5B6C7) ||
((Board.pieces[WB] >> 9) &
Board.pieces[BP] & MaskG5G6F7)) {
opening -= TrappedBishop;
endgame -= TrappedBishop;
}

}
if (board->square[H6] == WB && board>square[G5] == BP) {
*opening -= TrappedBishop / 2;
*endgame -= TrappedBishop / 2;
}

Blocked Bishop
Next we test for a blocked bishop. This is where a bishop is still on its initial
square and is prevented from moving by a pawn on D2/E2, and with a piece
blocking this pawn from advancing. This calculation is made in a very
straightforward way in both Rybka and Fruit. Rybka uses bitboard masks to test
for squares instead of using its 64-element sq array. Since this isn't a parallel
operation, the bitboards do not speed up the calculation.
Fruit

Rybka

if (board->square[D2] == WP && boardif ((Board.pieces[WB] & MaskC1) &&


>square[D3] != Empty &&
(Board.pieces[WP] & MaskD2) &&
board->square[C1] == WB) {
(Board->occupied & MaskD3))
*opening -= BlockedBishop;
opening -= BlockedBishop;
}

Blocked Rook
Finally we look for a blocked rook. This pattern prevents a king moving into a
wing without castling, thereby trapping the rook to the side of the king. This test
is done with a quick approximation, by only checking the position of the king and
rook, and not the pawns. Fruit and Rybka have functionally identical code here
too, and again Rybka's bitboard structure allows for the quick parallel testing of
the king and rook positions.
Fruit

Rybka

if ((board->square[C1] == WK || board>square[B1] == WK)


&& (board->square[A1] == WR || board>square[A2] == WR ||
board->square[B1] == WR)) {
*opening -= BlockedRook;
}

if ((Board.pieces[WR] & MaskA1B1A2)


&&
(Board.pieces[WK] & MaskB1C1))
*opening -= BlockedRook;

Material
The material tables in Rybka were one of the more interesting features
introduced. Their implementation was a new way to evaluate material
imbalances. The indexing and evaluations in the table seem to be unique, but
there are some very interesting similarities in the information stored in the table

with Fruit.
Structure
Rybka's material tables are implemented as a massive data structure that is
indexed by the count of every piece on the board. The count of each piece is
limited to a reasonable maximum, that can only be exceeded by promotions. This
is done to keep the table a reasonable size. Pawns have a count from 0-8, minors
and rooks have a count from 0-2, and queens are from 0-1. The total size of the
table is thus 9*9*3*3*3*3*3*3*2*2 entries. The table is indexed in a sort of split
base, with the pawn counts as the most significant indices. This means that while
positions with, say, 4 queens will not overflow the index (but will point to an entry
with an incorrect material configuration). The evaluation for a material
configuration is stored as a 32-bit integer, which is added to the material balance
determined with a sum of piece values.
In addition to the material values, Rybka keeps flags for certain situations in
the table, as well as a phase value. One flag, the flag for lazy evaluation, is only
in Rybka (Fruit has no lazy eval). All of the other flags come directly from Fruit.
The structure of the material table (at the source code level) isn't certain. It
seems likely, based on the disassembly, that the data type is something like this:
Rybka Material Table Structure
struct {
unsigned char flags;
unsigned char cflags;
unsigned char phase;
bool lazy_eval;
int mat_value;
};
The use of unsigned char and bool for phase and lazy eval are quite likely,
because of their use: the assembly code uses the byte registers for dealing with
them (al, bl, etc.). Phase is used only after zero-extending from a byte register to
an int (hence the unsigned). Lazy eval is most likely a bool in the source because
it takes the value 0 or 1, whereas the other flags use other bits, and it is not in
contiguous memory with the flags (the phase field separates them).
The flags fields are unclear, though. In Fruit, there are two sets of flags: color
dependent and not. The color dependent flags are stored in a two-element array
(cflags) and the others are stored in one element (flags). Each is 8 bits, giving 8
bits total. It is at least clear that flags and cflags are stored in separate fields in
Rybka--they are accessed in the assembly using byte ptr semantics, with cflags
taken from the stack address of flags+1.
It seems that cflags is not an array though. It has two flags, MatWhiteKingFlag
and MatBlackKing flag that are bits 0x08 and 0x80 respectively. The same flag is
in Fruit, MatKingFlag, used with bit 0x08 (1 << 3). This is stored in cflags[color],
to indicate the flag for both colors. In Rybka's material structure, it is as if it had
the same array but with 4-bit bitfields instead of bytes (though it is not possible
in C to have arrays of bitfields)--this would put the flags in the same bits that
they are now. This compression of two bytes to one byte was most likely done so
that each material table entry would be 8 bytes long. The use of 0x08 for a king
safety flag in both programs is certainly interesting, though. The exact usage of
the flags are dicussed below.
Also, for the one flag used in Rybka in the color-independent field,

DrawBishopFlag, it is stored as bit 0x80. However, Rybka's code only tests if the
flags field is nonzero, so the exact value is irrelevant. In Fruit, the same flag is in
bit 0x02 (1 << 1).

Flags
Below, I compare the different material flags used in both Rybka and Fruit. I will
note that all of the formulae for Rybka's flags have been decoded--since the
material table is a large constant array in the Rybka executable, the code to set
the flags is not there. The formulae are found by analyzing the pattern of when it
appears in the material table.
There are a set of flags in Fruit that are not in Rybka. All of these
(DrawNodeFlag, MatRookPawnFlag, MatBishopFlag, and MatKnightFlag) are not
included in Rybka because it does not have any separate endgame knowledge,
which is the purpose of all of these flags in Fruit. Rybka has all other flags that
are in Fruit,
and also an additional lazy evaluation flag. Fruit does not have lazy evaluation, so
there is no flag in it.

MatKingFlag
Fruit stores a flag for each color for whether king safety will be evaluated. This
is stored as 0x08 in the cflags array in the material table (see above). The
formula for this table is shown below. In Rybka, the exact same formula is used--if
the enemy has a queen and at least two pieces total, king safety is evaluated for
that side. Note also that cflags is a single byte, not a two-byte array as in Fruit
(see above, again).
MatKingFlag in Fruit

MatKingFlag in Rybka

const int MatKingFlag = 1 << 3;

const int MatWhiteKingFlag = 1 << 3;


const int MatBlackKingFlag = 1 << 7;

if (bq >= 1 && bq+br+bb+bn >= 2)


cflags[White] |= MatKingFlag;
if (wq >= 1 && wq+wr+wb+wn >= 2)
cflags[Black] |= MatKingFlag;

if (bq >= 1 && bq+br+bb+bn >= 2)


cflags |= MatWhiteKingFlag;
if (wq >= 1 && wq+wr+wb+wn >= 2)
cflags |= MatBlackKingFlag;

DrawBishopFlag
Fruit and Rybka store a flag in their material tables for signifying the possibility
of an opposite-color bishop endgame, which is generally drawish. The flag has
the exact same formula in both programs: there must be only bishops and pawns,
each side must have exactly one bishop, and the difference in the number of
pawns of each side cannot be more than two.
DrawBishopFlag in Fruit

DrawBishopFlag in Rybka

const int DrawBishopFlag = 1 << 1;

const int DrawBishopFlag = 1 << 7;

if (wq+wr+wn == 0 && bq+br+bn ==


0) {
if (wb == 1 && bb == 1) {
if (wp-bp >= -2 && wp-bp <= +2) {
flags |= DrawBishopFlag;
}
}
}

if (wq+wr+wn == 0 && bq+br+bn ==


0) {
if (wb == 1 && bb == 1) {
if (wp-bp >= -2 && wp-bp <= +2) {
flags |= DrawBishopFlag;
}
}
}

The usage of these flags is just as interesting: at the very end of the
evaluation, after the total score is computed, the flag is checked. Since both
programs do not distinguish the color of the bishops in the material table, the flag
only indicates whether an OCB ending is possible. The color of the bishops must
still be checked. The actual check is done in different ways because Rybka is in
bitboards, but the test has the same meaning. In Fruit, if it is really an OCB
ending, the mul value is set to 8 for each side (provided a draw recognizer has
not already marked this as a drawish ending). After this check, Fruit multiplies the
score by mul[color]/16, with the color depending on which side is ahead. If both
sides have a value of 8, as is the case when there is not a draw recognition, this
has the effect of dividing the score by two, bringing it closer to the draw value, 0.
In Rybka, there is no mul value, as there aren't any draw recognizers. But we see
that in the case of an OCB ending, it does the same thing as Fruit: divide the
score by two.
DrawBishopFlag usage in Fruit

DrawBishopFlag usage in Rybka

if ((mat_info->flags & DrawBishopFlag) !


if (flags & DrawBishopFlag) {
= 0) {
mask = Board.pieces[BB] |
wb = board->piece[White][1];
Board.pieces[WB];
bb = board->piece[Black][1];
if ((mask & MaskLightSquares) &&
if (SQUARE_COLOUR(wb) !=
(mask & MaskDarkSquares)) {
SQUARE_COLOUR(bb)) {
opening = opening / 2;
if (mul[White] == 16) mul[White] = 8;
endgame = endgame / 2;
if (mul[Black] == 16) mul[Black] = 8;
}
}
}
}
Lazy Evaluation
In addition to the flags discussed above, Rybka stores a boolean flag for
whether to perform lazy evaluation or not. Rybka has an extremely aggressive
lazy eval--if the material difference (not including the material table offset) is
beyond bounds set at the root based on previous iterations, the evaluation is
based only on material (this time including the material table offset). In addition
to these cases, there are a set of material configurations for which lazy
evaluation (material only) is performed unconditionally. For instance, in a KRR vs
KQN ending, Rybka does absolutely no evaluation beyond material--it simply
returns a constant value, regardless of previous search values or the position of
pieces. The pattern of material configurations which have this flag set is not very
clear. There are 1106 such configurations (though due to symmetry there are
only 553 unique ones). Each of these configurations also has in common that
they are not equal (the material is imbalanced), but the difference in material
value is not very large (the only configurations with more than 4 pawns difference
are KNN vs K and KNN vs KP). Beyond that, though, it's not very clear. Perhaps
these configurations were harvested from a collection of games and found to

have some property. There are certainly too many configurations, including very
obscure ones (such as KQRBPPPPP vs KQBBNN), for this to have been done by
hand.
However, there is a very serious bug in Rybka with regards to lazy evaluation.
The upper and lower bounds are set to the root score at the end of every iteration
that is at least 6 plies. However, Rybka deals with two different scales of
evaluation: units of a centipawn and units of 1/32 of a centipawn. In this case, the
two values are mixed up: Rybka's search value is in centipawns, but it sets the
lazy eval as if this value were in 1/32 centipawn units. Thus, every evaluation
(that happens to be less than 32 pawns in either direction, i.e. always) will cause
the lazy evaluation bounds to be set based on a score of 0. This means that if the
root score (before dividing by 3399) is >0, the bounds are set to -3 and 4, and if
the score is <0, the bounds are set to -4 and 3. Every single position with a score
outside of these bounds is lazily evaluated, which means that once the score is in
this range, Rybka effectively switches to material-only evaluation.
Phase
One of the more unique aspects of the Fruit evaluation is that it calculates two
different scores, for opening and endgame, and interpolates between the two
based on the phase of the game (which is calculated from the material left on the
board). This was quite uncommon when Fruit first appeared (if it was used
elsewhere at all), though in the meantime many other engines have begun to use
this strategy. It is interesting that Rybka uses the same approach (with one
interesting modification), though it is not necessarily evidence of any
wrongdoing. Looking at the phase value that is used to interpolate between the
two values, however, it is very clear that Rybka has copied Fruit's values.
Both Fruit and Rybka store the phase value in the material table. Fruit's
formula is pretty simple: for the opening, a phase of 0 is used, and for the
endgame, 256. This is calculated by taking phase values for each piece (pawns
do not count, minors count for 1, rooks for 2, and queens 4). The total of these
values is subtracted from TotalPhase (which is 24). This is then expanded into the
0-256 range with a simple proportionality constant.
Phase in Fruit
static
static
static
static
static

const
const
const
const
const

int
int
int
int
int

Phase in Rybka
PawnPhase = 0;
KnightPhase = 1;
BishopPhase = 1;
RookPhase = 2;
QueenPhase = 4;

static
static
static
static
static

const
const
const
const
const

int
int
int
int
int

PawnPhase = 0;
KnightPhase = 1;
BishopPhase = 1;
RookPhase = 2;
QueenPhase = 4;

static const int TotalPhase = PawnPhase static const int TotalPhase = PawnPhase
* 16 +
* 16 +
KnightPhase * 4 + BishopPhase * 4 +
KnightPhase * 4 + BishopPhase * 4 +
RookPhase * 4 + QueenPhase * 2;
RookPhase * 4 + QueenPhase * 2;
...

...

phase = TotalPhase;

phase = TotalPhase;

phase -= wp * PawnPhase;
phase -= wn * KnightPhase;
phase -= wb * BishopPhase;

phase -= wp * PawnPhase;
phase -= wn * KnightPhase;
phase -= wb * BishopPhase;

phase -= wr * RookPhase;
phase -= wq * QueenPhase;
phase
phase
phase
phase
phase

-=
-=
-=
-=
-=

bp * PawnPhase;
bn * KnightPhase;
bb * BishopPhase;
br * RookPhase;
bq * QueenPhase;

phase -= wr * RookPhase;
phase -= wq * QueenPhase;
phase
phase
phase
phase
phase

-=
-=
-=
-=
-=

bp * PawnPhase;
bn * KnightPhase;
bb * BishopPhase;
br * RookPhase;
bq * QueenPhase;

if (phase < 0) phase = 0;


if (phase < 0) phase = 0;
phase = (phase * 256 + (TotalPhase / 2))
phase = (phase * 256 + (TotalPhase / 2)) / TotalPhase;
/ TotalPhase;
phase /= 4;
Rybka has the same formula as Fruit. There is one important difference though:
in order for the value to fit into the one-byte field in the material table (which has
a range of only 0-255, instead of 0-256), it is divided by 4, bringing the range
from 0 to 65. There is not much loss here, since the values are extrapolated from
only 25 different phase values. It is interesting to note, however, that only 25 of
the values are ever possible. Rybka could have simply stored the 0-25 phase
without extrapolating to a larger range. Since the phase is used to index a table
(see below), this means that there are 40*2 entries which are never accessed in
this table. In my opinion, this makes it clear that the original code wasn't
understood fully.
In Rybka, the final interpolation between opening and endgame scores is done
using a table, phase_value[65][2]. The opening value is multiplied by
phase_value[phase][0], the endgame value is multiplied by phase_value[phase]
[1], and these are added together. This is then divided by 256*32--the sum of
each phase_value for opening and endgame is around 256, and Rybka evaluates
with a base of 32 units per centipawn (with the pawn actually worth 3399, about
106 centipawns). Each of these values (256 and 32) are confirmed by looking at
other places in the eval: when setting the lazy eval, Rybka multiplies by 256 and
divides by the sum of the two phase values. When returning the lazy eval, it
takes the material difference multiplied by 3399, adds the material table offset,
and divides by 32.
The phase_value table has values which are not quite simple, but when divided
into three sections of phases (0-12, 13-51, 52-64), the values can be quite closely
described by quadratic equations. This gives six total equations.

++++++++++++++++++++
This page discusses some general philosophy behind the evaluation function.
Instead of discussing the specific aspects of implementation, we discuss general
design decisions of the overall evaluation.

General

The evaluation is typically a collection of "rules of thumb" collected from


hundreds of years of human experience. It would be practically impossible to
program all of the rules that humans have come up with over the years. Even if
you could code for all of them, it would be inadvisable because of performance
reasons. There must be a trade-off between knowledge versus speed. The more
time you spend evaluating a position, the less time you have to search, and
therefore, the less deep your program can see. Some programs are designed with
a very light evaluation function containing only the most basic features, letting
the search make up the rest. Others prefer a heavy evaluation with as much
knowledge as possible. Most programs have an evaluation somewhere in the
middle, with the trend in recent years being towards simpler, lighter evaluation
functions, the idea being that these evaluations are the most bug-free and
maintainable, which is far more important in practice than having many obscure
pieces of knowledge. A big influence in this direction was the advent of Fruit,
which has a very minimal evaluation function, yet it is a very solid, strong engine.
Tord Romstad writes about his evaluation philosophy [1]:
You need patience, restraint, thorough testing, and a sound basic philosophy to
succeed. The following are, in my opinion, the most important properties of a
good evaluation function:

Orthogonality. When it is possible, it is better to avoid having two different


evaluation components which to some extent quantify the same extent of
the position. When adding a new evaluation component which has a nonzero "orthogonal projection" (in a methaphorical sense, of course) on a
previously existing component, try to adjust the two components in such a
way that the projection is minimized, or to generalize and combine the two
components into one.

Continuity. If two positions X and Y which are "close to each other" in the
sense that it is possible to get from position X to position Y by a short
sequence of good moves, the two positions should ideally have similar
evaluations. As a corollary, when one adds a big bonus or penalty for some
particular pattern, one should also consider introducing a smaller bonus or
penalty for getting close to this pattern. For instance, when adding a big
bonus for a knight on an outpost square, it might be a good idea to add a
smaller bonus for a knight attacking an outpost square.

Sense of progress. It is much more important that the evaluation function


is able to accurately judge which of two very similar positions is better,
than that it is able to judge which of two totally different positions is better.
The evaluation function doesn't need to be able to answer questions like
whether a certain classical King's Indian middle game is better than an
endgame arising from a Richter-Rauzer Sicilian. What it needs is to be able
to decide is things like whether one side should try to exchange a bishop
for a knight, or whether it is better to castle kingside or queenside.

Good worst case behavior. It is better to be wrong by 10 centipawns all the


time than to be completely correct 99.9% of the time and wrong by 300
centipawns 0.1% of the time.

References

1. ^ The Art of Evaluation by Tord Romstad, CCC, August 2, 2007

A "study" by Sune Fischer and Pradu Kannan in Dec. 2007 suggested approximate
relations between win percentage, pawn advantage, and ELO rating advantage
for computer chess. It was found that the the approximate relationship between
the winning probability W and the the pawn advantage P is

The inverse relationship can be given as

From the above, the relationship between the equivalent ELO rating advantage R
and the pawn advantage P can be given as

Data Acquisition
Data was taken from a collection of 405,460 computer games in PGN format.
Whenever exactly 5 plys in a game had gone by without captures, the game
result was accumulated twice in a table indexed by the material configuration.
The data was accumulated twice because it was assumed that material values
were equal for both colors. So if there was data for a KPK material configuration,
the data was also tallied for the KKP. Only data pertaining to the material
configuration was taken. This was considered reasonable because the material
configuration is the most important quantity that affects the result of a game.

Data Reduction and Modeling

For each material configuration, a pawn value was computed using conventional
pawn-normalized material ratios that are close to those used in strong chess
programs (P=1, N=4, B=4.1, R=6, Q=12). The relationship between Win
Percentage and Pawn Advantage was assumed to follow a logistic model; namely,

where K is an unknown non-zero constant. When applying the condition that the
win probability is 0.5 if there is no pawn advantage, the solution to the above
seperable differential equation becomes

For K=4, the proposed logistic model and the data is plotted here for comparison:

See

Other Material Considerations


In chess, it is known that certain material features provide an advantage, such as
the bishop pair (which might be worth as much as half a pawn). A program might
increase the values of rooks when there are less pawns on the board or prefer
knights when there are many pawns. Larry Kaufman performed statistical tests
on a variety of different material configurations to approximate their values [7] .
Other factors that affect material evaluation might be:
Bonus for the bishop pair (bishops complement each other, controlling squares
of different color)
Penalty for the rook pair (Larry Kaufman called it "principle of redundancy")
Penalty for the knight pair (as two knights are less successful against the rook
than any other pair of minor pieces)
decreasing the value of the rook pawns and increasing the value of the central
pawns (though this can be done in the piece-square tables as well)
Trade down bonus that encourages the winning side to trade pieces but no
pawns
Penalty for having no pawns, as it makes it more difficult to win the endgame

Bad trade penalty as proposed by Robert Hyatt, that is penalizing the material
imbalances that are disadvantageous like having three pawns for a piece or a
rook for two minors.

Insufficient

Material
Using values like these blindly can lead to bad play. Most programs uses special
code or tables to detect drawn or likely drawn material combinations. For
example, KB vs K is a draw, as is KN vs K and KNN vs K. There is also a class of
almost-certain draws, not mentioned in the FIDE rules because of the possibility
of a helpmate (KB vs KN, KNN vs KB, KBN vs KB, KBN vs KR etc.) A general rule
that, although not perfect, catches many likely draws is that if one side has no
pawns left, it needs the equivalent of +4 pawns more material to win. For
example, KRN vsv KR is usually a draw, where KRR vs KBN is usually a win for the
rook side. For more details see draw evaluation and interior node recognizer.
++++++++++++++++++
his page serves as a quick reference for those who look for ideas about
evaluating different kinds of pieces.

Pawn

Pawn Structure
Pawn Center
penalty for "d" and "e" pawns blocked on their initial squares

Knight

decreasing value as pawns disappear


Outposts
knight trapped on A8/H8/A7/H7 or A1/H1/A2/H2 - see Trapped Pieces
penalty for blocking a "c" pawn in closed openings (Crafty defines it as
follows: white knight on c3, white pawns on c2 and d4, no white pawn on
e4)
when calculating knight mobility, it is advisable to omit squares controlled
by enemy pawns

Bishop
Bishop pair
Bad Bishop
Color Weakness
Fianchetto
Returning Bishop
bishop trapped by enemy pawns on A2/H2/A7/H7 or on A3/H3/A6/H6, see
Trapped Pieces

Rook

increasing value as pawns disappear

Rook on open file


Rook on seventh (possibly also eigth) rank
Tarrasch Rule (Rook behind Passed Pawn)
penalty for a Rook blocked by an uncastled King

Queen

Penalty for early development


some programs do not evaluate queen mobility
if king tropism is evaluated, queen tends to get a higher bonus

King

King Safety in the middlegame


Mate at a Glance
Centralization in the endgame
Penalty for standing on a wing with no pawns present in the endgame
Pins/X-ray
Castling Rights

Mobility is a measure of the number of choices (legal moves) a player has in a


given position. It is often used as a term in the evaluation function of chess
programs. It is based on the idea that the more choices you have at your
disposal, the stronger your position. A study by Eliot Slater of 350 tournament
games in which the material balance was still even after the 20th move showed a
definite correlation between a player's mobility and the number of games won [1] .
Calculating Mobility
In computer programs, mobility is sometimes calculated differently than simply
by summing up the number of legal or pseudo-legal moves. Often, it is done
piece-by-piece, and the mobility bonus per possible move is not always the same
for each type of piece (e.g., in the opening, the mobility of the bishops and
knights is more important than that of the rooks). Sometimes forward mobility is
scored higher than backward mobility, sometimes (in case of rooks) vertical
mobility gets priority over horizontal mobility. Also, if a piece can move to the
square of another friendly piece, sometimes that move is also counted - although
it would not be a legal move, it is protecting the friendly piece, and therefore still
serves a useful role.
A couple of programs evaluates so-called safe mobility - counting only squares
where a piece can move without being En prise. This might be quite expensive,
unless a program already keeps incrementally updated attack tables. In some
cases, most notably in case of a knight, a middle-of-the-ground approach, not
counting squares controlled by enemy pawns, seems best.

The Value of Reaching a Square


Dan Heisman [4] represents an attempt at mathematical abstraction applied to
chess, introducing seven concepts as fundamental in analyzing a chess position:
mobility, flexibility, vulnerability, center control, piece coordination, time and
speed. Heisman applies two dichotomies: actual versus potential and local versus
global:

actu
al

pote
ntial

local
Single
moves
from this
position
Single
moves
on an
empty
board

global
All reachable
squares
from this
position
All reachable
squares
on an empty
board

Distance as generalization of mobility and unification of Heisman's notions was


introduced by Robert Levinson and Richard Snyder in the famous 1993 ICCA
Journal Vol. 16, No. 3 [5] . Abstract and excerpt:
This article suggests a new approach to computer chess. A graph-theoretic
representation of chess knowledge, uniformly based on a single mathematical
abstraction, DISTANCE, is described. Most of the traditional forms of chess
knowledge, it is shown, can be formalized in this new representation. In addition
to comparing this approach to others, the article gives some experimental results
and suggests how the new representation may be unified with existing
approaches.
The DISTANCE idea is based on exploring a piece's mobility graph to determine
what is close to and what is close to it. From a DISTANCE standpoint, moves on
the chess-board are only considered good if they result in improved movement
graphs for the mover's pieces and/or inferior ones for the opponent's pieces.
Often, a good chess-player will move a piece, not to improve the attacking
chances of that piece but rather the attacking chances of the piece behind it.

Mobility with Bitboards


For programs using Bitboards, piece mobility can be calculated very quickly
either by Population Count or a SIMD-wise kind of weighted population count.
Similar to Attacks by Occupancy Lookup to determine attack sets of sliding
pieces, one may use pre-calculated population count or even center-weighted
population count as a rough estimate on piece mobility. However it does not
consider subsets of let say safe target squares. Most strong chess programs use a
mobility calculation as part of the positional evaluation in some way. This

approach is taken to the extremes in case of OliThink - a chess engine whose


evaluation consists entirely of material balance and mobility.

Progressive Mobility
Fill approaches, like Dumb7Fill or Kogge-Stone algorithm are great to determine
target sets one may reach in two or more moves, which population or weighted
population might be considered as progressive mobility in some kind of positions
[6]
. Another application in late endings is to determine whether a piece may
attack a decisive stop or telestop of a passed pawn in time. Path finding
algorithms for various pieces may be applied to find so called Trajectories [7][8].
Trapped pieces are the extreme examples of poor mobility. They are awkwardly
placed and often endangered. A typical example of a trapped piece is a white
Bishop on h7 blocked by the black pawns on f7 and g6. Most of the time it gets
eventually captured, but it takes so much time that can be easily pushed over the
horizon. Most of the time the best thing such a bishop can do is to sacrifice itself
for a (second) pawn, so its value should be decreased by about 150.
Another examples include:

a white knight on h8 in presence of a black pawn either on h7 or on f7


a white bishop on h6 with black pawns on g5 and f6 (this one should be
scored less severely, perhaps -50)
a white knight on h7 with black pawns on g7 and h6
a white rook on h1/g1/h2/g2 with a white king on f1 or g1 (perhaps -40, to
prevent pseudo-castling with a rook hemmed in)

Good evaluation of the king safety is one of the most challenging tasks in
writing an evaluation function, but also the most rewarding. The subjects are
placed here in order of approximate (implementation) difficulty. If this page
grows, it might be worthwhile to create a sub-page for each term.
Pawn shield
When the king has castled, it is important to preserve pawns next to it, in order
to protect it against the assault. Generally speaking, it is best to keep the pawns
unmoved or possibly moved up one square. The lack of a shielding pawn
deserves a penalty, even more so if there is an open file next to the king.

Pawn storm
If the enemy pawns are near to the king, there might be a threat of opening a
file, even if the pawn shield is intact.

King tropism
King tropism is a simplified form of king safety evaluation. It takes into account
the distance between the King and the attacking pieces, possibly weighted
against piece value. For example, one may double the distance value for a
queen, and halve it for bishops and rooks. This kind of evaluation acts in a
probabilistic way - it is by no means certain that being close to the king helps in
attacking it. For example, if white castles short, black rook on h8 gets a higher
tropism value regardless whether it stands on an open file. Nevertheless, using
this kind of crude evaluation term increases a probability of building up an attack.
This kind of algorithm is used by Crafty. Another, perhaps more basic example is
the CPW-engine.

Scaling
Usually king safety value is scaled one way or the other. Even TSCP uses the
pawn shield and pawn storm score, scaled by the opponent's material. This way,
whenever the engine finds itself with a broken pawn shield, it tends to exchange
pieces in order to alleviate the danger. Fruit uses a more elaborate scheme,
counting the bonuses for attacking the squares near to the enemy king, and then
multiplying their sum by the constant derived from the number of attackers. For
an approximation of such approach, see CPW_King evaluation function.

Square control
The most elaborate king safety evaluation schemes gather information about
control of the squares near the enemy king. A good explanation of such an
algorithm might be found on Ed Schrder's Programmer Corner [2][3]. If a program,
unlike Rebel, does not keep incrementally updated attack tables, this knowledge
is likely to be uncovered while calculating mobility.

Patterns
There are positions that tend to be notoriously difficult for the chess programs.
One of them is a sacrifice of a minor piece on g5/g4, when it is simultaneously
attacked and protected by the "h" pawns. Another one occurs after a standard
Bxh7 sacrifice: White knight stands unchallenged on g5, white queen on h5, black
King on g8 (positions with Kg6 are best left to the search). Hard-coding such
patterns raises program's tactical awareness.
In chess, a tempo refers to a "turn" or single move. When a player achieves a
result in one fewer move, he gains a tempo and when he takes one more move
than necessary he loses a tempo.

Tarrasch's concept of force, space and time [1] and their equivalence (to some
extend) is considered by material (force), piece placement (space) and roughly
by mobility (space and time) inside the evaluation of a chess programm. Whether
gaining three tempos in a gambit opening is worth a pawn, is a matter of
considering piece development and king safety issues related to center pawn
structure (open/close, lever options) and castling abilities, f.i. estimating number
of moves to castle, and certain long term deficiencies. Also, some programs
better rely more or less on the opening book to play gambits well.
Some programs give a small bonus for having the right to move - the premise
being that it is usually advantageous to be able to do something, except in the
zugzwang positions. That bonus is useful mainly in the opening and middle game
positions, but can be counterproductive in the endgame.
The textbook division of game phases enumerates three of them:

Opening
Middlegame
Endgame

In the opening, the focus is on development, in the middlegame - on attack and


defence, in the endgame - on pawn promotion and realization of the material
advantage.
Whereas this definition is good enough for humans, coding it in a straightforward
manner would introduce evaluation discontinuity in many places. For that reason
many programs define several game phases, based on the amount of material on
the board.
The Opening is the first game phase from the initial starting position with the
aim to develop the minor pieces, to control the center and depending on the
center pawn structure, to prepare or achieve a safe square for the king, most
likely by castling.
Some programs apply special knowledge in their evaluation for this early game
stage, most notably to encourage all minor pieces to develop, to prepare castling
options, and to penalize too early queen development, which either hinders own
minor pieces, or to become target of opponent attacks with the consequence to
eventually lose some tempo.
The Middlegame is the game phase which happens after the opening, where the
game of chess literally starts after developing most minor pieces and to prepare
or already achieve castling. Quite often due to (double) pushes of the center
pawns, the basic pawn structure is often already determined during the first
moves, sometimes with center rams involved, already defining which bishop is
good or bad.
Opening versus Middlegame
The early middlegame does not necessarily imply a reduction of material by
trading pawns or pieces. On the other hand, early pawn trades or other captures
don't necessarily imply the beginning of middlegame as long as other aims of the
opening are not achieved. Tapered eval for a smooth transition between game
phases by multiple piece-square tables does usually not distinguish between
opening and early middlegame with all or most material still on the board.
Therefor some programmers apply special opening knowledge inside their

evaluation to avoid too early queen activity or moving developed pieces multiple
times while other minor pieces are still on their home square and have not moved
yet (considering returning bishop). Other programs purely rely on the opening
book and don't care to differentiate between these early phases.

Evaluation
Beside material, mobility and pawn structure considerations, king safety is the
most important evaluation term in the middlegame.
In chess, Strategy is related to setting up goals and long-term plans for future
play most importantly considering pawn structure and king safety. While tactics
is the domain of search, strategy is almost the domain of evaluation.
The concepts of strategy and tactics in chess and other sports are derived from
military origins as defined as a fourfold hierarchy of strategy, operational
objective, tactic and task
Tactics in chess is the coordinated task in realizing short-term advantage or
equilibration, yielding in mate attack, material gain, or forcing draw in inferior
positions. Tactical moves either change the material balance immediately, or in a
wider sense, threaten to win material and/or checkmate considering certain
tactical motives. Often tactics of several types are conjoined in a combination
including short-term sacrifices. While tactics is most often a domain of the
middlegame, it already appears in various opening lines, specially gambits, very
early in the game. During the endgame, technical or strategical play seems to
dominate, but there are many tactical motives like pursuits, stalemate,
zugzwang, pawns breakthrough and passed pawns to name a few, to be
considered there as well.
The concepts of strategy and tactics in chess and other sports are derived from
military origins as defined as a fourfold hierarchy of strategy, operational
objective, tactic and task.
Quotes
[3][4]

Richard Teichmann: Chess is 99 percent tactics.


Gerald Abrahams: The tactician knows what to do when there is something
to do; whereas the strategian knows what to do when there is nothing to
do.
Max Euwe: Strategy requires thought, tactics require observation.

Tactical Positions
Tactical positions usually involve tension and offer tactical motives and moves,
possibilities to attack and counter attack kings or valuable pieces, often one

tempo is decisive. Tactical positions require concrete calculation rather than


strategic long-range planning, and that is what makes most chess programs so
strong compared to human players in these kind of positions. On the other hand,
certain chess tactics, even if a domain of the computers, are still too complicated
and deep for chess programs to solve in a reasonable time frame, and require
hours or days of computer and/or human master analysis, to eventually discover
the "truth of the position".

Tactics in Chess Programs


Todays rather selective chess programs almost perform an exhaustive search of
immediate tactical moves, and even tend to extend them, or at least to don't
prune or reduce them, and further perform a quiescence search at horizon nodes.
If the iterative deepening search-routine of a chess program "suddenly"
recognizes a "big" score jump at the root, there is almost certainly chess tactics
involved.

Search Control
Search trees of highly tactical positions tend to be narrow and deep. Often,
replies are singular due to forced recaptures or check evasions, triggering for
instance check extensions, recapture extensions, singular extensions or one reply
extensions. The somehow complement set of non-tactical moves may be reduced
by null move pruning, multi-cut and late move reductions. Tactics is therefor
covered by search, even with rudimentary evaluation (better with some king
safety terms involved) - but positional evaluation and strategical knowledge is
necessary to drive the program into tactical positions, where it might out-search
the opponent due to its superior search. Anyway, some programs consider some
tactical motives in their evaluation or search control, which are usually not
subject of a quiescence search, to become aware before they occur two or more
plies behind the horizon, especially in PV-nodes and to avoid standing pat if the
opponent has some threats.

Evaluation
Some programs consider some tactical features in evaluation, i.e. penalties for
pieces en prise of either side [5]. One may also consider too many hanging pieces,
even if not actually attacked but the opponent has resources to perform double
attacks. Pinned pieces may subject of respective penalties in evaluation as well.

Terms and Motives

Annihilation of Defense
Battery
Blockade

Check
Checkmate
Combination
Decoying
Deflection
Demolition of Pawns
Discovered Attack
Discovered Check
Double Attack
Double Check
En prise
Gambit
Hanging Piece
Interception
Interference
Overloading
Pawns Breakthrough
Passed Pawn
Pin
Promotions
Pursuit (perpetual attack)
Sacrifice
Skewer
Space Clearance
Stalemate
Tactical Moves
Triangulation
Undermining
X-ray
Zugzwang
Zwischenzug

Related

Checks and Pinned Pieces (Bitboards)


Chunks
Intersection Squares
Knight Forks (Bitboards)
Mate at a Glance
Mate Search
Material
Pattern Recognition
Quiescence Search
Tempo
X-ray Attacks (Bitboards)

See also

Anti-Computerchess
Evaluation
Psychology
Search
Strategy

In the Endgame chess programs usually have quite a lot of difficulties. Even the
most simple endgames often just lead to a mate after 10 to 15 plys or more,
which is far beyond the horizon for engines without the specific endgame
knowledge. There are some concepts that a chess programmer should implement
to overcome the most basic problems. Usually chess engines activate this special
Endgame knowledge as soon as the material on board reaches a certain lowerbound.
Transposition Tables
Nowhere else are the Transposition Hash Tables more efficient than in Endgames.
They are invaluable.

Evaluation
When doing a positional evaluation, in the endgame, the chess engines should
change some parameters. So for example in the middlegame, if the opponent's
king is trapped in the center, it should be evaluated much better for the program
than the opponent's king, safely standing at the border. In the endgame
however, its the other way round. A king on the edge or even in the corner has
not so many squares to escape to and is more beneficial for the other player.
Furthermore, if you only have one bishop, it might be good to evaluate the
opponent's king being forced to go to the corner with the color of the bishop a
higher bonus, than for the other corner. In addition Pawn promotion is a very
important aim in most endgames. The chess engines should consider that by
evaluating the strength of passed pawns.

Special Knowledge
Some endgames are extensively covered by theory, and for that reason one can
supply a vast number of heuristics for playing them. Typical examples include:

Draw evaluation
Mop-up evaluation

Pawn Endgames

Pawn Endgame

o
o

Corresponding Squares
KPK database/rules

Piece Endgames

Bishops of Same Color endgame


Bishops of Opposite Colors endgame
Wrong color Bishop and rook pawn
Bishop versus Knight
Mating in KNBK
Rook Endgame
Queen Endgame

Endgame Tablebases
see main article Endgame Tablebases.
Currently many engines support the usage of Endgame Tablebases, precalculated
databases that hold for every possible position in a certain endgame, whether it
is drawn or how many moves are left for a side to win/lose assuming perfect play.
With the help of those, chess engines can simply lookup, if it is advisable to do a
certain exchange or not, as well as how to finish some of the more tricky
endgames.
The advantage of Endgame Tablebases is the ability to determine the definite
outcome of a certain position, but on the other side, Tablebases are very space
consuming and the disk-access tends to be slow.

Quotes
[2][3]

Gerald Abrahams: The tactician knows what to do when there is something


to do; whereas the strategian knows what to do when there is nothing to
do.
Mikhail Chigorin: Even a poor plan is better than no plan at all.
Max Euwe: Strategy requires thought, tactics require observation.
Emanuel Lasker: A bad plan is better than no plan at all. [4]
Aron Nimzowitsch: First restrain, next blockade, lastly destroy.

Steinitz's Four Rules of Strategy

Wilhelm Steinitz, the first undisputed world chess champion from 1886 to 1894,
was a main chess correspondent to present his ideas about chess strategy [5]:
1. The right to attack belongs to the side that has a positional advantage,
which not only has the right to attack, but the obligation to do so, else the
advantage will evaporate. The attack should be concentrated on the
weakest square in the opponent's position.
2. If in an inferior position, the defender should be ready to defend and make
compromises, or take other measures, such as a desperate counterattack.
3. In an equal position, the opponents should maneuver, trying to achieve a
position in which they have an advantage. If both sides play correctly, an
equal position will remain equal.
4. The advantage may be a big, indivisible one, or it may be a whole series of
small advantages. The goal of the stronger side is to store up the
advantages, and then to convert temporary advantages into permanent
ones.

Search Strategy
Search strategy refers to search techniques and algorithms:

Best-First Strategy
Brute-Force Strategy
Depth-First Strategy
Type A Strategy
Type B Strategy

Material

Balance
The Material Balance is finally returned as the almost most dominating evaluation
term, usually in Negamax from side to move's point of view, and in its pure form
simply the difference of both sides material, MD:
md := material[side_2_move] - material[side_2_move ^ 1];As mentioned, other
material considerations, concerning insufficient material and material imbalances
(e.g. rook versus two minor pieces, queen versus two rooks or three pieces, three
pawns versus piece) should be taken into account - for instance to encourage
exchanging pieces but no pawns if ahead, see the hashing approach of Chess 4.5.
Standard Terms
Single Pawn

Passed Pawn
Candidate Passed Pawn
Faker
Hidden Passed Pawn
Doubled Pawn

Isolated Pawn
Backward Pawn
Sentry

Groups of Pawns

Hanging Pawns
Pawn Islands
Pawn Chain
Pawn Majority
Minority Attack

General

Weak Pawns
Holes
Pawn Center
Pawn Race

Evaluation terms dependent on pawn structure

Bad Bishop
Color Weakness

Hashing Pawns

Pawn Hash Table

See also

CPW-Engine_eval
Pawn Pattern and Properties pawn structure aspects concerning bitboards
Pawn Structure Lessons Video Chess Lessons by Roman Dzindzichashvili
Strategy

Candidate passed pawn is a pawn on a half-open file, which, if the board had

only pawns on it, would eventually become a passed pawn by moving forward.
Whereas this definition is obvious for a human, in a form presented above it
would require no less than a separate recursive search routine. For that reason,
computers have to use approximations of that rule.
One possibility is to define a pawn as a candidate, if no square on its path is
controlled by more enemy pawns than own pawns. However, this simple
heuristics rules out an early recognition of a candidate passed pawns. For
example with white pawns on a4 and b5 and black pawn on a7, the b5 pawn
would be viewed as a candidate passer only after a4-a5.
A Faker is a "faked" candidate with more opponent sentries than own helpers.
The opponent sentries control the Stop square (or tele-stops) of the faker, while
the helpers defend the appropriate squares. If the number of helpers becomes
equal (for instance due to a pawn-capture) the faker becomes a real candidate.
A faker should not receive a candidate bonus, but is not that bad as a backward
pawn. Also two opponent sentries which control the same stop square, leave a
hole between both sentries, which might be considered elsewhere.

Faker c4, two sentries, but only one helper


The hidden passed pawn is an evaluation term used in Crafty, in the
terminology proposed by Hans Kmoch it is referred to as a sneaker.
Suppose that white has pawns on a6 and b5, and black on a7. At first glance we
have a backward pawn on b5, which is bad for white. However, after playing b5b6, white gets a passed pawn either there or on a6. For that reason the pawn on
a6 should be termed "a hidden passed pawn" and awarded a bonus.
A Pawn is doubled (or, sad to say, tripled) if there are more pawns of the same
color on a given file. Early programs and papers advocated evaluation it as half a
pawn, today the penalty uses to be less severe.
It might be argued that, given a perfect pawn structure evaluation, a term such
as doubled pawns would not be needed, since they usually introduce another
pawn structure weaknesses: some kind of backwardness, lack of a candidate
passer that otherwise would be there etc. However, most evaluation functions do
not go into such details as to justify removing this term.

Types of Doubled Pawns


Hans Berliner characterizes doubled pawns by their exchange potential against
opponent pawns on adjacent files [1][2]:

The doubled pawns on the b-file are the best


situation, the f-file pawns are next. The h-file
pawns are the worst situation because two pawns
are held back by one opposing pawn, so the
second pawn has little value.

8/1p6/p3pp1p/8/8/1PP2P1P/1P
3P1P/8 w -

Crippled Majority
A doubled pawn of a so called "crippled" majority devalue that majority. Hans
Berliner on following position in Some Innovations Introduced by Hitech [3]:

Our pawn-structure algorithm is quite simple,


detecting only isolated and multiple Pawns, and
the effect of multiple Pawns on viable pawn
majorities. For instance, in Diagram 1 the value of
White's doubled Pawn is negligible, while Black's is
almost full-valued. This distinction, an innovation,
was first introduced in Patsoc [4], and we were able
to adapt the code to make up tables to be loaded
into six identical hardware units computing pawn
structure in parallel.
8/1pp2ppp/1p6/8/8/5P2/PPP2P
PP/8 w - -

All About Doubled Pawns


The vast amount of detailed knowledge about doubled pawns, which rarely gets
implemented, can be found in the article All About Doubled Pawns [5] by Larry
Kaufman.
An isolated pawn is a pawn with no pawns of the same color on the neighboring
files. It is usually evaluated just like a backward pawn, except the penalty tends
to be bigger, as it is more open to attacks. It may be considered even weaker if
positioned on the half-open file, as then it is open to attack by enemy rooks.
Many programs tend to evaluate an isolated pawn in the center as weaker than
on the wing, as it can be attacked from more directions.

Examples
There is a whole class of openings characterized by the presence of an isolated
queen's pawn. In exchange for this structural weakness the side with an IQP gets
greater mobility, therefore this kind of positions can be used for balancing the
evaluation functions parameters.
A backward pawn is a pawn no longer defensible by own pawns and whose stop
square is controlled by an opponent pawn or even by two pawns, the so called
sentries. Thus, don't considering piece tactics, the backward pawn is not able to
push forward without being lost, even establishing an opponent passer. If two
opposing pawns on adjacent files in knight distance are mutually backward, the
more advanced is not considered backward.
A backward pawn is likely worse, if on a half-open file as suitable target of
opponent rooks. As pointed out by Sam Hamilton [1], considering stop squares
might be insufficient for pawns which may actually push, but have a permanent
weakened telestop. On the other hand, such a backward prospective pawn has a
vital tempo, which is often decisive in certain pawn endings.
In the terminology used by Hans Kmoch, a Sentry is a pawn controlling the
square lying on the path or front span of an opponent's pawn, thereby preventing
it from becoming a passed pawn.
Hanging pawns are an open, half-isolated duo. It means that they are standing
next to each other on the adjacent half-open files, usually on the fourth rank,
mutually protecting their stop squares. They share a number of characteristics of
weak pawns - they are not directly protected by other pawns and may become
targets of attack - but unlike them they have dynamic potential of executing a
pawn breakthrough and thus creating a passed pawn (admittedly being an
isolated pawn at the same time). The side with hanging pawns also tends to
enjoy a space advantage.
The Hanging Pawns formation:

8/pp3ppp/4p3/8/2PP4/8/P4
PPP/8 w - Most of the programs do not have a special evaluation of hanging pawns, relying

on search to decide if they are an asset or a liability in a given position.


The term "hanging" in chess otherwise refers to undefended pieces, who are En
prise if attacked!
Pawn islands of either side are groups of pawns (at least one), separated by
files without own pawns. Those files are therefore either halfopen or open. All
other things being equal, the side with fewer pawn islands has an advantage,
because the individual pawns are easier to defend against enemy attacks. Each
island has a west- or east border file. Rook-files may be the most outer borders.
Pawn chain consists of pawns placed on the same diagonal, without
interruption, so that only one of them is undefended. That pawn is called a base
of pawn chain. The player opposing the pawn chain should attack its base or else
strive to exchange pawns being parts of the chain. The old chess theory warned
against attacking the head of the chain, but some lines of the French defense
show that this strategy is perfectly applicable.

Pawn majority occurs when one side has more pawns than the other in a given
sector of the board. If the pawn majority is not compromised, i.e. if it does not
contain doubled or backward pawns, it should eventually create a passed pawn
by moving forward.
A general strategic principle tells us that a player ought to attack on the wing
where he possesses a pawn majority. An important exception to this rule is a
minority attack.

Minority attack refers to a method of play typical in a positions where white has
pawns on a2, b2 and d4, whereas black has pawns on a7, b7, c6 and d5. Despite
the fact that black nominally has a pawn majority on the queenside, white
attacks by moving the b-pawn forward and organizing a piece pressure along the
c-file. Then either white plays b5xc6, isolating black c-pawn, or black plays
c6xb5, opening the c-file and weakening d-pawn.

Weak pawns are pawns not defended and not defensible by the pawns of the
same color, whose stop square is also not covered by a friendly pawn. Some
programs, most notably Crafty, do not differentiate between various classes of
weak pawns, but most of the time their various kinds are evaluated differently.

Classes of weak pawns

Isolated Pawn
Backward Pawn
Overly advanced pawn (technically neither isolated nor backward, but
nevertheless not defensible by a pawn. Consider the following situation:

white pawns on a3, b5 and c4, black pawns on a4, b6 and c7, where the
black pawn on a4 falls into this category.)

1k6/2p5/1p6/1P6/p1P5/P7/3K4
/8 w - Some also add hanging pawns, but they are not as endangered and have a lot
more attacking potential than any of the above.

Holes are the squares that cannot be possibly covered by own pawns, usually
also controlled by the enemy pawns. A typical example is d4 square after White
forms a triangle consisting of pawns on c4, d3 and e4. Holes are excellent
locations for enemy minor pieces, safer than outposts. For that reason one should
keep minor pieces to cover the holes in one's position.
hess theory holds that it is advantageous to control center with pawns. This
principle has been stressed especially in the classical period of chess theory to
the extent that some perfectly healthy openings were considered
disadvantageous on the grounds that they "give up the center" like the
Rubinstein line in the French. Many of the hypermodern openings adopt the
opposite attitude, allowing the opponent to build the pawn center in order to
attack it later on. It follows that the evaluation function, in order to remain
flexible, should not assign too great weights to pawn center - else it would be
unable to evaluate this kind of position correctly.

Terms
related to Pawn Center by Hans Kmoch [1] [2]
Center lever - A lever wholly within the two center files
Center pawn - Pawn on the d- or e-file
Centerswap - A capture from and to the d- or e-file that produces a doubled
pawn
Innerswap - A capture towards the center that produces a doubled pawn

Coding
Coding pawn center evaluation routine, one can
1. use values in a piece-square table for pawns
2. raise the material value of central pawns or
3. assign bonuses for certain pawn configurations, like the following:
o ideal formation of white pawns on d4 and e4
o good formation of white pawns on d4 and c4
o e4 or d4 pawn defended by another one
Some other evaluation factors are interrelated with the pawn center, like the
penalty for a knight on c3 blocking c2 pawn in the closed openings.
Pawn race is a condition where, in a pawn endgame, both sides have likely
unstoppable passed pawns. To asses such positions correctly, program should
take several factors into account:

Distance to the promotion square


Do promoted queens give check?
Are front spans obstructed by own king?
Can the opponent king use the rule of the square to its advantage?

A good example of pawn race code can be found in the sources of Crafty.

Bad bishop is a bishop whose Mobility is restricted by own pawns. As opposed to


normal mobility problems, this situation is semi-permanent (more so if the
position is blocked), which fact justifies singling it out as another evaluation term.
Bad bishop is not such a liability if it is placed outside the pawn chain (Bf5 in the
Slav). For that reason some programs, like Crafty, score a bishop as bad only if its
forward mobility is impaired.
For a bitboard-based program a feasible approach would be to create an array of
64 bitboards, indexed by a Bishop's position, and marking the pawn positions
which would make our bishop bad.

Color weakness denotes a situation in which one of the players does not have
sufficient control over the squares of a certain color. This is usually caused by the
lack of a bishop of that color, coupled with the unfortunate pawn structure. In
such situations, the opponent may use squares of that color to penetrate the
opponent's position. Hans Kmoch defines insufficient occupation of the dark
respectively light squares by pawns and bishop as black poverty or Melanpenie
respectively white poverty or Leukopenie [1].

Examples
Typical examples of a color weakness are:

positions after the exchange of a fianchettoed bishop (this is especially


painful when it was a defender of the castled king)
black exchanging a dark squared bishop in the French, the Slav or the
Caro-Kann defences
white exchanging a light-squared bishop in the Queen's Gambit Declined or
in the Colle
white exchanging a dark-squared bishop in the King's Indian, most notably
in Smisch

You might also like