-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
ae27ac9
commit 5b30b34
Showing
4 changed files
with
311 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} |