A chess library for Gleam
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

Continue searching until a quiet position is reached

+53 -3
+1 -1
README.md
··· 10 10 - [x] Implement a system to test the performance of the generated moves 11 11 - [x] Maybe have some way to perform a search using iterative deepening? 12 12 - [x] Order moves before searching via heuristics to improve alpha-beta pruning 13 - - [ ] Continue searching past regular depth when captures are available (https://www.chessprogramming.org/Quiescence_Search) 13 + - [x] Continue searching past regular depth when captures are available (https://www.chessprogramming.org/Quiescence_Search) 14 14 - [ ] Improve static evaluation using pawn structure 15 15 - [ ] Improve endgame play by encouraging the king to the centre of the board and encouraging pawns to promote 16 16 - [ ] Incrementally update game information such as zobrist hash, material count, and other evaluation information
+52 -2
src/starfish/internal/search.gleam
··· 211 211 } 212 212 moves -> 213 213 case depth { 214 - // Once we reach the limit of our depth, we statically evaluate the position. 214 + // Once we reach the limit of our depth, we continue searching until 215 + // we reach only quiet positions. 215 216 0 -> { 216 - let eval = evaluate.evaluate(game, moves) 217 + let eval = 218 + quiescent_search(game, moves, best_eval, best_opponent_move) 217 219 let cached_positions = 218 220 hash.cache( 219 221 cached_positions, ··· 430 432 431 433 position_improvement + move_specific_score 432 434 } 435 + 436 + /// Search until we find a "quiet" position, to avoid thinking a position is good 437 + /// while really on the next move a valuable piece could be captured. 438 + /// https://www.chessprogramming.org/Quiescence_Search 439 + fn quiescent_search( 440 + game: Game, 441 + moves: List(#(Move, Int)), 442 + best_eval: Int, 443 + best_opponent_move: Int, 444 + ) -> Int { 445 + let evaluation = evaluate.evaluate(game, moves) 446 + 447 + use <- bool.guard(evaluation >= best_opponent_move, evaluation) 448 + 449 + let best_eval = case evaluation > best_eval { 450 + True -> evaluation 451 + False -> best_eval 452 + } 453 + 454 + quiescent_search_loop(game, moves, best_eval, best_opponent_move) 455 + } 456 + 457 + fn quiescent_search_loop( 458 + game: Game, 459 + moves: List(#(Move, Int)), 460 + best_eval: Int, 461 + best_opponent_move: Int, 462 + ) -> Int { 463 + case moves { 464 + [] -> best_eval 465 + // We don't need to search quiet moves 466 + [#(move.Move(..), _), ..moves] | [#(move.Castle(..), _), ..moves] -> 467 + quiescent_search_loop(game, moves, best_eval, best_opponent_move) 468 + [#(move, _), ..moves] -> { 469 + let new_game = move.apply(game, move) 470 + let new_moves = order_moves(new_game) 471 + let evaluation = 472 + -quiescent_search(new_game, new_moves, -best_opponent_move, -best_eval) 473 + 474 + use <- bool.guard(evaluation >= best_opponent_move, evaluation) 475 + let best_eval = case evaluation > best_eval { 476 + True -> evaluation 477 + False -> best_eval 478 + } 479 + quiescent_search_loop(game, moves, best_eval, best_opponent_move) 480 + } 481 + } 482 + }