Skip to content

Commit 415ae2f

Browse files
committed
Add mobility evaluation
1 parent 5111048 commit 415ae2f

File tree

5 files changed

+79
-2
lines changed

5 files changed

+79
-2
lines changed

README.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ implemented.
3737
* Tempo evaluation
3838
* Pair bonus
3939
* Rook on (half-)open files bonus
40-
* Passed pawn bonus
40+
* Passed pawn evaluation
41+
* Mobility evaluation
4142
* Simple search algorithm using
4243
* Negamax with alpha-beta-pruning
4344
* Quiescence search
@@ -62,6 +63,7 @@ implemented.
6263
* [chess-ai](https://github.com/xtreemtg/Chess_AI)
6364
* [sunfish](https://github.com/thomasahle/sunfish)
6465
* [Adam Berent Blog](https://adamberent.com/2019/03/02/chess-board-evaluation/)
66+
* [Dragontooth](https://github.com/dylhunn/dragontooth)
6567
* [Dragontoothmg](https://github.com/dylhunn/dragontoothmg)
6668
* [CounterGo](https://github.com/ChizhovVadim/CounterGo)
6769
* Several blogposts/stackoverflow questions found via Google

evaluation/evaluation.go

+46
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ type EvaluationPart struct {
1818
BlockedPiecesModifier int
1919
KingShieldModifier int
2020
PassedPawnModifier int
21+
MobilityModifier int
2122
}
2223

2324
type Evaluation struct {
@@ -86,6 +87,8 @@ func (e *Evaluation) updateTotal() {
8687
e.TotalScore -= e.Black.KingShieldModifier
8788
e.TotalScore += e.White.PassedPawnModifier
8889
e.TotalScore -= e.Black.PassedPawnModifier
90+
e.TotalScore += e.White.MobilityModifier
91+
e.TotalScore -= e.Black.MobilityModifier
8992

9093
e.TotalScorePerspective = e.TotalScore
9194
if !e.Game.Position.Wtomove {
@@ -104,6 +107,7 @@ func calculateEvaluationPart(g *game.Game, color game.PlayerColor) EvaluationPar
104107
TempoModifier: calculateTempoModifier(g, color),
105108
RookFileModifier: calculateRookModifier(g, color),
106109
PassedPawnModifier: calculatePassedPawns(g, color),
110+
MobilityModifier: calculateMobilityModifier(g, color),
107111
}
108112
return evalPart
109113
}
@@ -160,6 +164,8 @@ func getBitboardByPieceType(bbs *dragontoothmg.Bitboards, pieceType dragontoothm
160164
func calculateMaterialScoreForPieceType(g *game.Game, color game.PlayerColor, pieceType dragontoothmg.Piece, bitboard uint64) (int, int, int) {
161165
var x uint64
162166
ps, pstMid, pstEnd := 0, 0, 0
167+
168+
// Thanks to https://github.com/dylhunn/dragontooth for the extract pieces from bitboard pattern
163169
for x = bitboard; x != 0; x &= x - 1 {
164170
square := bits.TrailingZeros64(x)
165171

@@ -242,3 +248,43 @@ func calculatePassedPawns(g *game.Game, color game.PlayerColor) (result int) {
242248
}
243249
return
244250
}
251+
252+
func calculateMobilityModifier(g *game.Game, color game.PlayerColor) (result int) {
253+
return calculateDiagonalMobilityModifier(g, color) + calculateLinearMobilityModifier(g, color)
254+
}
255+
256+
func calculateDiagonalMobilityModifier(g *game.Game, color game.PlayerColor) (result int) {
257+
diagonalBB := g.Position.White.Bishops | g.Position.White.Queens
258+
ownBB := g.Position.White.All
259+
if color == game.Black {
260+
diagonalBB = g.Position.Black.Bishops | g.Position.Black.Queens
261+
ownBB = g.Position.Black.All
262+
}
263+
allBB := g.Position.White.All | g.Position.Black.All
264+
265+
for x := diagonalBB; x != 0; x &= x - 1 {
266+
square := bits.TrailingZeros64(x)
267+
movableSquares := dragontoothmg.CalculateBishopMoveBitboard(uint8(square), allBB) & ^ownBB
268+
result += bits.OnesCount64(movableSquares) * weights[color].AdditionalModifier.DiagonalMobilityModifier
269+
}
270+
271+
return
272+
}
273+
274+
func calculateLinearMobilityModifier(g *game.Game, color game.PlayerColor) (result int) {
275+
linearBB := g.Position.White.Rooks | g.Position.White.Queens
276+
ownBB := g.Position.White.All
277+
if color == game.Black {
278+
linearBB = g.Position.Black.Rooks | g.Position.Black.Queens
279+
ownBB = g.Position.Black.All
280+
}
281+
allBB := g.Position.White.All | g.Position.Black.All
282+
283+
for x := linearBB; x != 0; x &= x - 1 {
284+
square := bits.TrailingZeros64(x)
285+
movableSquares := dragontoothmg.CalculateRookMoveBitboard(uint8(square), allBB) & ^ownBB
286+
result += bits.OnesCount64(movableSquares) * weights[color].AdditionalModifier.LinearMobilityModifier
287+
}
288+
289+
return
290+
}

evaluation/evaluation_test.go

+24
Original file line numberDiff line numberDiff line change
@@ -305,3 +305,27 @@ func Test_calculatePassedPawns(t *testing.T) {
305305
})
306306
}
307307
}
308+
309+
func Test_calculateMobilityModifier(t *testing.T) {
310+
type args struct {
311+
g *game.Game
312+
color game.PlayerColor
313+
}
314+
tests := []struct {
315+
name string
316+
args args
317+
want int
318+
}{
319+
{"GameStart White", args{game.NewFromFen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"), game.White}, 0},
320+
{"GameStart Black", args{game.NewFromFen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"), game.Black}, 0},
321+
{"OpenBishop White", args{game.NewFromFen("rnbqkbnr/pppppppp/8/8/6P1/8/PPPPPP1P/RNBQKBNR b KQkq - 0 1"), game.White}, 4},
322+
{"OpenRook Black", args{game.NewFromFen("rnbqkbnr/ppppppp1/8/7p/6P1/8/PPPPPP1P/RNBQKBNR w KQkq - 0 2"), game.Black}, 8},
323+
}
324+
for _, tt := range tests {
325+
t.Run(tt.name, func(t *testing.T) {
326+
if got := calculateMobilityModifier(tt.args.g, tt.args.color); got != tt.want {
327+
t.Errorf("calculateMobilityModifier() = %v, want %v", got, tt.want)
328+
}
329+
})
330+
}
331+
}

evaluation/weights.go

+4
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ type AdditionalModifier struct {
1515
KingShieldRank2Modifier int
1616
KingShieldRank3Modifier int
1717
RookBlockedByKingModifier int
18+
DiagonalMobilityModifier int
19+
LinearMobilityModifier int
1820
}
1921

2022
type GamephaseWeights struct {
@@ -40,6 +42,8 @@ var (
4042
KingShieldRank2Modifier: 10,
4143
KingShieldRank3Modifier: 5,
4244
RookBlockedByKingModifier: 24,
45+
DiagonalMobilityModifier: 2,
46+
LinearMobilityModifier: 4,
4347
}
4448
weightsForAllPhases = GamephaseWeights{
4549
Material: map[dragontoothmg.Piece]int{

search/search_test.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,9 @@ func TestSearch_SearchBestMove(t *testing.T) {
106106
{"Mate in 1 - White", fields{game.NewFromFen("4k3/pp3p1p/3Kp3/3P2r1/2r5/3q4/5PPP/4b2R b - - 5 29"), 3, 2}, []dragontoothmg.Move{getMove("c4c6")}, []dragontoothmg.Move{0}},
107107
{"Avoid Nullmove from Issue #2", fields{game.NewFromFen("1r4k1/2p4p/B5pP/P2p1p2/3PpP2/1nP1PnQP/q5K1/B1R5 w - - 1 40"), 3, 2}, []dragontoothmg.Move{}, []dragontoothmg.Move{0}},
108108
{"Mate in 1 from Issue #2", fields{game.NewFromFen("2rq1rk1/1Rp3pp/p2pN3/3Nn3/b3pb1P/2B3Q1/2PP1PP1/1R4K1 w - - 0 23"), 3, 2}, []dragontoothmg.Move{getMove("g3g7")}, []dragontoothmg.Move{0}},
109-
{"Avoid mate in 1 from https://lichess.org/9FeZycDP/black#65", fields{game.NewFromFen("1r3k1R/5p2/5p2/4rQ2/p3p3/n1b3P1/4qP1P/5RK1 b - - 4 33"), 6, 3}, []dragontoothmg.Move{}, []dragontoothmg.Move{0, getMove("f8g7")}},
109+
{"Avoid mate in 1 from https://lichess.org/9FeZycDP/black#65", fields{game.NewFromFen("1r3k1R/5p2/5p2/4rQ2/p3p3/n1b3P1/4qP1P/5RK1 b - - 4 33"), 3, 3}, []dragontoothmg.Move{}, []dragontoothmg.Move{0, getMove("f8g7")}},
110110
{"Avoid mate in 1 from #6", fields{game.NewFromFen("3r2k1/rp1n3p/2pb2p1/p1n3P1/2PBP3/P1N3q1/1PQ1B3/3R1R1K w - - 4 35"), 3, 3}, []dragontoothmg.Move{}, []dragontoothmg.Move{0, getMove("d4f2")}},
111+
{"Avoid queen loss in 2 from #8", fields{game.NewFromFen("r2r3k/3q3p/1RnN2pB/2P1pp1n/P7/6P1/Q3PP1P/6K1 b - - 0 28"), 5, 3}, []dragontoothmg.Move{getMove("a8a7")}, []dragontoothmg.Move{}},
111112
}
112113
logger := log.New(os.Stdout, "", log.LstdFlags)
113114
evaluator := evaluation.Evaluation{}

0 commit comments

Comments
 (0)