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 }