Skip to content

Commit

Permalink
Add passed pawn evaluation
Browse files Browse the repository at this point in the history
  • Loading branch information
ArcticXWolf committed Apr 17, 2021
1 parent 6e215e0 commit c4cb549
Show file tree
Hide file tree
Showing 11 changed files with 261 additions and 52 deletions.
3 changes: 1 addition & 2 deletions .goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,8 @@ archives:
linux: linux
windows: windows
386: i386
amd64: x86_64
format: binary
name_template: "{{ tolower .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
name_template: "{{ tolower .ProjectName }}_{{ .Os }}_{{ .Arch }}"
checksum:
name_template: 'checksums.txt'
snapshot:
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ BUILD=`date +%FT%T%z`
COMMIT=`git rev-list -1 HEAD`
BINARY=axwchessbot

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

build:
echo "Building for linux and windows"
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ implemented.
* Tempo evaluation
* Pair bonus
* Rook on (half-)open files bonus
* Passed pawn bonus
* Simple search algorithm using
* Negamax with alpha-beta-pruning
* Quiescence search
Expand All @@ -49,7 +50,6 @@ implemented.
* Evaluation improvements
* Pawnshield bonus
* Blocked piece penalty
* Passed pawn bonus
* Search improvements
* Killer move heuristics
* Multiprocessing (Lazy SMP?)
Expand Down
37 changes: 26 additions & 11 deletions evaluation/evaluation.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ func calculateEvaluationPart(g *game.Game, color game.PlayerColor) EvaluationPar
PairModifier: calculatePairModifier(g, color),
TempoModifier: calculateTempoModifier(g, color),
RookFileModifier: calculateRookModifier(g, color),
PassedPawnModifier: calculatePassedPawns(g, color),
}
return evalPart
}
Expand Down Expand Up @@ -215,15 +216,29 @@ func calculateRookModifier(g *game.Game, color game.PlayerColor) (result int) {
return rooksOnOpenFiles*weights[color].AdditionalModifier.OpenRookModifier + rooksOnHalfOpenFiles*weights[color].AdditionalModifier.HalfRookModifier
}

func calculatePawnFileFill(pawnBitboard uint64) uint64 {
// Northfill
pawnBitboard |= (pawnBitboard << 8)
pawnBitboard |= (pawnBitboard << 16)
pawnBitboard |= (pawnBitboard << 32)
// Southfill
pawnBitboard |= (pawnBitboard >> 8)
pawnBitboard |= (pawnBitboard >> 16)
pawnBitboard |= (pawnBitboard >> 32)

return pawnBitboard
func calculatePassedPawns(g *game.Game, color game.PlayerColor) (result int) {
if color == game.White {
frontSpansBlack := calculatePawnSouthFill(g.Position.Black.Pawns) & ^g.Position.Black.Pawns
attackingSpansBlack := frontSpansBlack
attackingSpansBlack |= (frontSpansBlack << 1) & ^bitboardFileA //shift everything east and care for wraps
attackingSpansBlack |= (frontSpansBlack >> 1) & ^bitboardFileH //shift everything west and care for wraps
whitePassedPawns := g.Position.White.Pawns & ^attackingSpansBlack
for x := whitePassedPawns; x != 0; x &= x - 1 {
square := bits.TrailingZeros64(x)
result += weights[color].Midgame.PassedPawnModifier[square]
}
return
}

//black
frontSpansWhite := calculatePawnNorthFill(g.Position.White.Pawns) & ^g.Position.White.Pawns
attackingSpansWhite := frontSpansWhite
attackingSpansWhite |= (frontSpansWhite << 1) & ^bitboardFileA //shift everything east and care for wraps
attackingSpansWhite |= (frontSpansWhite >> 1) & ^bitboardFileH //shift everything west and care for wraps
blackPassedPawns := g.Position.Black.Pawns & ^attackingSpansWhite
for x := blackPassedPawns; x != 0; x &= x - 1 {
square := bits.TrailingZeros64(x)
result += weights[color].Midgame.PassedPawnModifier[square]
}
return
}
88 changes: 88 additions & 0 deletions evaluation/evaluation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,3 +217,91 @@ func TestCalculateEvaluation(t *testing.T) {
})
}
}

func Test_calculatePairModifier(t *testing.T) {
type args struct {
g *game.Game
color game.PlayerColor
}
tests := []struct {
name string
args args
want int
}{
{"GameStart White", args{game.NewFromFen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"), game.White}, 6},
{"GameStart Black", args{game.NewFromFen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"), game.Black}, 6},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := calculatePairModifier(tt.args.g, tt.args.color); got != tt.want {
t.Errorf("calculatePairModifier() = %v, want %v", got, tt.want)
}
})
}
}

func Test_calculateTempoModifier(t *testing.T) {
type args struct {
g *game.Game
color game.PlayerColor
}
tests := []struct {
name string
args args
want int
}{
{"GameStart White", args{game.NewFromFen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"), game.White}, 10},
{"GameStart Black", args{game.NewFromFen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"), game.Black}, 0},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := calculateTempoModifier(tt.args.g, tt.args.color); got != tt.want {
t.Errorf("calculateTempoModifier() = %v, want %v", got, tt.want)
}
})
}
}

func Test_calculateRookModifier(t *testing.T) {
type args struct {
g *game.Game
color game.PlayerColor
}
tests := []struct {
name string
args args
want int
}{
{"GameStart White", args{game.NewFromFen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"), game.White}, 0},
{"GameStart Black", args{game.NewFromFen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"), game.Black}, 0},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := calculateRookModifier(tt.args.g, tt.args.color); got != tt.want {
t.Errorf("calculateRookModifier() = %v, want %v", got, tt.want)
}
})
}
}

func Test_calculatePassedPawns(t *testing.T) {
type args struct {
g *game.Game
color game.PlayerColor
}
tests := []struct {
name string
args args
want int
}{
{"GameStart White", args{game.NewFromFen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"), game.White}, 0},
{"GameStart Black", args{game.NewFromFen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"), game.Black}, 0},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := calculatePassedPawns(tt.args.g, tt.args.color); got != tt.want {
t.Errorf("calculatePassedPawns() = %v, want %v", got, tt.want)
}
})
}
}
26 changes: 26 additions & 0 deletions evaluation/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package evaluation

var (
bitboardFileA uint64 = 0x0101010101010101
bitboardFileH uint64 = 0x8080808080808080
)

func calculatePawnFileFill(pawnBitboard uint64) uint64 {
pawnBitboard |= calculatePawnNorthFill(pawnBitboard)
pawnBitboard |= calculatePawnSouthFill(pawnBitboard)
return pawnBitboard
}

func calculatePawnNorthFill(pawnBitboard uint64) uint64 {
pawnBitboard |= (pawnBitboard << 8)
pawnBitboard |= (pawnBitboard << 16)
pawnBitboard |= (pawnBitboard << 32)
return pawnBitboard
}

func calculatePawnSouthFill(pawnBitboard uint64) uint64 {
pawnBitboard |= (pawnBitboard >> 8)
pawnBitboard |= (pawnBitboard >> 16)
pawnBitboard |= (pawnBitboard >> 32)
return pawnBitboard
}
29 changes: 22 additions & 7 deletions evaluation/weights.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ type AdditionalModifier struct {
}

type GamephaseWeights struct {
Material map[dragontoothmg.Piece]int
PieceSquareTables map[dragontoothmg.Piece][64]int
Material map[dragontoothmg.Piece]int
PassedPawnModifier [64]int
PieceSquareTables map[dragontoothmg.Piece][64]int
}

type Weights struct {
Expand Down Expand Up @@ -49,6 +50,16 @@ var (
dragontoothmg.Queen: 900,
dragontoothmg.King: 0,
},
PassedPawnModifier: [64]int{
0, 0, 0, 0, 0, 0, 0, 0,
10, 10, 10, 10, 10, 10, 10, 10,
20, 20, 20, 20, 20, 20, 20, 20,
40, 40, 40, 40, 40, 40, 40, 40,
60, 60, 60, 60, 60, 60, 60, 60,
80, 80, 80, 80, 80, 80, 80, 80,
100, 100, 100, 100, 100, 100, 100, 100,
0, 0, 0, 0, 0, 0, 0, 0,
},
PieceSquareTables: map[dragontoothmg.Piece][64]int{
dragontoothmg.Pawn: [64]int{
0, 0, 0, 0, 0, 0, 0, 0,
Expand Down Expand Up @@ -116,11 +127,13 @@ var (

var (
midgameWeights = GamephaseWeights{
Material: weightsForAllPhases.Material,
PieceSquareTables: weightsForAllPhases.PieceSquareTables,
Material: weightsForAllPhases.Material,
PassedPawnModifier: weightsForAllPhases.PassedPawnModifier,
PieceSquareTables: weightsForAllPhases.PieceSquareTables,
}
endgameWeights = GamephaseWeights{
Material: weightsForAllPhases.Material,
Material: weightsForAllPhases.Material,
PassedPawnModifier: weightsForAllPhases.PassedPawnModifier,
PieceSquareTables: map[dragontoothmg.Piece][64]int{
dragontoothmg.Pawn: weightsForAllPhases.PieceSquareTables[dragontoothmg.Pawn],
dragontoothmg.Knight: weightsForAllPhases.PieceSquareTables[dragontoothmg.Knight],
Expand Down Expand Up @@ -160,7 +173,8 @@ var (
},
game.Black: Weights{
Midgame: GamephaseWeights{
Material: midgameWeights.Material,
Material: midgameWeights.Material,
PassedPawnModifier: flipPstArrayVertically(midgameWeights.PassedPawnModifier),
PieceSquareTables: map[dragontoothmg.Piece][64]int{
dragontoothmg.Pawn: flipPstArrayVertically(midgameWeights.PieceSquareTables[dragontoothmg.Pawn]),
dragontoothmg.Knight: flipPstArrayVertically(midgameWeights.PieceSquareTables[dragontoothmg.Knight]),
Expand All @@ -171,7 +185,8 @@ var (
},
},
Endgame: GamephaseWeights{
Material: endgameWeights.Material,
Material: endgameWeights.Material,
PassedPawnModifier: flipPstArrayVertically(endgameWeights.PassedPawnModifier),
PieceSquareTables: map[dragontoothmg.Piece][64]int{
dragontoothmg.Pawn: flipPstArrayVertically(endgameWeights.PieceSquareTables[dragontoothmg.Pawn]),
dragontoothmg.Knight: flipPstArrayVertically(endgameWeights.PieceSquareTables[dragontoothmg.Knight]),
Expand Down
2 changes: 1 addition & 1 deletion search/search_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ func TestProblematicGames(t *testing.T) {
"Test QuiescenceSearch on Errors concerning no available moves",
fields{
"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",
7,
4,
4,
},
},
Expand Down
20 changes: 13 additions & 7 deletions search/transposition_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const (
alphaBetaBoundUpper
)

// 96 bits = 12 Bytes
type transpositionTableEntry struct {
lock int32
move dragontoothmg.Move
Expand All @@ -23,23 +24,28 @@ type transpositionTableEntry struct {
}

type TranspositionTable struct {
maxSize int
entries map[uint64]transpositionTableEntry
maxSizeInEntries int
entries map[uint64]transpositionTableEntry
}

// 1 entry is 20 bytes
func NewTranspositionTable(maxSize int) *TranspositionTable {
// 1 entry is 12 bytes
func NewTranspositionTable(maxSizeInBytes int) *TranspositionTable {
maxSizeInEntries := maxSizeInBytes / 12
return &TranspositionTable{
maxSize: maxSize,
entries: make(map[uint64]transpositionTableEntry, maxSize),
maxSizeInEntries: maxSizeInEntries,
entries: make(map[uint64]transpositionTableEntry, maxSizeInEntries),
}
}

func (tt *TranspositionTable) Empty() {
tt.entries = make(map[uint64]transpositionTableEntry, tt.maxSize)
tt.entries = make(map[uint64]transpositionTableEntry, tt.maxSizeInEntries)
}

func (tt *TranspositionTable) InsertIfNeeded(hash uint64, move dragontoothmg.Move, score int, depth int, bound alphaBetaBound) {
if len(tt.entries) >= tt.maxSizeInEntries {
tt.Empty()
}

entry, found := tt.entries[hash]

if !found {
Expand Down
25 changes: 21 additions & 4 deletions uci/timemanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func NewTimingInfo(messageParts []string) (timingInfo *UciTimingInfo) {
return
}

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

timeLeft -= MoveOverhead
moveOverhead := MoveOverhead
maxTime := MaxTime
for _, option := range options {
if option.name == "Move Overhead" {
optionsMO, err := strconv.Atoi(option.value)
if err == nil {
moveOverhead = time.Duration(optionsMO) * time.Millisecond
}
}
if option.name == "Max Time" {
optionsMT, err := strconv.Atoi(option.value)
if err == nil {
maxTime = time.Duration(optionsMT) * time.Second
}
}
}

timeLeft -= moveOverhead
if timeLeft <= 0 {
timeLeft = 0
}
Expand All @@ -73,8 +90,8 @@ func (timingInfo *UciTimingInfo) calculateTimeoutContext(ctx context.Context, g
if limit > timeLeft-MinTimeLeft {
limit = timeLeft - MinTimeLeft
}
if limit > MaxTime {
limit = MaxTime
if limit > maxTime {
limit = maxTime
}

return context.WithDeadline(ctx, timingInfo.StartTimestamp.Add(limit))
Expand Down
Loading

0 comments on commit c4cb549

Please sign in to comment.