Skip to content

Commit

Permalink
Add tests to evaluation
Browse files Browse the repository at this point in the history
  • Loading branch information
ArcticXWolf committed Apr 11, 2021
1 parent ae27ac9 commit 5b30b34
Show file tree
Hide file tree
Showing 4 changed files with 311 additions and 12 deletions.
7 changes: 3 additions & 4 deletions evaluation/evaluation.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func (e *Evaluation) updateTotal() {
if e.Game.Result == game.WhiteWon {
e.TotalScore = int(^uint(0) >> 1) // MAX INT
} else if e.Game.Result == game.BlackWon {
e.TotalScore = -e.TotalScore - 1 // MIN INT
e.TotalScore = -int(^uint(0)>>1) - 1 // MIN INT
} else {
e.TotalScore = 0 // DRAW
}
Expand Down Expand Up @@ -100,7 +100,7 @@ func calculatePieceScore(g *game.Game, color game.PlayerColor) map[dragontoothmg
bboards = g.Position.Black
}

for i := 1; i <= 5; i++ {
for i := 1; i <= 6; i++ {
bitboard := getBitboardByPieceType(&bboards, dragontoothmg.Piece(i))
count := bits.OnesCount64(bitboard)
ps[dragontoothmg.Piece(i)] += count * GetWeights().Midgame.Material[dragontoothmg.Piece(i)]
Expand All @@ -121,10 +121,9 @@ func getBitboardByPieceType(bbs *dragontoothmg.Bitboards, pieceType dragontoothm
return bbs.Rooks
case dragontoothmg.Queen:
return bbs.Queens
case dragontoothmg.King:
default:
return bbs.Kings
}
return bbs.All
}

func sumMapValues(mapToSum map[dragontoothmg.Piece]int) int {
Expand Down
207 changes: 207 additions & 0 deletions evaluation/evaluation_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
package evaluation

import (
"reflect"
"testing"

"github.com/dylhunn/dragontoothmg"
"go.janniklasrichter.de/axwchessbot/game"
)

func Test_calculateGamephase(t *testing.T) {
type args struct {
g *game.Game
color game.PlayerColor
}
tests := []struct {
name string
args args
want uint8
}{
{"GameStart White", args{game.NewFromFen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"), game.White}, 12},
{"GameStart Black", args{game.NewFromFen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"), game.Black}, 12},
{"KvKQ White", args{game.NewFromFen("1q6/2k5/8/8/8/8/3K4/8 w - - 0 1"), game.White}, 0},
{"KvKQ Black", args{game.NewFromFen("1q6/2k5/8/8/8/8/3K4/8 w - - 0 1"), game.Black}, 4},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := calculateGamephase(tt.args.g, tt.args.color); got != tt.want {
t.Errorf("calculateGamephase() = %v, want %v", got, tt.want)
}
})
}
}

func Test_calculatePieceScore(t *testing.T) {
type args struct {
g *game.Game
color game.PlayerColor
}
tests := []struct {
name string
args args
want map[dragontoothmg.Piece]int
}{
{"GameStart White", args{game.NewFromFen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"), game.White}, map[dragontoothmg.Piece]int{dragontoothmg.Pawn: 800, dragontoothmg.Knight: 640, dragontoothmg.Bishop: 660, dragontoothmg.Rook: 1000, dragontoothmg.Queen: 900, dragontoothmg.King: 0}},
{"GameStart Black", args{game.NewFromFen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"), game.Black}, map[dragontoothmg.Piece]int{dragontoothmg.Pawn: 800, dragontoothmg.Knight: 640, dragontoothmg.Bishop: 660, dragontoothmg.Rook: 1000, dragontoothmg.Queen: 900, dragontoothmg.King: 0}},
{"KRNPvKQB White", args{game.NewFromFen("1q6/2k1b3/8/8/8/5P2/3KN3/7R w - - 0 1"), game.White}, map[dragontoothmg.Piece]int{dragontoothmg.Pawn: 100, dragontoothmg.Knight: 320, dragontoothmg.Bishop: 0, dragontoothmg.Rook: 500, dragontoothmg.Queen: 0, dragontoothmg.King: 0}},
{"KRNPvKQB Black", args{game.NewFromFen("1q6/2k1b3/8/8/8/5P2/3KN3/7R w - - 0 1"), game.Black}, map[dragontoothmg.Piece]int{dragontoothmg.Pawn: 0, dragontoothmg.Knight: 0, dragontoothmg.Bishop: 330, dragontoothmg.Rook: 0, dragontoothmg.Queen: 900, dragontoothmg.King: 0}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := calculatePieceScore(tt.args.g, tt.args.color); !reflect.DeepEqual(got, tt.want) {
t.Errorf("calculatePieceScore() = %v, want %v", got, tt.want)
}
})
}
}

func TestEvaluation_updateTotal(t *testing.T) {
type fields struct {
Game *game.Game
White EvaluationPart
Black EvaluationPart
GameOver bool
TotalScore int
TotalScorePerspective int
}
tests := []struct {
name string
fields fields
want int
}{
{
"GameStart",
fields{
Game: game.NewFromFen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"),
White: EvaluationPart{
GamePhase: 12,
PieceScore: map[dragontoothmg.Piece]int{dragontoothmg.Pawn: 800, dragontoothmg.Knight: 640, dragontoothmg.Bishop: 660, dragontoothmg.Rook: 1000, dragontoothmg.Queen: 900, dragontoothmg.King: 0},
},
Black: EvaluationPart{
GamePhase: 12,
PieceScore: map[dragontoothmg.Piece]int{dragontoothmg.Pawn: 800, dragontoothmg.Knight: 640, dragontoothmg.Bishop: 660, dragontoothmg.Rook: 1000, dragontoothmg.Queen: 900, dragontoothmg.King: 0},
},
},
0,
},
{
"KRNPvKQB",
fields{
Game: game.NewFromFen("1q6/2k1b3/8/8/8/5P2/3KN3/7R b - - 0 1"),
White: EvaluationPart{
GamePhase: 12,
PieceScore: map[dragontoothmg.Piece]int{dragontoothmg.Pawn: 100, dragontoothmg.Knight: 320, dragontoothmg.Bishop: 0, dragontoothmg.Rook: 500, dragontoothmg.Queen: 0, dragontoothmg.King: 0},
},
Black: EvaluationPart{
GamePhase: 12,
PieceScore: map[dragontoothmg.Piece]int{dragontoothmg.Pawn: 0, dragontoothmg.Knight: 0, dragontoothmg.Bishop: 330, dragontoothmg.Rook: 0, dragontoothmg.Queen: 900, dragontoothmg.King: 0},
},
},
-310,
},
{
"KQvK Checkmate White",
fields{
Game: game.NewFromFen("7k/6Q1/5K2/8/8/8/8/8 b - - 0 1"),
White: EvaluationPart{
GamePhase: 4,
PieceScore: map[dragontoothmg.Piece]int{dragontoothmg.Pawn: 0, dragontoothmg.Knight: 0, dragontoothmg.Bishop: 0, dragontoothmg.Rook: 0, dragontoothmg.Queen: 900, dragontoothmg.King: 0},
},
Black: EvaluationPart{
GamePhase: 0,
PieceScore: map[dragontoothmg.Piece]int{dragontoothmg.Pawn: 0, dragontoothmg.Knight: 0, dragontoothmg.Bishop: 0, dragontoothmg.Rook: 0, dragontoothmg.Queen: 0, dragontoothmg.King: 0},
},
GameOver: true,
},
int(^uint(0) >> 1),
},
{
"KQvK Checkmate Black",
fields{
Game: game.NewFromFen("8/8/8/8/8/2k5/1q6/K7 w - - 0 1"),
White: EvaluationPart{
GamePhase: 0,
PieceScore: map[dragontoothmg.Piece]int{dragontoothmg.Pawn: 0, dragontoothmg.Knight: 0, dragontoothmg.Bishop: 0, dragontoothmg.Rook: 0, dragontoothmg.Queen: 0, dragontoothmg.King: 0},
},
Black: EvaluationPart{
GamePhase: 4,
PieceScore: map[dragontoothmg.Piece]int{dragontoothmg.Pawn: 0, dragontoothmg.Knight: 0, dragontoothmg.Bishop: 0, dragontoothmg.Rook: 0, dragontoothmg.Queen: 900, dragontoothmg.King: 0},
},
GameOver: true,
},
-int(^uint(0)>>1) - 1,
},
{
"KvKR Stalemate",
fields{
Game: game.NewFromFen("8/8/8/8/8/2k5/1r6/K7 w - - 0 1"),
White: EvaluationPart{
GamePhase: 0,
PieceScore: map[dragontoothmg.Piece]int{dragontoothmg.Pawn: 0, dragontoothmg.Knight: 0, dragontoothmg.Bishop: 0, dragontoothmg.Rook: 0, dragontoothmg.Queen: 0, dragontoothmg.King: 0},
},
Black: EvaluationPart{
GamePhase: 2,
PieceScore: map[dragontoothmg.Piece]int{dragontoothmg.Pawn: 0, dragontoothmg.Knight: 0, dragontoothmg.Bishop: 0, dragontoothmg.Rook: 500, dragontoothmg.Queen: 0, dragontoothmg.King: 0},
},
GameOver: true,
},
0,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
e := &Evaluation{
Game: tt.fields.Game,
White: tt.fields.White,
Black: tt.fields.Black,
GameOver: tt.fields.GameOver,
TotalScore: tt.fields.TotalScore,
TotalScorePerspective: tt.fields.TotalScorePerspective,
}
e.updateTotal()
if !reflect.DeepEqual(e.TotalScore, tt.want) {
t.Errorf("Evaluation.updateTotal() = %v, want %v", e.TotalScore, tt.want)
}
})
}
}

func TestCalculateEvaluation(t *testing.T) {
game_gamestart := game.NewFromFen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1")
tests := []struct {
name string
args *game.Game
want *Evaluation
}{
{
"GameStart",
game_gamestart,
&Evaluation{
Game: game_gamestart,
White: EvaluationPart{
GamePhase: 12,
PieceScore: map[dragontoothmg.Piece]int{dragontoothmg.Pawn: 800, dragontoothmg.Knight: 640, dragontoothmg.Bishop: 660, dragontoothmg.Rook: 1000, dragontoothmg.Queen: 900, dragontoothmg.King: 0},
PieceSquareScoreMidgame: make(map[dragontoothmg.Piece]int),
PieceSquareScoreEndgame: make(map[dragontoothmg.Piece]int),
},
Black: EvaluationPart{
GamePhase: 12,
PieceScore: map[dragontoothmg.Piece]int{dragontoothmg.Pawn: 800, dragontoothmg.Knight: 640, dragontoothmg.Bishop: 660, dragontoothmg.Rook: 1000, dragontoothmg.Queen: 900, dragontoothmg.King: 0},
PieceSquareScoreMidgame: make(map[dragontoothmg.Piece]int),
PieceSquareScoreEndgame: make(map[dragontoothmg.Piece]int),
},
GameOver: false,
TotalScore: 0,
TotalScorePerspective: 0,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := CalculateEvaluation(tt.args); !reflect.DeepEqual(got, tt.want) {
t.Errorf("CalculateEvaluation() = %v, want %v", got, tt.want)
}
})
}
}
5 changes: 3 additions & 2 deletions evaluation/weights.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ func getWeightsForAllPhases() GamephaseWeights {
return GamephaseWeights{
Material: map[dragontoothmg.Piece]int{
dragontoothmg.Pawn: 100,
dragontoothmg.Knight: 300,
dragontoothmg.Bishop: 300,
dragontoothmg.Knight: 320,
dragontoothmg.Bishop: 330,
dragontoothmg.Rook: 500,
dragontoothmg.Queen: 900,
dragontoothmg.King: 0,
},
PieceSquareTables: map[dragontoothmg.Piece][64]int{
dragontoothmg.Pawn: [64]int{
Expand Down
104 changes: 98 additions & 6 deletions game/game_test.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,106 @@
package game

import (
"reflect"
"testing"
)

func TestCheckmate(t *testing.T) {
fenStr := "rn1qkbnr/pbpp1ppp/1p6/4p3/2B1P3/5Q2/PPPP1PPP/RNB1K1NR w KQkq - 0 1"
game := NewFromFen(fenStr)
game.pushMove("f3f7")
if game.Result != WhiteWon {
t.Fatalf("expected result %d but got %d", WhiteWon, game.Result)
func TestGameOverDetection(t *testing.T) {
type args struct {
fen string
}
tests := []struct {
name string
args args
wantResult GameResult
wantReason DrawReason
}{
{"GameNotOver", args{"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"}, GameNotOver, NoDraw},
{"Checkmate White", args{"rn1qkbnr/pbpp1Qpp/1p6/4p3/2B1P3/8/PPPP1PPP/RNB1K1NR b KQkq - 0 1"}, WhiteWon, NoDraw},
{"Checkmate Black", args{"rnb1k1nr/pppp1ppp/8/2b1p3/2B1P3/2N5/PPPP1qPP/R1BQK2R w KQkq - 0 1"}, BlackWon, NoDraw},
{"Draw Stalemate", args{"7k/6R1/5K2/8/8/8/8/8 b - - 0 1"}, Draw, Stalemate},
{"Draw InsufficientMaterial KBvKB Whitefields", args{"8/2kb4/8/8/8/3B4/4K3/8 w - - 0 1"}, Draw, InsufficientMaterial},
{"Draw InsufficientMaterial KBvKB Blackfields", args{"8/2k1b3/8/8/5B2/5K2/8/8 w - - 0 1"}, Draw, InsufficientMaterial},
{"Draw InsufficientMaterial KvK", args{"8/2k5/8/8/8/8/3K4/8 w - - 0 1"}, Draw, InsufficientMaterial},
{"Draw InsufficientMaterial KNvK", args{"8/2k5/8/8/4N3/5K2/8/8 w - - 0 1"}, Draw, InsufficientMaterial},
{"Draw InsufficientMaterial KvKB", args{"8/2k5/3b4/8/8/5K2/8/8 w - - 0 1"}, Draw, InsufficientMaterial},
{"Draw FiftyMoveRule", args{"rNB1k2r/ppp2ppp/3p3B/3np2Q/3NP2q/3P3b/PPP2PPP/Rnb1K2R w - - 100 43"}, Draw, FiftyMoveRule},
{"GameNotOver KBvKB", args{"8/2k1b3/8/8/4B3/5K2/8/8 w - - 0 1"}, GameNotOver, NoDraw},
{"King Missing", args{"8/8/8/8/8/5K2/8/8 w - - 0 1"}, GameNotOver, NoDraw},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := NewFromFen(tt.args.fen)
if !reflect.DeepEqual(got.Result, tt.wantResult) {
t.Errorf("Game Over Detection, Result = %v, want %v", got.Result, tt.wantResult)
}
if !reflect.DeepEqual(got.DrawReason, tt.wantReason) {
t.Errorf("Game Over Detection, Reason = %v, want %v", got.DrawReason, tt.wantReason)
}
})
}
}

func TestDrawByRepetition(t *testing.T) {
game := NewFromFen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1")
game.pushMove("e2e4")
game.pushMove("e7e5")

game.pushMove("f1c4")
game.pushMove("f8c5")
game.pushMove("c4f1")
game.pushMove("c5f8")

game.pushMove("f1c4")
game.pushMove("f8c5")
game.pushMove("c4f1")
game.pushMove("c5f8")

game.pushMove("f1c4")
game.pushMove("f8c5")
game.pushMove("c4f1")
game.pushMove("c5f8")

if game.Result != Draw {
t.Errorf("Draw by Repetition, Result = %v, want %v", game.Result, Draw)
}
if game.DrawReason != ThreefoldRepetition {
t.Errorf("Draw by Repetition, Result = %v, want %v", game.DrawReason, ThreefoldRepetition)
}
}

func TestPushPopMove(t *testing.T) {
game := New()
unmodified_game := New()
game.pushMove("e2e4")

if reflect.DeepEqual(game, unmodified_game) {
t.Errorf("PushMove = %v is wrongfully equal to %v", game, unmodified_game)
}

game.popMove()

if !reflect.DeepEqual(game, unmodified_game) {
t.Errorf("PushPopMove = %v, want %v", game, unmodified_game)
}
}

func TestPushMoveError(t *testing.T) {
game := New()

err := game.pushMove("e123e1")

if err == nil {
t.Errorf("PushMoveError = %v is not an error", err)
}
}

func TestPopNoMoves(t *testing.T) {
game := New()

err := game.popMove()

if err == nil {
t.Errorf("PopNoMoves = %v, is not an error", err)
}
}

0 comments on commit 5b30b34

Please sign in to comment.