Professional Documents
Culture Documents
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.
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)
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
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.
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[6] == 0)
== 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.
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
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.
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 ((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];
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] &
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.
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.
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);
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);
=
=
=
=
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
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->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
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
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
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
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
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;
++++++++++++++++++++
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
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.
References
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
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.
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
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
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
Queen
King
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
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:
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
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]
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
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.
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
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
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]
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
Bad Bishop
Color Weakness
Hashing Pawns
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.
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]:
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
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.
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:
A good example of pawn race code can be found in the sources of Crafty.
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: