Add disambiguation to SAN generation

This commit is contained in:
Alex Clink 2021-09-27 23:34:47 -04:00
parent d8c2a79733
commit 5427797139
6 changed files with 133 additions and 24 deletions

View file

@ -1,5 +1,5 @@
name: lx_chess
version: 0.1.0
version: 0.1.3
authors:
- Alex Clink <alexclink@gmail.com>

View file

@ -0,0 +1,100 @@
require "../../spec_helper"
include LxChess
describe Game do
describe "#move_to_san" do
it "converts pawn captures" do
game = Game.new players: [Player.new, Player.new]
place(game.board, {
"e4" => 'P',
"d5" => 'p',
})
debug_board(game, ["e4", "d5"])
san = game.move_to_san(from: "e4", to: "d5", turn: 0)
puts san.to_s
san.to_s.should eq("exd5")
end
it "converts pawn moves" do
game = Game.new players: [Player.new, Player.new]
place(game.board, {"e2" => 'P'})
debug_board(game, ["e2", "e4"])
san = game.move_to_san(from: "e2", to: "e4", turn: 0)
puts san.to_s
san.to_s.should eq("e4")
end
it "disambiguates pawn captures" do
game = Game.new players: [Player.new, Player.new]
place(game.board, {
"c4" => 'P',
"e4" => 'P',
"d5" => 'p',
})
debug_board(game, ["e4", "d5"])
san = game.move_to_san(from: "e4", to: "d5", turn: 0)
puts san.to_s
san.to_s.should eq("exd5")
debug_board(game, ["c4", "d5"])
san = game.move_to_san(from: "c4", to: "d5", turn: 0)
puts san.to_s
san.to_s.should eq("cxd5")
end
it "disambiguates rook moves" do
game = Game.new players: [Player.new, Player.new]
place(game.board, {
"e4" => 'R',
"d5" => 'R',
})
debug_board(game, ["e4", "e5"])
san = game.move_to_san(from: "e4", to: "e5", turn: 0)
puts san.to_s
san.to_s.should eq("Ree5")
end
it "disambiguates knight moves" do
game = Game.new players: [Player.new, Player.new]
place(game.board, {
"f3" => 'N',
"e2" => 'N',
})
debug_board(game, ["e2", "d4"])
san = game.move_to_san(from: "e2", to: "d4", turn: 0)
puts san.to_s
san.to_s.should eq("Ned4")
end
it "disambiguates knight moves on the same file" do
game = Game.new players: [Player.new, Player.new]
place(game.board, {
"e4" => 'N',
"e2" => 'N',
})
debug_board(game, ["e2", "c3"])
san = game.move_to_san(from: "e2", to: "c3", turn: 0)
puts san.to_s
san.to_s.should eq("Ne2c3")
end
it "detects check" do
game = Game.new players: [Player.new, Player.new]
game.board["e1"] = Piece.from_fen('K')
game.board["c8"] = Piece.from_fen('r')
debug_board(game, ["c8", "e8"])
san = game.move_to_san(from: "c8", to: "e8", turn: 1)
puts san.to_s
san.to_s.should eq("Re8+")
end
it "detects checkmate" do
fen = Fen.parse("r1bqkb1r/pppp1ppp/2n2n2/4p2Q/2B1P3/8/PPPP1PPP/RNB1K1NR w KQkq - 4 4")
game = Game.new(board: fen.board, players: [Player.new, Player.new])
san = game.move_to_san(from: "h5", to: "f7", turn: 0)
debug_board(game, ["h5", "f7"])
puts san.to_s
san.to_s.should eq("Qxf7#")
end
end
end

View file

@ -74,25 +74,6 @@ describe Game do
end
end
describe "#move_to_san" do
it "detects check" do
game = Game.new players: [Player.new, Player.new]
game.board["e1"] = Piece.from_fen('K')
game.board["c8"] = Piece.from_fen('r')
debug_board(game, ["c8", "e8"])
san = game.move_to_san(from: "c8", to: "e8", turn: 1)
san.to_s.should eq("Re8+")
end
it "detects checkmate" do
fen = Fen.parse("r1bqkb1r/pppp1ppp/2n2n2/4p2Q/2B1P3/8/PPPP1PPP/RNB1K1NR w KQkq - 4 4")
game = Game.new(board: fen.board, players: [Player.new, Player.new])
san = game.move_to_san(from: "h5", to: "f7", turn: 0)
debug_board(game, ["h5", "f7"])
san.to_s.should eq("Qxf7#")
end
end
describe "#in_check?" do
it "detects if the player is in check" do
game = Game.new players: [Player.new, Player.new]

View file

@ -175,18 +175,46 @@ module LxChess
end
end
candidate_move_sets = @board.select do |candidate|
next unless candidate
next if candidate == piece
candidate.fen_symbol == piece.fen_symbol
end.compact.map do |candidate|
moves(candidate.index)
end.compact.select do |move_set|
move_set.moves.includes?(to)
end
origin = nil
takes = en_passant || !@board[to].nil?
if candidate_move_sets.any?
origin ||= ""
origin += @board.cord(from)[0]
end
if candidate_move_sets.any? { |set| @board.cord(set.piece.index)[0] == @board.cord(from)[0] }
origin ||= ""
origin += @board.cord(from)[1]
end
if origin.nil? && piece.pawn? && takes
origin = @board.cord(from)[0].to_s
end
Notation.new(
square: @board.cord(to),
promotion: promotion,
piece_abbr: piece.fen_symbol,
from: @board.cord(from),
to: @board.cord(to),
takes: en_passant || !@board[to].nil?,
takes: takes,
en_passant: en_passant,
check: check && !checkmate,
checkmate: checkmate,
castles_k: castles_k,
castles_q: castles_q
castles_q: castles_q,
origin: origin
)
end

View file

@ -156,7 +156,7 @@ module LxChess
when castles_q?
"O-O-O"
else
piece_abbr != 'P' || @takes ? piece_abbr : nil
piece_abbr unless piece_abbr == 'P'
end
if @en_passant

View file

@ -1,3 +1,3 @@
module LxChess
VERSION = "0.1.2"
VERSION = "0.1.3"
end