Add promotion and promotion checks for pawns

This commit is contained in:
Alex Clink 2021-09-20 23:20:59 -04:00
parent f96c207066
commit 3502140bdb
4 changed files with 54 additions and 3 deletions

View file

@ -0,0 +1,32 @@
require "../../spec_helper"
require "../../../src/lx_chess/board"
require "../../../src/lx_chess/piece"
require "../../../src/lx_chess/move_set"
require "../../../src/lx_chess/game"
include LxChess
describe Game do
describe "#make_move" do
context "when a move will promote" do
it "raises an exception when promotion is not specified" do
game = Game.new
game.board["e7"] = Piece.from_fen('P')
expect_raises(Game::IllegalMove) do
game.make_move("e7", "e8")
end
end
it "raises an exception when promotion is not specified" do
game = Game.new
game.board["e7"] = Piece.from_fen('P')
game.make_move("e7", "e8", 'Q')
piece = game.board["e8"]
from = game.board["e7"]
from.should be_nil
raise "e8 is empty" unless piece
piece.fen_symbol.should eq('Q')
end
end
end
end

View file

@ -126,7 +126,7 @@ module LxChess
end
# TODO: checkmate
def move_to_san(from : Int, to : Int, promotion : String? = nil, turn = @turn)
def move_to_san(from : Int, to : Int, promotion : Char? = nil, turn = @turn)
raise "No piece at #{@board.cord(from)}" unless piece = @board[from]
en_passant = piece.pawn? && to == @en_passant_target
@ -355,6 +355,7 @@ module LxChess
if piece.pawn?
distance = from - to
if distance.abs == @board.width * 2
# Double pawn push, set en passant target
@en_passant_target = distance > 0 ? to + @board.width : to - @board.width
else
if to == @en_passant_target
@ -368,6 +369,22 @@ module LxChess
@en_passant_target = nil
end
# Promotion
if piece.pawn?
rank = @board.rank(to)
if rank == 0 || rank == @board.height - 1
if promotion
raise IllegalMove.new("Cannot promote to #{promotion}") unless "RNBQ".chars.includes?(promotion.upcase)
promotion = piece.white? ? promotion.upcase : promotion.downcase
@board[from] = Piece.from_fen(promotion)
else
raise IllegalMove.new("Pawns must promote on the last rank")
end
elsif promotion
raise IllegalMove.new("Cannot promote on #{@board.cord(to)}")
end
end
@board.move(from, to)
next_turn!
@pgn.history << san
@ -408,6 +425,7 @@ module LxChess
# Get the next turn index
def next_turn
return 0.to_i8 if @players.size == 0
(@turn + 1) % @players.size
end
end

View file

@ -12,6 +12,7 @@ module LxChess
KNIGHT = 5
def self.from_fen(fen : Char)
raise "#{fen} is not a valid FEN symbol" unless FEN_SYMBOLS.chars.includes?(fen)
id = FEN_SYMBOLS.index(fen).as(Int32).to_i8
Piece.new(id)
end

View file

@ -71,7 +71,7 @@ module LxChess
end
if from && to
@gb.clear
san = @game.make_move(from, to)
san = @game.make_move(from, to, promo)
@gb.highlight([@game.board.index(from), @game.board.index(to)])
@log.unshift "#{san.to_s}: #{from} => #{to}"
end
@ -84,7 +84,7 @@ module LxChess
from, to = @game.parse_san(notation)
if from && to
@gb.clear
san = @game.make_move(from, to)
san = @game.make_move(from, to, notation.promotion)
@gb.highlight([from.to_i16, to.to_i16])
@log.unshift "#{san.to_s}: #{@game.board.cord(from)} => #{@game.board.cord(to)}"
end