···7777 #(20, 30, 10, 0, 0, 10, 30, 20),
7878)
79798080-/// In the beginning and middle of the game, the king must be kept safe, however
8080+/// In the beginning and middle of the game, the king must be kept safe. However
8181/// as the game progresses towards the end, the king should become more aggressive
8282/// so we use a different set of scores for kings in the endgame.
8383const king_endgame = #(
···9191 #(-50, -30, -30, -30, -30, -30, -30, -50),
9292)
93939494+/// In the middlegame, pawns are encouraged to protect the king's castling
9595+/// squares. In the endgame though, they no longer need to protect the king and
9696+/// instead should promote. Therefore, we use a different table to encourage this.
9797+const pawn_endgame = #(
9898+ #(100, 100, 100, 100, 100, 100, 100, 100),
9999+ #(80, 80, 80, 80, 80, 80, 80, 80),
100100+ #(50, 50, 50, 50, 50, 50, 50, 50),
101101+ #(30, 30, 30, 30, 30, 30, 30, 30),
102102+ #(10, 10, 10, 10, 10, 10, 10, 10),
103103+ // Since pawns can double-move, the first two ranks are equivalent from the
104104+ // pawn's perspective.
105105+ #(-10, -10, -10, -10, -10, -10, -10, -10),
106106+ #(-10, -10, -10, -10, -10, -10, -10, -10),
107107+ #(-10, -10, -10, -10, -10, -10, -10, -10),
108108+)
109109+94110type Table =
95111 #(
96112 #(Int, Int, Int, Int, Int, Int, Int, Int),
···140156 piece: board.Piece,
141157 colour: board.Colour,
142158 position: Int,
159159+ phase: Int,
143160) -> Int {
144161 let table = case piece {
145162 board.Pawn -> pawn
···150167 board.King -> king
151168 }
152169153153- // TODO: take into account endgame for kings
154154- get(table, position, colour)
170170+ let middlegame_value = get(table, position, colour)
171171+172172+ case piece {
173173+ board.King if phase > 0 ->
174174+ interpolate(middlegame_value, get(king_endgame, position, colour), phase)
175175+ board.Pawn if phase > 0 ->
176176+ interpolate(middlegame_value, get(pawn_endgame, position, colour), phase)
177177+ _ -> middlegame_value
178178+ }
179179+}
180180+181181+fn interpolate(middlegame_value: Int, endgame_value: Int, phase: Int) -> Int {
182182+ { middlegame_value * { 128 - phase } + endgame_value * phase } / 128
155183}
+11-6
src/starfish/internal/search.gleam
···345345/// in order to save iterating the list a second time. The guesses are discarded
346346/// after this point.
347347fn order_moves(game: Game) -> List(#(Move, Int)) {
348348+ let phase = evaluate.phase(game)
348349 game
349350 |> move.legal
350350- |> collect_guessed_eval(game, [])
351351+ |> collect_guessed_eval(game, phase, [])
351352 |> list.sort(fn(a, b) { int.compare(a.1, b.1) })
352353}
353354···367368fn collect_guessed_eval(
368369 moves: List(Move),
369370 game: Game,
371371+ phase: Int,
370372 acc: List(#(Move, Int)),
371373) -> List(#(Move, Int)) {
372374 case moves {
373375 [] -> acc
374376 [move, ..moves] ->
375375- collect_guessed_eval(moves, game, [#(move, guess_eval(game, move)), ..acc])
377377+ collect_guessed_eval(moves, game, phase, [
378378+ #(move, guess_eval(game, move, phase)),
379379+ ..acc
380380+ ])
376381 }
377382}
378383···382387/// Guess the evaluation of a move so we can hopefully search moves in a better
383388/// order than random. Searching better moves first improves alpha-beta pruning,
384389/// allowing us to search more positions.
385385-fn guess_eval(game: Game, move: Move) -> Int {
390390+fn guess_eval(game: Game, move: Move, phase: Int) -> Int {
386391 let assert board.Occupied(piece:, colour:) = board.get(game.board, move.from)
387392 as "Invalid move trying to move empty piece"
388393···392397 piece
393398 }
394399395395- let from_score = piece_table.piece_score(moving_piece, colour, move.from)
396396- let to_score = piece_table.piece_score(moving_piece, colour, move.to)
397397-400400+ let from_score = piece_table.piece_score(piece, colour, move.from, phase)
401401+ let to_score = piece_table.piece_score(moving_piece, colour, move.to, phase)
398402 let position_improvement = to_score - from_score
403403+399404 let move_specific_score = case move {
400405 // TODO store information in moves so we don't have to retrieve it from the
401406 // board every time.