A chess library for Gleam

Generate promotion moves

+35 -3
+2
src/starfish/internal/board.gleam
··· 23 23 King 24 24 } 25 25 26 + pub const pawn_promotions = [Bishop, Knight, Rook, Queen] 27 + 26 28 pub type Colour { 27 29 White 28 30 Black
+33 -3
src/starfish/internal/move.gleam
··· 90 90 position: Int, 91 91 moves: List(Move(Legal)), 92 92 ) -> List(Move(Legal)) { 93 - let #(forward, left, right) = case game.to_move { 94 - board.Black -> #(direction.down, direction.down_left, direction.down_right) 95 - board.White -> #(direction.up, direction.up_left, direction.up_right) 93 + let #(forward, left, right, promotion_rank) = case game.to_move { 94 + board.Black -> #( 95 + direction.down, 96 + direction.down_left, 97 + direction.down_right, 98 + 0, 99 + ) 100 + board.White -> #(direction.up, direction.up_left, direction.up_right, 7) 96 101 } 97 102 98 103 let forward_one = direction.in_direction(position, forward) 104 + // If moving forward on square is a promotion, then captured are also 105 + // promotions, because they must move to the same rank. A double-move can 106 + // never be a promotion because a pawn cannot double move from a position that 107 + // ends on the promotion rank. 108 + let is_promotion = board.rank(forward_one) == promotion_rank 109 + 99 110 let moves = case iv.get(game.board, forward_one) { 100 111 Ok(board.Empty) -> { 101 112 let moves = case 102 113 can_move(position, forward_one, game.attack_information) 103 114 { 104 115 False -> moves 116 + True if is_promotion -> 117 + add_promotions(position, forward_one, moves, board.pawn_promotions) 105 118 True -> [Move(from: position, to: forward_one), ..moves] 106 119 } 107 120 ··· 130 143 Ok(board.Occupied(colour:, ..)) if colour != game.to_move -> 131 144 case can_move(position, new_position, game.attack_information) { 132 145 False -> moves 146 + True if is_promotion -> 147 + add_promotions(position, new_position, moves, board.pawn_promotions) 133 148 True -> [Capture(from: position, to: new_position), ..moves] 134 149 } 135 150 Ok(board.Empty) if game.en_passant_square == Some(new_position) -> ··· 145 160 Ok(board.Occupied(colour:, ..)) if colour != game.to_move -> 146 161 case can_move(position, new_position, game.attack_information) { 147 162 False -> moves 163 + True if is_promotion -> 164 + add_promotions(position, new_position, moves, board.pawn_promotions) 148 165 True -> [Capture(from: position, to: new_position), ..moves] 149 166 } 150 167 Ok(board.Empty) if game.en_passant_square == Some(new_position) -> ··· 153 170 True -> [EnPassant(from: position, to: new_position), ..moves] 154 171 } 155 172 Ok(board.Empty) | Ok(board.Occupied(_, _)) | Error(_) -> moves 173 + } 174 + } 175 + 176 + fn add_promotions( 177 + from: Int, 178 + to: Int, 179 + moves: List(Move(Legal)), 180 + pieces: List(board.Piece), 181 + ) -> List(Move(Legal)) { 182 + case pieces { 183 + [] -> moves 184 + [piece, ..pieces] -> 185 + add_promotions(from, to, [Promotion(from:, to:, piece:), ..moves], pieces) 156 186 } 157 187 } 158 188