You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

292 lines
6.8 KiB

package chess
import (
"log"
"strings"
"testing"
)
func TestCheckmate(t *testing.T) {
fenStr := "rn1qkbnr/pbpp1ppp/1p6/4p3/2B1P3/5Q2/PPPP1PPP/RNB1K1NR w KQkq - 0 1"
fen, err := FEN(fenStr)
if err != nil {
t.Fatal(err)
}
g := NewGame(fen)
if err := g.MoveStr("Qxf7#"); err != nil {
t.Fatal(err)
}
if g.Method() != Checkmate {
t.Fatalf("expected method %s but got %s", Checkmate, g.Method())
}
if g.Outcome() != WhiteWon {
t.Fatalf("expected outcome %s but got %s", WhiteWon, g.Outcome())
}
}
func TestCheckmateFromFen(t *testing.T) {
fenStr := "rn1qkbnr/pbpp1Qpp/1p6/4p3/2B1P3/8/PPPP1PPP/RNB1K1NR b KQkq - 0 1"
fen, err := FEN(fenStr)
if err != nil {
t.Fatal(err)
}
g := NewGame(fen)
if g.Method() != Checkmate {
t.Error(g.Position().Board().Draw())
t.Fatalf("expected method %s but got %s", Checkmate, g.Method())
}
if g.Outcome() != WhiteWon {
t.Fatalf("expected outcome %s but got %s", WhiteWon, g.Outcome())
}
}
func TestStalemate(t *testing.T) {
fenStr := "k1K5/8/8/8/8/8/8/1Q6 w - - 0 1"
fen, err := FEN(fenStr)
if err != nil {
t.Fatal(err)
}
g := NewGame(fen)
if err := g.MoveStr("Qb6"); err != nil {
t.Fatal(err)
}
if g.Method() != Stalemate {
t.Fatalf("expected method %s but got %s", Stalemate, g.Method())
}
if g.Outcome() != Draw {
t.Fatalf("expected outcome %s but got %s", Draw, g.Outcome())
}
}
// position shouldn't result in stalemate because pawn can move http://en.lichess.org/Pc6mJDZN#138
func TestInvalidStalemate(t *testing.T) {
fenStr := "8/3P4/8/8/8/7k/7p/7K w - - 2 70"
fen, err := FEN(fenStr)
if err != nil {
t.Fatal(err)
}
g := NewGame(fen)
if err := g.MoveStr("d8=Q"); err != nil {
t.Fatal(err)
}
if g.Outcome() != NoOutcome {
t.Fatalf("expected outcome %s but got %s", NoOutcome, g.Outcome())
}
}
func TestThreeFoldRepition(t *testing.T) {
g := NewGame()
moves := []string{
"Nf3", "Nf6", "Ng1", "Ng8",
"Nf3", "Nf6", "Ng1", "Ng8",
}
for _, m := range moves {
if err := g.MoveStr(m); err != nil {
t.Fatal(err)
}
}
if err := g.Draw(ThreefoldRepetition); err != nil {
for _, pos := range g.Positions() {
log.Println(pos.String())
}
t.Fatalf("%s - %d reps", err.Error(), g.numOfRepitions())
}
}
func TestInvalidThreeFoldRepition(t *testing.T) {
g := NewGame()
moves := []string{
"Nf3", "Nf6", "Ng1", "Ng8",
}
for _, m := range moves {
if err := g.MoveStr(m); err != nil {
t.Fatal(err)
}
}
if err := g.Draw(ThreefoldRepetition); err == nil {
t.Fatal("should require three repeated board states")
}
}
func TestFiveFoldRepition(t *testing.T) {
g := NewGame()
moves := []string{
"Nf3", "Nf6", "Ng1", "Ng8",
"Nf3", "Nf6", "Ng1", "Ng8",
"Nf3", "Nf6", "Ng1", "Ng8",
"Nf3", "Nf6", "Ng1", "Ng8",
}
for _, m := range moves {
if err := g.MoveStr(m); err != nil {
t.Fatal(err)
}
}
if g.Outcome() != Draw || g.Method() != FivefoldRepetition {
t.Fatal("should automatically draw after five repetitions")
}
}
func TestFiftyMoveRule(t *testing.T) {
fen, _ := FEN("2r3k1/1q1nbppp/r3p3/3pP3/pPpP4/P1Q2N2/2RN1PPP/2R4K b - b3 100 60")
g := NewGame(fen)
if err := g.Draw(FiftyMoveRule); err != nil {
t.Fatal(err)
}
}
func TestInvalidFiftyMoveRule(t *testing.T) {
fen, _ := FEN("2r3k1/1q1nbppp/r3p3/3pP3/pPpP4/P1Q2N2/2RN1PPP/2R4K b - b3 99 60")
g := NewGame(fen)
if err := g.Draw(FiftyMoveRule); err == nil {
t.Fatal("should require fifty moves")
}
}
func TestSeventyFiveMoveRule(t *testing.T) {
fen, _ := FEN("2r3k1/1q1nbppp/r3p3/3pP3/pPpP4/P1Q2N2/2RN1PPP/2R4K b - b3 149 80")
g := NewGame(fen)
if err := g.MoveStr("Kf8"); err != nil {
t.Fatal(err)
}
if g.Outcome() != Draw || g.Method() != SeventyFiveMoveRule {
t.Fatal("should automatically draw after seventy five moves w/ no pawn move or capture")
}
}
func TestInsufficentMaterial(t *testing.T) {
fens := []string{
"8/2k5/8/8/8/3K4/8/8 w - - 1 1",
"8/2k5/8/8/8/3K1N2/8/8 w - - 1 1",
"8/2k5/8/8/8/3K1B2/8/8 w - - 1 1",
"8/2k5/2b5/8/8/3K1B2/8/8 w - - 1 1",
"4b3/2k5/2b5/8/8/3K1B2/8/8 w - - 1 1",
}
for _, f := range fens {
fen, err := FEN(f)
if err != nil {
t.Fatal(err)
}
g := NewGame(fen)
if g.Outcome() != Draw || g.Method() != InsufficientMaterial {
log.Println(g.Position().Board().Draw())
t.Fatalf("%s should automatically draw by insufficent material", f)
}
}
}
func TestSufficentMaterial(t *testing.T) {
fens := []string{
"8/2k5/8/8/8/3K1B2/4N3/8 w - - 1 1",
"8/2k5/8/8/8/3KBB2/8/8 w - - 1 1",
"8/2k1b3/8/8/8/3K1B2/8/8 w - - 1 1",
"8/2k5/8/8/4P3/3K4/8/8 w - - 1 1",
"8/2k5/8/8/8/3KQ3/8/8 w - - 1 1",
"8/2k5/8/8/8/3KR3/8/8 w - - 1 1",
}
for _, f := range fens {
fen, err := FEN(f)
if err != nil {
t.Fatal(err)
}
g := NewGame(fen)
if g.Outcome() != NoOutcome {
log.Println(g.Position().Board().Draw())
t.Fatalf("%s should not find insufficent material", f)
}
}
}
func TestSerializationCycle(t *testing.T) {
g := NewGame()
pgn, err := PGN(strings.NewReader(g.String()))
if err != nil {
t.Fatal(err)
}
cp := NewGame(pgn)
if cp.String() != g.String() {
t.Fatalf("expected %s but got %s", g.String(), cp.String())
}
}
func TestInitialNumOfValidMoves(t *testing.T) {
g := NewGame()
if len(g.ValidMoves()) != 20 {
t.Fatal("should find 20 valid moves from the initial position")
}
}
func TestTagPairs(t *testing.T) {
g := NewGame()
g.AddTagPair("Draw Offer", "White")
tagPair := g.GetTagPair("Draw Offer")
if tagPair == nil {
t.Fatalf("expected %s but got %s", "White", "nil")
}
if tagPair.Value != "White" {
t.Fatalf("expected %s but got %s", "White", tagPair.Value)
}
g.RemoveTagPair("Draw Offer")
tagPair = g.GetTagPair("Draw Offer")
if tagPair != nil {
t.Fatalf("expected %s but got %s", "nil", "not nil")
}
}
func TestPositionHash(t *testing.T) {
g1 := NewGame()
for _, s := range []string{"Nc3", "e5", "Nf3"} {
g1.MoveStr(s)
}
g2 := NewGame()
for _, s := range []string{"Nf3", "e5", "Nc3"} {
g2.MoveStr(s)
}
if g1.Position().Hash() != g2.Position().Hash() {
t.Fatalf("expected position hashes to be equal but got %s and %s", g1.Position().Hash(), g2.Position().Hash())
}
}
func BenchmarkStalemateStatus(b *testing.B) {
fenStr := "k1K5/8/8/8/8/8/8/1Q6 w - - 0 1"
fen, err := FEN(fenStr)
if err != nil {
b.Fatal(err)
}
g := NewGame(fen)
if err := g.MoveStr("Qb6"); err != nil {
b.Fatal(err)
}
b.ResetTimer()
for n := 0; n < b.N; n++ {
g.Position().Status()
}
}
func BenchmarkInvalidStalemateStatus(b *testing.B) {
fenStr := "8/3P4/8/8/8/7k/7p/7K w - - 2 70"
fen, err := FEN(fenStr)
if err != nil {
b.Fatal(err)
}
g := NewGame(fen)
if err := g.MoveStr("d8=Q"); err != nil {
b.Fatal(err)
}
b.ResetTimer()
for n := 0; n < b.N; n++ {
g.Position().Status()
}
}
func BenchmarkPositionHash(b *testing.B) {
fenStr := "8/3P4/8/8/8/7k/7p/7K w - - 2 70"
fen, err := FEN(fenStr)
if err != nil {
b.Fatal(err)
}
g := NewGame(fen)
b.ResetTimer()
for n := 0; n < b.N; n++ {
g.Position().Hash()
}
}