github.com/frankkopp/FrankyGo@v1.0.3/internal/movegen/movegen_test.go (about)

     1  //
     2  // FrankyGo - UCI chess engine in GO for learning purposes
     3  //
     4  // MIT License
     5  //
     6  // Copyright (c) 2018-2020 Frank Kopp
     7  //
     8  // Permission is hereby granted, free of charge, to any person obtaining a copy
     9  // of this software and associated documentation files (the "Software"), to deal
    10  // in the Software without restriction, including without limitation the rights
    11  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    12  // copies of the Software, and to permit persons to whom the Software is
    13  // furnished to do so, subject to the following conditions:
    14  //
    15  // The above copyright notice and this permission notice shall be included in all
    16  // copies or substantial portions of the Software.
    17  //
    18  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    19  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    20  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    21  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    22  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    23  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    24  // SOFTWARE.
    25  //
    26  
    27  package movegen
    28  
    29  import (
    30  	"os"
    31  	"path"
    32  	"runtime"
    33  	"testing"
    34  	"time"
    35  
    36  	"github.com/op/go-logging"
    37  	"github.com/stretchr/testify/assert"
    38  
    39  	"github.com/frankkopp/FrankyGo/internal/config"
    40  	myLogging "github.com/frankkopp/FrankyGo/internal/logging"
    41  	"github.com/frankkopp/FrankyGo/internal/moveslice"
    42  	"github.com/frankkopp/FrankyGo/internal/position"
    43  	. "github.com/frankkopp/FrankyGo/internal/types"
    44  )
    45  
    46  var logTest *logging.Logger
    47  
    48  // make tests run in the projects root directory.
    49  func init() {
    50  	_, filename, _, _ := runtime.Caller(0)
    51  	dir := path.Join(path.Dir(filename), "../..")
    52  	err := os.Chdir(dir)
    53  	if err != nil {
    54  		panic(err)
    55  	}
    56  }
    57  
    58  // Setup the tests.
    59  func TestMain(m *testing.M) {
    60  	config.Setup()
    61  	logTest = myLogging.GetTestLog()
    62  	code := m.Run()
    63  	os.Exit(code)
    64  }
    65  
    66  func TestMovegenString(t *testing.T) {
    67  	mg := NewMoveGen()
    68  	out.Println(mg.String())
    69  	assert.EqualValues(t, "MoveGen: { OnDemand Stage: { 0 }, PV Move: Move: { MoveNone } Killer Move 1: " +
    70  		"Move: { MoveNone } Killer Move 2: Move: { MoveNone } }", mg.String())
    71  }
    72  
    73  func TestMovegenGeneratePawnMoves(t *testing.T) {
    74  	config.Settings.Search.UsePromNonQuiet = false
    75  	mg := NewMoveGen()
    76  	pos, _ := position.NewPositionFen("1kr3nr/pp1pP1P1/2p1p3/3P1p2/1n1bP3/2P5/PP3PPP/RNBQKBNR w KQ -")
    77  	moves := moveslice.MoveSlice{}
    78  
    79  	mg.generatePawnMoves(pos, GenNonQuiet, false, BbZero, &moves)
    80  	assert.Equal(t, 9, moves.Len())
    81  
    82  	moves.Clear()
    83  	mg.generatePawnMoves(pos, GenQuiet, false, BbZero, &moves)
    84  	assert.Equal(t, 16, moves.Len())
    85  
    86  	moves.Clear()
    87  	mg.generatePawnMoves(pos, GenAll, false, BbZero, &moves)
    88  	assert.Equal(t, 25, moves.Len())
    89  
    90  	config.Settings.Search.UsePromNonQuiet = true
    91  
    92  	moves.Clear()
    93  	mg.generatePawnMoves(pos, GenNonQuiet, false, BbZero, &moves)
    94  	assert.Equal(t, 11, moves.Len())
    95  
    96  	moves.Clear()
    97  	mg.generatePawnMoves(pos, GenQuiet, false, BbZero, &moves)
    98  	assert.Equal(t, 14, moves.Len())
    99  
   100  	moves.Clear()
   101  	mg.generatePawnMoves(pos, GenAll, false, BbZero, &moves)
   102  	assert.Equal(t, 25, moves.Len())
   103  
   104  	// moves.Sort()
   105  	// fmt.Printf("Moves: %d\n", moves.Len())
   106  	// l := moves.Len()
   107  	// for i := 0; i < l; i++ {
   108  	// 	fmt.Printf("Move: %s\n", moves.At(i))
   109  	// }
   110  }
   111  
   112  func TestMovegenGenerateCastling(t *testing.T) {
   113  	mg := NewMoveGen()
   114  	pos, _ := position.NewPositionFen("r3k2r/pbppqppp/1pn2n2/1B2p3/1b2P3/N1PP1N2/PP1BQPPP/R3K2R w KQkq -")
   115  	moves := moveslice.MoveSlice{}
   116  
   117  	mg.generateCastling(pos, GenAll, &moves)
   118  	assert.Equal(t, 2, moves.Len())
   119  	assert.Equal(t, "e1g1 e1c1", moves.StringUci())
   120  	moves.Clear()
   121  
   122  	pos, _ = position.NewPositionFen("r3k2r/pbppqppp/1pn2n2/1B2p3/1b2P3/N1PP1N2/PP1BQPPP/R3K2R b KQkq -")
   123  	mg.generateCastling(pos, GenAll, &moves)
   124  	assert.Equal(t, 2, moves.Len())
   125  	assert.Equal(t, "e8g8 e8c8", moves.StringUci())
   126  
   127  }
   128  
   129  func TestMovegenGenerateKingMoves(t *testing.T) {
   130  	mg := NewMoveGen()
   131  	moves := moveslice.MoveSlice{}
   132  
   133  	pos, _ := position.NewPositionFen("r3k2r/pbpNqppp/1pn2n2/1B2p3/1b2P3/2PP1N2/PP1nQPPP/R3K2R w KQkq -")
   134  	mg.generateKingMoves(pos, GenAll, false, BbZero, &moves)
   135  	assert.Equal(t, 3, moves.Len())
   136  	assert.Equal(t, "e1d2 e1d1 e1f1", moves.StringUci())
   137  	moves.Clear()
   138  
   139  	pos, _ = position.NewPositionFen("r3k2r/pbpNqppp/1pn2n2/1B2p3/1b2P3/2PP1N2/PP1nQPPP/R3K2R b KQkq -")
   140  	mg.generateKingMoves(pos, GenAll, false, BbZero, &moves)
   141  	assert.Equal(t, 3, moves.Len())
   142  	assert.Equal(t, "e8d7 e8d8 e8f8", moves.StringUci())
   143  }
   144  
   145  func TestMovegenGenerateMoves(t *testing.T) {
   146  	mg := NewMoveGen()
   147  	moves := moveslice.MoveSlice{}
   148  
   149  	pos, _ := position.NewPositionFen("r3k2r/pbpNqppp/1pn2n2/1B2p3/1b2P3/2PP1N2/PP1nQPPP/R3K2R w KQkq -")
   150  	mg.generateMoves(pos, GenNonQuiet, false, BbZero, &moves)
   151  	assert.Equal(t, 7, moves.Len())
   152  	assert.Equal(t, "f3d2 f3e5 d7e5 d7b6 d7f6 b5c6 e2d2", moves.StringUci())
   153  	moves.Clear()
   154  
   155  	pos, _ = position.NewPositionFen("r3k2r/pbpNqppp/1pn2n2/1B2p3/1b2P3/2PP1N2/PP1nQPPP/R3K2R b KQkq -")
   156  	mg.generateMoves(pos, GenQuiet, false, BbZero, &moves)
   157  	assert.Equal(t, 28, moves.Len())
   158  	assert.Equal(t, "d2b1 d2f1 d2b3 d2c4 c6d4 c6a5 c6b8 c6d8 f6g4 f6d5 f6h5 f6g8 b4a3 b4a5 b4c5 b4d6 b7a6 "+
   159  		"b7c8 a8b8 a8c8 a8d8 h8f8 h8g8 e7c5 e7d6 e7e6 e7d8 e7f8", moves.StringUci())
   160  	moves.Clear()
   161  
   162  	pos, _ = position.NewPositionFen("r3k2r/pbpNqppp/1pn2n2/1B2p3/1b2P3/2PP1N2/PP1nQPPP/R3K2R b KQkq -")
   163  	mg.generateMoves(pos, GenAll, false, BbZero, &moves)
   164  	assert.Equal(t, 34, moves.Len())
   165  	assert.Equal(t, "d2f3 d2e4 d2b1 d2f1 d2b3 d2c4 c6d4 c6a5 c6b8 c6d8 f6e4 f6d7 f6g4 f6d5 f6h5 f6g8 b4c3 "+
   166  		"b4a3 b4a5 b4c5 b4d6 b7a6 b7c8 a8b8 a8c8 a8d8 h8f8 h8g8 e7d7 e7c5 e7d6 e7e6 e7d8 e7f8", moves.StringUci())
   167  }
   168  
   169  func TestOnDemand(t *testing.T) {
   170  	config.Settings.Search.UsePromNonQuiet = false
   171  
   172  	mg := NewMoveGen()
   173  
   174  	pos := position.NewPosition()
   175  
   176  	var moves = moveslice.NewMoveSlice(100)
   177  	for move := mg.GetNextMove(pos, GenAll, false); move != MoveNone; move = mg.GetNextMove(pos, GenAll, false) {
   178  		moves.PushBack(move)
   179  	}
   180  	assert.Equal(t, 20, moves.Len())
   181  	assert.Equal(t, "d2d4 e2e4 a2a3 h2h3 a2a4 b2b4 c2c4 f2f4 g2g4 h2h4 d2d3 e2e3 b2b3 g2g3 c2c3 f2f3 b1c3 " +
   182  		"g1f3 b1a3 g1h3", moves.StringUci())
   183  	moves.Clear()
   184  
   185  	pos, _ = position.NewPositionFen("r3k2r/pbpNqppp/1pn2n2/1B2p3/1b2P3/2PP1N2/PP1nQPPP/R3K2R w KQkq -")
   186  	for move := mg.GetNextMove(pos, GenAll, false); move != MoveNone; move = mg.GetNextMove(pos, GenAll, false) {
   187  		moves.PushBack(move)
   188  	}
   189  	assert.Equal(t, 40, moves.Len())
   190  	assert.Equal(t, "c3b4 d7f6 f3d2 b5c6 f3e5 d7e5 d7b6 e2d2 e1d2 d3d4 a2a3 h2h3 a2a4 g2g4 h2h4 c3c4 b2b3 " +
   191  		"g2g3 e1g1 e1c1 f3d4 d7c5 a1c1 a1d1 h1f1 b5c4 f3g5 e2e3 e2d1 b5a4 b5a6 a1b1 h1g1 e2f1 f3g1 f3h4 d7f8 d7b8 " +
   192  		"e1f1 e1d1", moves.StringUci())
   193  	moves.Clear()
   194  
   195  	// 86
   196  	pos, _ = position.NewPositionFen("r3k2r/1ppn3p/2q1q1n1/4P3/2q1Pp2/B5R1/pbp2PPP/1R4K1 b kq e3")
   197  	for move := mg.GetNextMove(pos, GenAll, false); move != MoveNone; move = mg.GetNextMove(pos, GenAll, false) {
   198  		moves.PushBack(move)
   199  	}
   200  	assert.Equal(t, 86, moves.Len())
   201  	moves.Clear()
   202  
   203  	// 218
   204  	pos, _ = position.NewPositionFen("R6R/3Q4/1Q4Q1/4Q3/2Q4Q/Q4Q2/pp1Q4/kBNN1KB1 w - -")
   205  	for move := mg.GetNextMove(pos, GenAll, false); move != MoveNone; move = mg.GetNextMove(pos, GenAll, false) {
   206  		moves.PushBack(move)
   207  	}
   208  	assert.Equal(t, 218, moves.Len())
   209  	moves.Clear()
   210  
   211  }
   212  
   213  
   214  func TestOnDemandPromNonQuiet(t *testing.T) {
   215  	config.Settings.Search.UsePromNonQuiet = true
   216  
   217  	mg := NewMoveGen()
   218  
   219  	pos := position.NewPosition()
   220  
   221  	var moves = moveslice.NewMoveSlice(100)
   222  	for move := mg.GetNextMove(pos, GenAll, false); move != MoveNone; move = mg.GetNextMove(pos, GenAll, false) {
   223  		moves.PushBack(move)
   224  	}
   225  	assert.Equal(t, 20, moves.Len())
   226  	assert.Equal(t, "d2d4 e2e4 a2a3 h2h3 a2a4 b2b4 c2c4 f2f4 g2g4 h2h4 d2d3 e2e3 b2b3 g2g3 c2c3 f2f3 b1c3 " +
   227  		"g1f3 b1a3 g1h3", moves.StringUci())
   228  	moves.Clear()
   229  
   230  	pos, _ = position.NewPositionFen("r3k2r/pbpNqppp/1pn2n2/1B2p3/1b2P3/2PP1N2/PP1nQPPP/R3K2R w KQkq -")
   231  	for move := mg.GetNextMove(pos, GenAll, false); move != MoveNone; move = mg.GetNextMove(pos, GenAll, false) {
   232  		moves.PushBack(move)
   233  	}
   234  	assert.Equal(t, 40, moves.Len())
   235  	assert.Equal(t, "c3b4 d7f6 f3d2 b5c6 f3e5 d7e5 d7b6 e2d2 e1d2 d3d4 a2a3 h2h3 a2a4 g2g4 h2h4 c3c4 b2b3 " +
   236  		"g2g3 e1g1 e1c1 f3d4 d7c5 a1c1 a1d1 h1f1 b5c4 f3g5 e2e3 e2d1 b5a4 b5a6 a1b1 h1g1 e2f1 f3g1 f3h4 d7f8 d7b8 " +
   237  		"e1f1 e1d1", moves.StringUci())
   238  	moves.Clear()
   239  
   240  	// 218
   241  	pos, _ = position.NewPositionFen("R6R/3Q4/1Q4Q1/4Q3/2Q4Q/Q4Q2/pp1Q4/kBNN1KB1 w - -")
   242  	for move := mg.GetNextMove(pos, GenAll, false); move != MoveNone; move = mg.GetNextMove(pos, GenAll, false) {
   243  		moves.PushBack(move)
   244  	}
   245  	assert.Equal(t, 218, moves.Len())
   246  	moves.Clear()
   247  
   248  	// 86
   249  	pos, _ = position.NewPositionFen("r3k2r/1ppn3p/2q1q1n1/4P3/2q1Pp2/B5R1/pbp2PPP/1R4K1 b kq e3")
   250  	for move := mg.GetNextMove(pos, GenAll, false); move != MoveNone; move = mg.GetNextMove(pos, GenAll, false) {
   251  		moves.PushBack(move)
   252  	}
   253  	assert.Equal(t, 86, moves.Len())
   254  	assert.Equal(t, "c2b1Q a2b1Q a2a1Q c2c1Q c2b1N a2b1N f4g3 a2a1N c2c1N f4e3 c2b1R a2b1R c2b1B a2b1B b2a3 a8a3 g6e5 d7e5 b2e5 e6e5 c4e4 c6e4 f4f3 h7h6 b7b5 h7h5 b7b6 a2a1R c2c1R a2a1B c2c1B e8g8 e8c8 d7c5 a8c8 a8d8 h8f8 d7f6 b2d4 g6e7 d7b6 b2c3 c4c5 c4d5 c6c5 c6d5 c6d6 e6d5 e6f5 e6d6 e6f6 e6e7 e6f7 c4d4 a8a4 a8a5 a8a6 a8a7 c4e2 c4b3 c4c3 c4d3 c4b4 c4b5 c6b5 c6b6 e6g4 c4a4 c6a4 b2c1 a8b8 h8g8 c4f1 c4a6 c6a6 e6h3 e6g8 g6f8 d7f8 b2a1 d7b8 g6h4 e8f8 e8e7 e8f7 e8d8", moves.StringUci())
   255  	moves.Clear()
   256  
   257  	config.Settings.Search.UsePromNonQuiet = false
   258  
   259  	// 86
   260  	mg.ResetOnDemand()
   261  	pos, _ = position.NewPositionFen("r3k2r/1ppn3p/2q1q1n1/4P3/2q1Pp2/B5R1/pbp2PPP/1R4K1 b kq e3")
   262  	for move := mg.GetNextMove(pos, GenAll, false); move != MoveNone; move = mg.GetNextMove(pos, GenAll, false) {
   263  		moves.PushBack(move)
   264  	}
   265  	assert.Equal(t, 86, moves.Len())
   266  	assert.Equal(t, "c2b1Q a2b1Q c2b1N a2b1N f4g3 f4e3 c2b1R a2b1R c2b1B a2b1B b2a3 a8a3 g6e5 d7e5 b2e5 e6e5 c4e4 c6e4 a2a1Q c2c1Q a2a1N c2c1N f4f3 h7h6 b7b5 h7h5 b7b6 a2a1R c2c1R a2a1B c2c1B e8g8 e8c8 d7c5 a8c8 a8d8 h8f8 d7f6 b2d4 g6e7 d7b6 b2c3 c4c5 c4d5 c6c5 c6d5 c6d6 e6d5 e6f5 e6d6 e6f6 e6e7 e6f7 c4d4 a8a4 a8a5 a8a6 a8a7 c4e2 c4b3 c4c3 c4d3 c4b4 c4b5 c6b5 c6b6 e6g4 c4a4 c6a4 b2c1 a8b8 h8g8 c4f1 c4a6 c6a6 e6h3 e6g8 g6f8 d7f8 b2a1 d7b8 g6h4 e8f8 e8e7 e8f7 e8d8", moves.StringUci())
   267  	moves.Clear()
   268  
   269  }
   270  
   271  func TestMovegenGeneratePseudoLegalMoves(t *testing.T) {
   272  
   273  	mg := NewMoveGen()
   274  
   275  	pos := position.NewPosition()
   276  	moves := mg.GeneratePseudoLegalMoves(pos, GenAll, false)
   277  	assert.Equal(t, 20, len(*moves))
   278  	assert.Equal(t, "d2d4 e2e4 b1c3 g1f3 a2a3 h2h3 a2a4 b2b4 c2c4 f2f4 g2g4 h2h4 d2d3 e2e3 b2b3 g2g3 c2c3 " +
   279  		"f2f3 b1a3 g1h3", moves.StringUci())
   280  	// l := mg.pseudoLegalMoves.Len()
   281  	// for i := 0; i < l; i++ {
   282  	// 	fmt.Printf("%d. %s\n", i+1, moves.At(i).String())
   283  	// }
   284  	moves.Clear()
   285  
   286  	pos, _ = position.NewPositionFen("r3k2r/pbpNqppp/1pn2n2/1B2p3/1b2P3/2PP1N2/PP1nQPPP/R3K2R w KQkq -")
   287  	moves = mg.GeneratePseudoLegalMoves(pos, GenAll, false)
   288  	assert.Equal(t, 40, len(*moves))
   289  	assert.Equal(t, "c3b4 d7f6 f3d2 b5c6 f3e5 d7e5 d7b6 e2d2 e1d2 e1g1 e1c1 d3d4 f3d4 d7c5 a1c1 a1d1 h1f1 " +
   290  		"b5c4 a2a3 h2h3 f3g5 e2e3 a2a4 g2g4 h2h4 c3c4 e1f1 b2b3 g2g3 e2d1 b5a4 b5a6 a1b1 h1g1 e2f1 e1d1 f3g1 f3h4 " +
   291  		"d7f8 d7b8", moves.StringUci())
   292  	// l = mg.pseudoLegalMoves.Len()
   293  	// for i := 0; i < l; i++ {
   294  	// 	fmt.Printf("%d. %s\n", i+1, moves.At(i).String())
   295  	// }
   296  	moves.Clear()
   297  
   298  	// 86 moves
   299  	pos, _ = position.NewPositionFen("r3k2r/1ppn3p/2q1q1n1/4P3/2q1Pp2/B5R1/pbp2PPP/1R4K1 b kq e3")
   300  	moves = mg.GeneratePseudoLegalMoves(pos, GenAll, false)
   301  	assert.Equal(t, 86, len(*moves))
   302  	moves.Clear()
   303  
   304  	// 218 moves
   305  	pos, _ = position.NewPositionFen("R6R/3Q4/1Q4Q1/4Q3/2Q4Q/Q4Q2/pp1Q4/kBNN1KB1 w - -")
   306  	moves = mg.GeneratePseudoLegalMoves(pos, GenAll, false)
   307  	assert.Equal(t, 218, len(*moves))
   308  	moves.Clear()
   309  }
   310  
   311  func TestMovegenGenerateLegalMoves(t *testing.T) {
   312  	config.Settings.Search.UsePromNonQuiet = false
   313  
   314  	mg := NewMoveGen()
   315  
   316  	pos := position.NewPosition()
   317  	moves := mg.GenerateLegalMoves(pos, GenAll)
   318  	assert.Equal(t, 20, len(*moves))
   319  	assert.Equal(t, "d2d4 e2e4 b1c3 g1f3 a2a3 h2h3 a2a4 b2b4 c2c4 f2f4 g2g4 h2h4 d2d3 e2e3 b2b3 g2g3 c2c3 " +
   320  		"f2f3 b1a3 g1h3", moves.StringUci())
   321  	moves.Clear()
   322  
   323  	pos, _ = position.NewPositionFen("r3k2r/pbpNqppp/1pn2n2/1B2p3/1b2P3/2PP1N2/PP1nQPPP/R3K2R w KQkq -")
   324  	moves = mg.GenerateLegalMoves(pos, GenAll)
   325  	assert.Equal(t, 38, len(*moves))
   326  	assert.Equal(t, "c3b4 d7f6 f3d2 b5c6 f3e5 d7e5 d7b6 e2d2 e1d2 e1c1 d3d4 f3d4 d7c5 a1c1 a1d1 h1f1 b5c4 " +
   327  		"a2a3 h2h3 f3g5 e2e3 a2a4 g2g4 h2h4 c3c4 b2b3 g2g3 e2d1 b5a4 b5a6 a1b1 h1g1 e2f1 e1d1 f3g1 f3h4 d7f8 d7b8",
   328  		moves.StringUci())
   329  	// l = mg.pseudoLegalMoves.Len()
   330  	// for i := 0; i < l; i++ {
   331  	// 	fmt.Printf("%d. %s\n", i+1, moves.At(i).String())
   332  	// }
   333  	moves.Clear()
   334  
   335  	// 86 moves
   336  	pos, _ = position.NewPositionFen("r3k2r/1ppn3p/2q1q1n1/4P3/2q1Pp2/B5R1/pbp2PPP/1R4K1 b kq e3")
   337  	moves = mg.GenerateLegalMoves(pos, GenAll)
   338  	assert.Equal(t, 83, len(*moves))
   339  	assert.Equal(t, "c2b1Q a2b1Q c2b1N a2b1N f4g3 f4e3 b2a3 a8a3 g6e5 d7e5 b2e5 e6e5 c4e4 c6e4 c2b1R a2b1R c2b1B a2b1B e8c8 a2a1Q c2c1Q a2a1N c2c1N d7c5 a8c8 a8d8 h8f8 d7f6 b2d4 f4f3 h7h6 g6e7 d7b6 b2c3 c4c5 c4d5 c6c5 c6d5 c6d6 e6d5 e6f5 e6d6 e6f6 e6e7 e6f7 c4d4 b7b5 h7h5 a8a4 a8a5 a8a6 a8a7 c4e2 c4b3 c4c3 c4d3 c4b4 c4b5 c6b5 c6b6 e6g4 b7b6 c4a4 c6a4 b2c1 a8b8 h8g8 c4f1 c4a6 c6a6 e6h3 e6g8 e8f7 e8d8 g6f8 d7f8 b2a1 d7b8 g6h4 a2a1R c2c1R a2a1B c2c1B", moves.StringUci())
   340  	moves.Clear()
   341  
   342  	// 218 moves
   343  	pos, _ = position.NewPositionFen("R6R/3Q4/1Q4Q1/4Q3/2Q4Q/Q4Q2/pp1Q4/kBNN1KB1 w - -")
   344  	moves = mg.GenerateLegalMoves(pos, GenAll)
   345  	assert.Equal(t, 218, len(*moves))
   346  	moves.Clear()
   347  }
   348  
   349  func TestHasLegalMoves(t *testing.T) {
   350  
   351  	mg := NewMoveGen()
   352  
   353  	// check mate position
   354  	pos, _ := position.NewPositionFen("rn2kbnr/pbpp1ppp/8/1p2p1q1/4K3/3P4/PPP1PPPP/RNBQ1BNR w kq -")
   355  	assert.False(t, mg.HasLegalMove(pos))
   356  	assert.True(t, pos.HasCheck())
   357  
   358  	// stale mate position
   359  	pos, _ = position.NewPositionFen("7k/5K2/6Q1/8/8/8/8/8 b - -")
   360  	assert.False(t, mg.HasLegalMove(pos))
   361  	assert.False(t, pos.HasCheck())
   362  
   363  	// only en passant
   364  	pos, _ = position.NewPositionFen("8/8/8/8/5Pp1/6P1/7k/K3BQ2 b - f3")
   365  	assert.True(t, mg.HasLegalMove(pos))
   366  	assert.False(t, pos.HasCheck())
   367  }
   368  
   369  func TestMovegenGetMoveFromUci(t *testing.T) {
   370  
   371  	pos, _ := position.NewPositionFen("r3k2r/1ppn3p/2q1q1n1/4P3/2q1Pp2/B5R1/pbp2PPP/1R4K1 b kq e3")
   372  	mg := NewMoveGen()
   373  
   374  	// invalid pattern
   375  	move := mg.GetMoveFromUci(pos, "8888")
   376  	assert.Equal(t, MoveNone, move)
   377  
   378  	// valid move
   379  	move = mg.GetMoveFromUci(pos, "b7b5")
   380  	assert.Equal(t, CreateMove(SqB7, SqB5, Normal, PtNone), move)
   381  
   382  	// invalid move
   383  	move = mg.GetMoveFromUci(pos, "a7a5")
   384  	assert.Equal(t, MoveNone, move)
   385  
   386  	// valid promotion
   387  	move = mg.GetMoveFromUci(pos, "a2a1Q")
   388  	assert.Equal(t, CreateMove(SqA2, SqA1, Promotion, Queen), move)
   389  
   390  	// valid promotion (we allow lower case promotions)
   391  	move = mg.GetMoveFromUci(pos, "a2a1q")
   392  	assert.Equal(t, CreateMove(SqA2, SqA1, Promotion, Queen), move)
   393  
   394  	// valid castling
   395  	move = mg.GetMoveFromUci(pos, "e8c8")
   396  	assert.Equal(t, CreateMove(SqE8, SqC8, Castling, PtNone), move)
   397  
   398  	// invalid castling
   399  	move = mg.GetMoveFromUci(pos, "e8g8")
   400  	assert.Equal(t, MoveNone, move)
   401  }
   402  
   403  func TestMovegenGetMoveFromSan(t *testing.T) {
   404  
   405  	pos, _ := position.NewPositionFen("r3k2r/1ppn3p/2q1q1n1/4P3/2q1Pp2/B5R1/pbp2PPP/1R4K1 b kq e3")
   406  	mg := NewMoveGen()
   407  
   408  	// invalid pattern
   409  	move := mg.GetMoveFromSan(pos, "33")
   410  	assert.Equal(t, MoveNone, move)
   411  
   412  	// valid move
   413  	move = mg.GetMoveFromSan(pos, "b5")
   414  	assert.Equal(t, CreateMove(SqB7, SqB5, Normal, PtNone), move)
   415  
   416  	// invalid move
   417  	move = mg.GetMoveFromSan(pos, "a5")
   418  	assert.Equal(t, MoveNone, move)
   419  
   420  	// valid promotion
   421  	move = mg.GetMoveFromSan(pos, "a1Q")
   422  	assert.Equal(t, CreateMove(SqA2, SqA1, Promotion, Queen), move)
   423  
   424  	// valid promotion (we allow lower case promotions)
   425  	move = mg.GetMoveFromSan(pos, "a1q")
   426  	assert.Equal(t, MoveNone, move)
   427  
   428  	// valid castling
   429  	move = mg.GetMoveFromSan(pos, "O-O-O")
   430  	assert.Equal(t, CreateMove(SqE8, SqC8, Castling, PtNone), move)
   431  
   432  	// invalid castling
   433  	move = mg.GetMoveFromSan(pos, "O-O")
   434  	assert.Equal(t, MoveNone, move)
   435  
   436  	// ambiguous
   437  	move = mg.GetMoveFromSan(pos, "Ne5")
   438  	assert.Equal(t, MoveNone, move)
   439  	move = mg.GetMoveFromSan(pos, "Nde5")
   440  	assert.Equal(t, CreateMove(SqD7, SqE5, Normal, PtNone), move)
   441  	move = mg.GetMoveFromSan(pos, "Nge5")
   442  	assert.Equal(t, CreateMove(SqG6, SqE5, Normal, PtNone), move)
   443  	move = mg.GetMoveFromSan(pos, "N7e5")
   444  	assert.Equal(t, CreateMove(SqD7, SqE5, Normal, PtNone), move)
   445  	move = mg.GetMoveFromSan(pos, "N6e5")
   446  	assert.Equal(t, CreateMove(SqG6, SqE5, Normal, PtNone), move)
   447  	move = mg.GetMoveFromSan(pos, "ab1Q")
   448  	assert.Equal(t, CreateMove(SqA2, SqB1, Promotion, Queen), move)
   449  	move = mg.GetMoveFromSan(pos, "cb1Q")
   450  	assert.Equal(t, CreateMove(SqC2, SqB1, Promotion, Queen), move)
   451  }
   452  
   453  func TestOnDemandKillerPv(t *testing.T) {
   454  	config.Settings.Search.UsePromNonQuiet = false
   455  
   456  	mg := NewMoveGen()
   457  	var moves = moveslice.NewMoveSlice(100)
   458  
   459  	// 86
   460  	pos, _ := position.NewPositionFen("r3k2r/1ppn3p/2q1q1n1/4P3/2q1Pp2/B5R1/pbp2PPP/1R4K1 b kq e3")
   461  	mg.StoreKiller(mg.GetMoveFromUci(pos, "g6h4"))
   462  	mg.StoreKiller(mg.GetMoveFromUci(pos, "b7b6"))
   463  	mg.SetPvMove(mg.GetMoveFromUci(pos, "a2b1Q")) // changes c2b1Q a2b1Q to a2b1Q c2b1Q
   464  	for move := mg.GetNextMove(pos, GenAll, false); move != MoveNone; move = mg.GetNextMove(pos, GenAll, false) {
   465  		moves.PushBack(move)
   466  	}
   467  	assert.Equal(t, 86, moves.Len())
   468  	assert.Equal(t, "a2b1Q c2b1Q c2b1N a2b1N f4g3 f4e3 c2b1R a2b1R c2b1B a2b1B b2a3 a8a3 g6e5 d7e5 b2e5 e6e5 c4e4 c6e4 b7b6 a2a1Q c2c1Q a2a1N c2c1N f4f3 h7h6 b7b5 h7h5 a2a1R c2c1R a2a1B c2c1B e8g8 e8c8 g6h4 d7c5 a8c8 a8d8 h8f8 d7f6 b2d4 b2c3 c4c5 c4d5 c6c5 c6d5 c6d6 e6d5 e6f5 e6d6 e6f6 g6e7 d7b6 e6e7 e6f7 c4d4 a8a4 a8a5 a8a6 a8a7 c4e2 c4b3 c4c3 c4d3 c4b4 c4b5 c6b5 c6b6 e6g4 c4a4 c6a4 a8b8 h8g8 b2c1 c4f1 c4a6 c6a6 e6h3 e6g8 g6f8 d7f8 b2a1 d7b8 e8f8 e8e7 e8f7 e8d8", moves.StringUci())
   469  	moves.Clear()
   470  
   471  	// 48 kiwipete
   472  	pos, _ = position.NewPositionFen("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - ")
   473  	mg.StoreKiller(mg.GetMoveFromUci(pos, "d2g5"))
   474  	mg.StoreKiller(mg.GetMoveFromUci(pos, "b2b3"))
   475  	mg.SetPvMove(mg.GetMoveFromUci(pos, "e2a6"))
   476  	for move := mg.GetNextMove(pos, GenAll, false); move != MoveNone; move = mg.GetNextMove(pos, GenAll, false) {
   477  		moves.PushBack(move)
   478  	}
   479  	assert.Equal(t, 48, moves.Len())
   480  	assert.Equal(t, "e2a6 g2h3 d5e6 e5g6 e5d7 e5f7 f3f6 f3h3 b2b3 a2a3 d5d6 a2a4 g2g4 g2g3 e1g1 e1c1 d2g5 " +
   481  		"e5d3 e5c4 a1c1 a1d1 h1f1 e5c6 d2e3 d2f4 e2d3 e2c4 c3b5 e2b5 f3d3 f3e3 f3f4 f3f5 e5g4 f3g3 f3g4 f3h5 d2h6 " +
   482  		"e2d1 a1b1 h1g1 c3b1 c3d1 c3a4 d2c1 e2f1 e1f1 e1d1", moves.StringUci())
   483  	moves.Clear()
   484  
   485  }
   486  
   487  func TestPseudoLegalPVKiller(t *testing.T) {
   488  	config.Settings.Search.UsePromNonQuiet = false
   489  
   490  	mg := NewMoveGen()
   491  	var moves = moveslice.NewMoveSlice(100)
   492  
   493  	// 86
   494  	pos, _ := position.NewPositionFen("r3k2r/1ppn3p/2q1q1n1/4P3/2q1Pp2/B5R1/pbp2PPP/1R4K1 b kq e3")
   495  	mg.SetPvMove(mg.GetMoveFromUci(pos, "a2b1Q")) // changes c2b1Q a2b1Q to a2b1Q c2b1Q
   496  	mg.StoreKiller(mg.GetMoveFromUci(pos, "g6h4"))
   497  	mg.StoreKiller(mg.GetMoveFromUci(pos, "b7b6"))
   498  	moves = mg.GeneratePseudoLegalMoves(pos, GenAll, false)
   499  	assert.Equal(t, 86, moves.Len())
   500  	assert.Equal(t, "a2b1Q c2b1Q c2b1N a2b1N f4g3 f4e3 b2a3 a8a3 g6e5 d7e5 b2e5 e6e5 c4e4 c6e4 c2b1R a2b1R c2b1B a2b1B b7b6 g6h4 e8g8 e8c8 a2a1Q c2c1Q a2a1N c2c1N d7c5 f4f3 a8c8 a8d8 h8f8 d7f6 b2d4 h7h6 b2c3 c4c5 c4d5 c6c5 c6d5 c6d6 e6d5 e6f5 e6d6 e6f6 g6e7 d7b6 e6e7 e6f7 c4d4 b7b5 h7h5 a8a4 a8a5 a8a6 a8a7 c4e2 c4b3 c4c3 c4d3 c4b4 c4b5 c6b5 c6b6 e6g4 e8f8 c4a4 c6a4 a8b8 h8g8 b2c1 c4f1 c4a6 c6a6 e6h3 e6g8 e8e7 e8f7 g6f8 d7f8 b2a1 e8d8 d7b8 a2a1R c2c1R a2a1B c2c1B", moves.StringUci())
   501  	moves.Clear()
   502  
   503  	// 48 kiwipete
   504  	pos, _ = position.NewPositionFen("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - ")
   505  	mg.SetPvMove(mg.GetMoveFromUci(pos, "e2a6"))
   506  	mg.StoreKiller(mg.GetMoveFromUci(pos, "d2g5"))
   507  	mg.StoreKiller(mg.GetMoveFromUci(pos, "b2b3"))
   508  	moves = mg.GeneratePseudoLegalMoves(pos, GenAll, false)
   509  	assert.Equal(t, 48, moves.Len())
   510  	assert.Equal(t, "e2a6 g2h3 d5e6 e5g6 e5d7 e5f7 f3f6 f3h3 b2b3 d2g5 e1g1 e1c1 e5d3 e5c4 a1c1 a1d1 h1f1 "+
   511  		"e5c6 d2e3 d2f4 e2d3 e2c4 a2a3 d5d6 c3b5 e2b5 f3d3 f3e3 f3f4 f3f5 a2a4 g2g4 e1f1 e5g4 f3g3 f3g4 g2g3 f3h5 " +
   512  		"d2h6 e2d1 a1b1 h1g1 e1d1 c3b1 c3d1 c3a4 d2c1 e2f1", moves.StringUci())
   513  	moves.Clear()
   514  
   515  }
   516  
   517  func TestEvasion(t *testing.T) {
   518  	mg := NewMoveGen()
   519  	var p *position.Position
   520  	var pseudoLegalMoves, evasionMoves, legalMoves *moveslice.MoveSlice
   521  
   522  	p = position.NewPosition("r3k2r/1pp4p/2q1qNn1/3nP3/2q1Pp2/B5R1/pbp2PPP/1R4K1 b kq -")
   523  	pseudoLegalMoves = mg.GeneratePseudoLegalMoves(p, GenAll, false).Clone()
   524  	evasionMoves = mg.GeneratePseudoLegalMoves(p, GenAll, true).Clone()
   525  	legalMoves = mg.GenerateLegalMoves(p, GenAll).Clone()
   526  	out.Printf("PseudoLegal: %3d %s\n", pseudoLegalMoves.Len(), pseudoLegalMoves.StringUci())
   527  	out.Printf("Evasion    : %3d %s\n", evasionMoves.Len(), evasionMoves.StringUci())
   528  	out.Printf("Legal      : %3d %s\n", legalMoves.Len(), legalMoves.StringUci())
   529  	out.Println()
   530  
   531  	p = position.NewPosition("5k2/8/8/8/8/8/6p1/3K1R2 b - -")
   532  	pseudoLegalMoves = mg.GeneratePseudoLegalMoves(p, GenAll, false).Clone()
   533  	evasionMoves = mg.GeneratePseudoLegalMoves(p, GenAll, true).Clone()
   534  	legalMoves = mg.GenerateLegalMoves(p, GenAll).Clone()
   535  	out.Printf("PseudoLegal: %3d %s\n", pseudoLegalMoves.Len(), pseudoLegalMoves.StringUci())
   536  	out.Printf("Evasion    : %3d %s\n", evasionMoves.Len(), evasionMoves.StringUci())
   537  	out.Printf("Legal      : %3d %s\n", legalMoves.Len(), legalMoves.StringUci())
   538  	out.Println()
   539  
   540  	p = position.NewPosition("5k2/8/8/8/8/6p1/5R2/3K4 b - -")
   541  	pseudoLegalMoves = mg.GeneratePseudoLegalMoves(p, GenAll, false).Clone()
   542  	evasionMoves = mg.GeneratePseudoLegalMoves(p, GenAll, true).Clone()
   543  	legalMoves = mg.GenerateLegalMoves(p, GenAll).Clone()
   544  	out.Printf("PseudoLegal: %3d %s\n", pseudoLegalMoves.Len(), pseudoLegalMoves.StringUci())
   545  	out.Printf("Evasion    : %3d %s\n", evasionMoves.Len(), evasionMoves.StringUci())
   546  	out.Printf("Legal      : %3d %s\n", legalMoves.Len(), legalMoves.StringUci())
   547  	out.Println()
   548  
   549  	p = position.NewPosition("8/8/8/3k4/4Pp2/8/8/3K4 b - e3")
   550  	pseudoLegalMoves = mg.GeneratePseudoLegalMoves(p, GenAll, false).Clone()
   551  	evasionMoves = mg.GeneratePseudoLegalMoves(p, GenAll, true).Clone()
   552  	legalMoves = mg.GenerateLegalMoves(p, GenAll).Clone()
   553  	out.Printf("PseudoLegal: %3d %s\n", pseudoLegalMoves.Len(), pseudoLegalMoves.StringUci())
   554  	out.Printf("Evasion    : %3d %s\n", evasionMoves.Len(), evasionMoves.StringUci())
   555  	out.Printf("Legal      : %3d %s\n", legalMoves.Len(), legalMoves.StringUci())
   556  	out.Println()
   557  
   558  	p = position.NewPosition("8/8/8/3k2n1/8/8/6B1/3K4 b - -")
   559  	pseudoLegalMoves = mg.GeneratePseudoLegalMoves(p, GenAll, false).Clone()
   560  	evasionMoves = mg.GeneratePseudoLegalMoves(p, GenAll, true).Clone()
   561  	legalMoves = mg.GenerateLegalMoves(p, GenAll).Clone()
   562  	out.Printf("PseudoLegal: %3d %s\n", pseudoLegalMoves.Len(), pseudoLegalMoves.StringUci())
   563  	out.Printf("Evasion    : %3d %s\n", evasionMoves.Len(), evasionMoves.StringUci())
   564  	out.Printf("Legal      : %3d %s\n", legalMoves.Len(), legalMoves.StringUci())
   565  	out.Println()
   566  
   567  	p = position.NewPosition("5k2/3N4/8/8/8/8/6p1/3K1R2 b - - 1 1 ")
   568  	pseudoLegalMoves = mg.GeneratePseudoLegalMoves(p, GenAll, false).Clone()
   569  	evasionMoves = mg.GeneratePseudoLegalMoves(p, GenAll, true).Clone()
   570  	legalMoves = mg.GenerateLegalMoves(p, GenAll).Clone()
   571  	out.Printf("PseudoLegal: %3d %s\n", pseudoLegalMoves.Len(), pseudoLegalMoves.StringUci())
   572  	out.Printf("Evasion    : %3d %s\n", evasionMoves.Len(), evasionMoves.StringUci())
   573  	out.Printf("Legal      : %3d %s\n", legalMoves.Len(), legalMoves.StringUci())
   574  	out.Println()
   575  }
   576  
   577  func TestTimingPseudoMoveGen(t *testing.T) {
   578  
   579  	if testing.Short() {
   580  		t.Skip("skipping test in short mode.")
   581  	}
   582  
   583  	const rounds = 5
   584  	const iterations uint64 = 1_000_000
   585  
   586  	mg := NewMoveGen()
   587  	// kiwi pete
   588  	pos := position.NewPosition("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - ")
   589  	moves := mg.GeneratePseudoLegalMoves(pos, GenAll, false)
   590  
   591  	for r := 1; r <= rounds; r++ {
   592  		out.Printf("Round %d\n", r)
   593  		start := time.Now()
   594  		for i := uint64(0); i < iterations; i++ {
   595  			moves.Clear()
   596  			moves = mg.GeneratePseudoLegalMoves(pos, GenAll, false)
   597  		}
   598  		elapsed := time.Since(start)
   599  		out.Printf("GeneratePseudoLegalMoves took %d ns for %d iterations\n", elapsed.Nanoseconds(), iterations)
   600  		generated := uint64(len(*moves)) * iterations
   601  		out.Printf("%d moves generated in %d ns: %d mps\n",
   602  			generated,
   603  			elapsed.Nanoseconds()/int64(iterations),
   604  			(generated*uint64(1_000_000_000))/uint64(elapsed.Nanoseconds()))
   605  	}
   606  }
   607  
   608  func TestTimingOnDemandMoveGen(t *testing.T) {
   609  
   610  	if testing.Short() {
   611  		t.Skip("skipping test in short mode.")
   612  	}
   613  
   614  	const rounds = 5
   615  	const iterations uint64 = 1_000_000
   616  
   617  	mg := NewMoveGen()
   618  	pos, _ := position.NewPositionFen("r3k2r/1ppn3p/2q1q1n1/4P3/2q1Pp2/B5R1/pbp2PPP/1R4K1 b kq e3")
   619  
   620  	for r := 1; r <= rounds; r++ {
   621  		out.Printf("Round %d\n", r)
   622  		start := time.Now()
   623  		generated := uint64(0)
   624  		for i := uint64(0); i < iterations; i++ {
   625  			generated = 0
   626  			mg.ResetOnDemand()
   627  			for move := mg.GetNextMove(pos, GenAll, false); move != MoveNone; move = mg.GetNextMove(pos, GenAll, false) {
   628  				generated++
   629  			}
   630  		}
   631  		elapsed := time.Since(start)
   632  		out.Printf("GeneratePseudoLegalMoves took %d ns for %d iterations\n", elapsed.Nanoseconds(), iterations)
   633  		generated = generated * iterations
   634  		out.Printf("%d moves generated in %d ns: %d mps\n",
   635  			generated,
   636  			elapsed.Nanoseconds()/int64(iterations),
   637  			(generated*uint64(1_000_000_000))/uint64(elapsed.Nanoseconds()))
   638  	}
   639  }
   640  
   641  // GeneratePseudoLegalMoves took 1.655.000.800 ns for 1.000.000 iterations
   642  // 86.000.000 moves generated in 1.655 ns: 51.963.721 mps.
   643  func TestTimingOnDemandRealMoveGen(t *testing.T) {
   644  	if testing.Short() {
   645  		t.Skip("skipping test in short mode.")
   646  	}
   647  
   648  	const rounds = 5
   649  	const iterations uint64 = 1_000_000
   650  
   651  	mg := NewMoveGen()
   652  	pos, _ := position.NewPositionFen("r3k2r/1ppn3p/2q1q1n1/4P3/2q1Pp2/B5R1/pbp2PPP/1R4K1 b kq e3")
   653  	k1 := mg.GetMoveFromUci(pos, "g6h4")
   654  	k2 := mg.GetMoveFromUci(pos, "b7b6")
   655  	pv := mg.GetMoveFromUci(pos, "a2b1Q")
   656  
   657  	for r := 1; r <= rounds; r++ {
   658  		out.Printf("Round %d\n", r)
   659  		start := time.Now()
   660  		generated := uint64(0)
   661  		for i := uint64(0); i < iterations; i++ {
   662  			generated = 0
   663  			mg.ResetOnDemand()
   664  			mg.StoreKiller(k1)
   665  			mg.StoreKiller(k2)
   666  			mg.SetPvMove(pv)
   667  			for move := mg.GetNextMove(pos, GenAll, false); move != MoveNone; move = mg.GetNextMove(pos, GenAll, false) {
   668  				generated++
   669  			}
   670  		}
   671  		elapsed := time.Since(start)
   672  		out.Printf("GeneratePseudoLegalMoves took %d ns for %d iterations\n", elapsed.Nanoseconds(), iterations)
   673  		generated = generated * iterations
   674  		out.Printf("%d moves generated in %d ns: %d mps\n",
   675  			generated,
   676  			elapsed.Nanoseconds()/int64(iterations),
   677  			(generated*uint64(1_000_000_000))/uint64(elapsed.Nanoseconds()))
   678  	}
   679  }
   680  
   681  func TestTimingGenerateMovesNew(t *testing.T) {
   682  	// defer profile.Start(profile.CPUProfile, profile.ProfilePath(".")).Stop()
   683  	// go tool pprof -http :8080 ./main ./prof.null/cpu.pprof
   684  
   685  	if testing.Short() {
   686  		t.Skip("skipping test in short mode.")
   687  	}
   688  
   689  	mg := NewMoveGen()
   690  	p := position.NewPosition("r1b1k2r/pppp1ppp/2n2n2/1Bb1p2q/4P3/2NP1N2/1PP2PPP/R1BQK2R w KQkq -")
   691  	result := Value(0)
   692  
   693  	const rounds = 5
   694  	const iterations uint64 = 10_000_000
   695  
   696  	for r := 1; r <= rounds; r++ {
   697  		out.Printf("Round %d\n", r)
   698  		start := time.Now()
   699  		for i := uint64(0); i < iterations; i++ {
   700  			mg.pseudoLegalMoves.Clear()
   701  			mg.generateMoves(p, GenAll, false, BbZero, mg.pseudoLegalMoves)
   702  		}
   703  		elapsed := time.Since(start)
   704  		out.Printf("Test took %s for %d iterations\n", elapsed, iterations)
   705  		out.Printf("Test took %d ns per iteration\n", elapsed.Nanoseconds()/int64(iterations))
   706  		out.Printf("Iterations per sec %d\n", int64(iterations*1e9)/elapsed.Nanoseconds())
   707  	}
   708  	_ = result
   709  }