Skip to content

Commit c4cb549

Browse files
committed
Add passed pawn evaluation
1 parent 6e215e0 commit c4cb549

File tree

11 files changed

+261
-52
lines changed

11 files changed

+261
-52
lines changed

.goreleaser.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,8 @@ archives:
1414
linux: linux
1515
windows: windows
1616
386: i386
17-
amd64: x86_64
1817
format: binary
19-
name_template: "{{ tolower .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
18+
name_template: "{{ tolower .ProjectName }}_{{ .Os }}_{{ .Arch }}"
2019
checksum:
2120
name_template: 'checksums.txt'
2221
snapshot:

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ BUILD=`date +%FT%T%z`
33
COMMIT=`git rev-list -1 HEAD`
44
BINARY=axwchessbot
55

6-
LDFLAGS=-ldflags "-w -s -X main.engineVersion=${VERSION} -X main.buildDate=${BUILD} -X main.gitCommit=${COMMIT}"
6+
LDFLAGS=-ldflags "-w -s -X main.version=${VERSION} -X main.date=${BUILD} -X main.commit=${COMMIT}"
77

88
build:
99
echo "Building for linux and windows"

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ implemented.
3737
* Tempo evaluation
3838
* Pair bonus
3939
* Rook on (half-)open files bonus
40+
* Passed pawn bonus
4041
* Simple search algorithm using
4142
* Negamax with alpha-beta-pruning
4243
* Quiescence search
@@ -49,7 +50,6 @@ implemented.
4950
* Evaluation improvements
5051
* Pawnshield bonus
5152
* Blocked piece penalty
52-
* Passed pawn bonus
5353
* Search improvements
5454
* Killer move heuristics
5555
* Multiprocessing (Lazy SMP?)

evaluation/evaluation.go

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ func calculateEvaluationPart(g *game.Game, color game.PlayerColor) EvaluationPar
103103
PairModifier: calculatePairModifier(g, color),
104104
TempoModifier: calculateTempoModifier(g, color),
105105
RookFileModifier: calculateRookModifier(g, color),
106+
PassedPawnModifier: calculatePassedPawns(g, color),
106107
}
107108
return evalPart
108109
}
@@ -215,15 +216,29 @@ func calculateRookModifier(g *game.Game, color game.PlayerColor) (result int) {
215216
return rooksOnOpenFiles*weights[color].AdditionalModifier.OpenRookModifier + rooksOnHalfOpenFiles*weights[color].AdditionalModifier.HalfRookModifier
216217
}
217218

218-
func calculatePawnFileFill(pawnBitboard uint64) uint64 {
219-
// Northfill
220-
pawnBitboard |= (pawnBitboard << 8)
221-
pawnBitboard |= (pawnBitboard << 16)
222-
pawnBitboard |= (pawnBitboard << 32)
223-
// Southfill
224-
pawnBitboard |= (pawnBitboard >> 8)
225-
pawnBitboard |= (pawnBitboard >> 16)
226-
pawnBitboard |= (pawnBitboard >> 32)
227-
228-
return pawnBitboard
219+
func calculatePassedPawns(g *game.Game, color game.PlayerColor) (result int) {
220+
if color == game.White {
221+
frontSpansBlack := calculatePawnSouthFill(g.Position.Black.Pawns) & ^g.Position.Black.Pawns
222+
attackingSpansBlack := frontSpansBlack
223+
attackingSpansBlack |= (frontSpansBlack << 1) & ^bitboardFileA //shift everything east and care for wraps
224+
attackingSpansBlack |= (frontSpansBlack >> 1) & ^bitboardFileH //shift everything west and care for wraps
225+
whitePassedPawns := g.Position.White.Pawns & ^attackingSpansBlack
226+
for x := whitePassedPawns; x != 0; x &= x - 1 {
227+
square := bits.TrailingZeros64(x)
228+
result += weights[color].Midgame.PassedPawnModifier[square]
229+
}
230+
return
231+
}
232+
233+
//black
234+
frontSpansWhite := calculatePawnNorthFill(g.Position.White.Pawns) & ^g.Position.White.Pawns
235+
attackingSpansWhite := frontSpansWhite
236+
attackingSpansWhite |= (frontSpansWhite << 1) & ^bitboardFileA //shift everything east and care for wraps
237+
attackingSpansWhite |= (frontSpansWhite >> 1) & ^bitboardFileH //shift everything west and care for wraps
238+
blackPassedPawns := g.Position.Black.Pawns & ^attackingSpansWhite
239+
for x := blackPassedPawns; x != 0; x &= x - 1 {
240+
square := bits.TrailingZeros64(x)
241+
result += weights[color].Midgame.PassedPawnModifier[square]
242+
}
243+
return
229244
}

evaluation/evaluation_test.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,3 +217,91 @@ func TestCalculateEvaluation(t *testing.T) {
217217
})
218218
}
219219
}
220+
221+
func Test_calculatePairModifier(t *testing.T) {
222+
type args struct {
223+
g *game.Game
224+
color game.PlayerColor
225+
}
226+
tests := []struct {
227+
name string
228+
args args
229+
want int
230+
}{
231+
{"GameStart White", args{game.NewFromFen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"), game.White}, 6},
232+
{"GameStart Black", args{game.NewFromFen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"), game.Black}, 6},
233+
}
234+
for _, tt := range tests {
235+
t.Run(tt.name, func(t *testing.T) {
236+
if got := calculatePairModifier(tt.args.g, tt.args.color); got != tt.want {
237+
t.Errorf("calculatePairModifier() = %v, want %v", got, tt.want)
238+
}
239+
})
240+
}
241+
}
242+
243+
func Test_calculateTempoModifier(t *testing.T) {
244+
type args struct {
245+
g *game.Game
246+
color game.PlayerColor
247+
}
248+
tests := []struct {
249+
name string
250+
args args
251+
want int
252+
}{
253+
{"GameStart White", args{game.NewFromFen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"), game.White}, 10},
254+
{"GameStart Black", args{game.NewFromFen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"), game.Black}, 0},
255+
}
256+
for _, tt := range tests {
257+
t.Run(tt.name, func(t *testing.T) {
258+
if got := calculateTempoModifier(tt.args.g, tt.args.color); got != tt.want {
259+
t.Errorf("calculateTempoModifier() = %v, want %v", got, tt.want)
260+
}
261+
})
262+
}
263+
}
264+
265+
func Test_calculateRookModifier(t *testing.T) {
266+
type args struct {
267+
g *game.Game
268+
color game.PlayerColor
269+
}
270+
tests := []struct {
271+
name string
272+
args args
273+
want int
274+
}{
275+
{"GameStart White", args{game.NewFromFen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"), game.White}, 0},
276+
{"GameStart Black", args{game.NewFromFen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"), game.Black}, 0},
277+
}
278+
for _, tt := range tests {
279+
t.Run(tt.name, func(t *testing.T) {
280+
if got := calculateRookModifier(tt.args.g, tt.args.color); got != tt.want {
281+
t.Errorf("calculateRookModifier() = %v, want %v", got, tt.want)
282+
}
283+
})
284+
}
285+
}
286+
287+
func Test_calculatePassedPawns(t *testing.T) {
288+
type args struct {
289+
g *game.Game
290+
color game.PlayerColor
291+
}
292+
tests := []struct {
293+
name string
294+
args args
295+
want int
296+
}{
297+
{"GameStart White", args{game.NewFromFen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"), game.White}, 0},
298+
{"GameStart Black", args{game.NewFromFen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"), game.Black}, 0},
299+
}
300+
for _, tt := range tests {
301+
t.Run(tt.name, func(t *testing.T) {
302+
if got := calculatePassedPawns(tt.args.g, tt.args.color); got != tt.want {
303+
t.Errorf("calculatePassedPawns() = %v, want %v", got, tt.want)
304+
}
305+
})
306+
}
307+
}

evaluation/utils.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package evaluation
2+
3+
var (
4+
bitboardFileA uint64 = 0x0101010101010101
5+
bitboardFileH uint64 = 0x8080808080808080
6+
)
7+
8+
func calculatePawnFileFill(pawnBitboard uint64) uint64 {
9+
pawnBitboard |= calculatePawnNorthFill(pawnBitboard)
10+
pawnBitboard |= calculatePawnSouthFill(pawnBitboard)
11+
return pawnBitboard
12+
}
13+
14+
func calculatePawnNorthFill(pawnBitboard uint64) uint64 {
15+
pawnBitboard |= (pawnBitboard << 8)
16+
pawnBitboard |= (pawnBitboard << 16)
17+
pawnBitboard |= (pawnBitboard << 32)
18+
return pawnBitboard
19+
}
20+
21+
func calculatePawnSouthFill(pawnBitboard uint64) uint64 {
22+
pawnBitboard |= (pawnBitboard >> 8)
23+
pawnBitboard |= (pawnBitboard >> 16)
24+
pawnBitboard |= (pawnBitboard >> 32)
25+
return pawnBitboard
26+
}

evaluation/weights.go

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@ type AdditionalModifier struct {
1818
}
1919

2020
type GamephaseWeights struct {
21-
Material map[dragontoothmg.Piece]int
22-
PieceSquareTables map[dragontoothmg.Piece][64]int
21+
Material map[dragontoothmg.Piece]int
22+
PassedPawnModifier [64]int
23+
PieceSquareTables map[dragontoothmg.Piece][64]int
2324
}
2425

2526
type Weights struct {
@@ -49,6 +50,16 @@ var (
4950
dragontoothmg.Queen: 900,
5051
dragontoothmg.King: 0,
5152
},
53+
PassedPawnModifier: [64]int{
54+
0, 0, 0, 0, 0, 0, 0, 0,
55+
10, 10, 10, 10, 10, 10, 10, 10,
56+
20, 20, 20, 20, 20, 20, 20, 20,
57+
40, 40, 40, 40, 40, 40, 40, 40,
58+
60, 60, 60, 60, 60, 60, 60, 60,
59+
80, 80, 80, 80, 80, 80, 80, 80,
60+
100, 100, 100, 100, 100, 100, 100, 100,
61+
0, 0, 0, 0, 0, 0, 0, 0,
62+
},
5263
PieceSquareTables: map[dragontoothmg.Piece][64]int{
5364
dragontoothmg.Pawn: [64]int{
5465
0, 0, 0, 0, 0, 0, 0, 0,
@@ -116,11 +127,13 @@ var (
116127

117128
var (
118129
midgameWeights = GamephaseWeights{
119-
Material: weightsForAllPhases.Material,
120-
PieceSquareTables: weightsForAllPhases.PieceSquareTables,
130+
Material: weightsForAllPhases.Material,
131+
PassedPawnModifier: weightsForAllPhases.PassedPawnModifier,
132+
PieceSquareTables: weightsForAllPhases.PieceSquareTables,
121133
}
122134
endgameWeights = GamephaseWeights{
123-
Material: weightsForAllPhases.Material,
135+
Material: weightsForAllPhases.Material,
136+
PassedPawnModifier: weightsForAllPhases.PassedPawnModifier,
124137
PieceSquareTables: map[dragontoothmg.Piece][64]int{
125138
dragontoothmg.Pawn: weightsForAllPhases.PieceSquareTables[dragontoothmg.Pawn],
126139
dragontoothmg.Knight: weightsForAllPhases.PieceSquareTables[dragontoothmg.Knight],
@@ -160,7 +173,8 @@ var (
160173
},
161174
game.Black: Weights{
162175
Midgame: GamephaseWeights{
163-
Material: midgameWeights.Material,
176+
Material: midgameWeights.Material,
177+
PassedPawnModifier: flipPstArrayVertically(midgameWeights.PassedPawnModifier),
164178
PieceSquareTables: map[dragontoothmg.Piece][64]int{
165179
dragontoothmg.Pawn: flipPstArrayVertically(midgameWeights.PieceSquareTables[dragontoothmg.Pawn]),
166180
dragontoothmg.Knight: flipPstArrayVertically(midgameWeights.PieceSquareTables[dragontoothmg.Knight]),
@@ -171,7 +185,8 @@ var (
171185
},
172186
},
173187
Endgame: GamephaseWeights{
174-
Material: endgameWeights.Material,
188+
Material: endgameWeights.Material,
189+
PassedPawnModifier: flipPstArrayVertically(endgameWeights.PassedPawnModifier),
175190
PieceSquareTables: map[dragontoothmg.Piece][64]int{
176191
dragontoothmg.Pawn: flipPstArrayVertically(endgameWeights.PieceSquareTables[dragontoothmg.Pawn]),
177192
dragontoothmg.Knight: flipPstArrayVertically(endgameWeights.PieceSquareTables[dragontoothmg.Knight]),

search/search_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ func TestProblematicGames(t *testing.T) {
112112
"Test QuiescenceSearch on Errors concerning no available moves",
113113
fields{
114114
"e2e4 e7e5 g1f3 b8c6 f1b5 a7a6 b5c6 d7c6 e1g1 d8f6 d2d4 e5d4 c1g5 f6d6 f3d4 c6c5 d4f3 d6d1 f1d1 f7f6 g5f4 g7g5 f4c7 c8g4 b1d2 e8d7 f3e5 f6e5 c7e5 g4d1 a1d1 d7e7 e5h8 a8d8 h8c3 b7b5 c3a5 d8d4 a5c3 d4d6 b2b4 d6g6 b4c5 e7e8 d2b3 g5g4 d1d3 g6e6 f2f3 f8e7 h2h3 g4f3 g2f3 e6c6 d3d5 e7f6 c3f6 c6f6 b3d4 b5b4 c5c6 g8e7 c6c7 e7c8 d5d8 e8e7 d8c8 e7d7 c8b8 d7c7 b8b4 a6a5 b4b5 f6d6 c2c3 a5a4 b5a5",
115-
7,
115+
4,
116116
4,
117117
},
118118
},

search/transposition_table.go

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const (
1414
alphaBetaBoundUpper
1515
)
1616

17+
// 96 bits = 12 Bytes
1718
type transpositionTableEntry struct {
1819
lock int32
1920
move dragontoothmg.Move
@@ -23,23 +24,28 @@ type transpositionTableEntry struct {
2324
}
2425

2526
type TranspositionTable struct {
26-
maxSize int
27-
entries map[uint64]transpositionTableEntry
27+
maxSizeInEntries int
28+
entries map[uint64]transpositionTableEntry
2829
}
2930

30-
// 1 entry is 20 bytes
31-
func NewTranspositionTable(maxSize int) *TranspositionTable {
31+
// 1 entry is 12 bytes
32+
func NewTranspositionTable(maxSizeInBytes int) *TranspositionTable {
33+
maxSizeInEntries := maxSizeInBytes / 12
3234
return &TranspositionTable{
33-
maxSize: maxSize,
34-
entries: make(map[uint64]transpositionTableEntry, maxSize),
35+
maxSizeInEntries: maxSizeInEntries,
36+
entries: make(map[uint64]transpositionTableEntry, maxSizeInEntries),
3537
}
3638
}
3739

3840
func (tt *TranspositionTable) Empty() {
39-
tt.entries = make(map[uint64]transpositionTableEntry, tt.maxSize)
41+
tt.entries = make(map[uint64]transpositionTableEntry, tt.maxSizeInEntries)
4042
}
4143

4244
func (tt *TranspositionTable) InsertIfNeeded(hash uint64, move dragontoothmg.Move, score int, depth int, bound alphaBetaBound) {
45+
if len(tt.entries) >= tt.maxSizeInEntries {
46+
tt.Empty()
47+
}
48+
4349
entry, found := tt.entries[hash]
4450

4551
if !found {

uci/timemanager.go

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ func NewTimingInfo(messageParts []string) (timingInfo *UciTimingInfo) {
4848
return
4949
}
5050

51-
func (timingInfo *UciTimingInfo) calculateTimeoutContext(ctx context.Context, g *game.Game) (context.Context, func()) {
51+
func (timingInfo *UciTimingInfo) calculateTimeoutContext(ctx context.Context, g *game.Game, options []UciOption) (context.Context, func()) {
5252
if timingInfo.MovesToGo <= 0 && timingInfo.TimeWhite <= 0 && timingInfo.TimeBlack <= 0 {
5353
return context.WithCancel(ctx)
5454
}
@@ -62,7 +62,24 @@ func (timingInfo *UciTimingInfo) calculateTimeoutContext(ctx context.Context, g
6262
timeLeft, increment = time.Duration(timingInfo.TimeBlack)*time.Millisecond, time.Duration(timingInfo.IncrementBlack)*time.Millisecond
6363
}
6464

65-
timeLeft -= MoveOverhead
65+
moveOverhead := MoveOverhead
66+
maxTime := MaxTime
67+
for _, option := range options {
68+
if option.name == "Move Overhead" {
69+
optionsMO, err := strconv.Atoi(option.value)
70+
if err == nil {
71+
moveOverhead = time.Duration(optionsMO) * time.Millisecond
72+
}
73+
}
74+
if option.name == "Max Time" {
75+
optionsMT, err := strconv.Atoi(option.value)
76+
if err == nil {
77+
maxTime = time.Duration(optionsMT) * time.Second
78+
}
79+
}
80+
}
81+
82+
timeLeft -= moveOverhead
6683
if timeLeft <= 0 {
6784
timeLeft = 0
6885
}
@@ -73,8 +90,8 @@ func (timingInfo *UciTimingInfo) calculateTimeoutContext(ctx context.Context, g
7390
if limit > timeLeft-MinTimeLeft {
7491
limit = timeLeft - MinTimeLeft
7592
}
76-
if limit > MaxTime {
77-
limit = MaxTime
93+
if limit > maxTime {
94+
limit = maxTime
7895
}
7996

8097
return context.WithDeadline(ctx, timingInfo.StartTimestamp.Add(limit))

0 commit comments

Comments
 (0)