Add pawn captures, FEN placement, board turns

This commit is contained in:
Alex Clink 2021-09-11 23:20:00 -04:00
parent 466075bee9
commit 279a9adaaa
6 changed files with 101 additions and 15 deletions

View file

@ -48,7 +48,7 @@ describe LxChess::Game do
end
end
it "correctly generates black white pawn moves" do
it "correctly generates black pawn moves" do
game = LxChess::Game.new
game.board["e6"] = LxChess::Piece.from_fen('p')
if move_set = game.moves("e6")
@ -59,6 +59,32 @@ describe LxChess::Game do
end
end
it "generates captures for white pawns" do
game = LxChess::Game.new
game.board["e4"] = LxChess::Piece.from_fen('P')
game.board["f5"] = LxChess::Piece.from_fen('p')
game.board["d5"] = LxChess::Piece.from_fen('p')
if move_set = game.moves("e4")
debug_board(game, move_set.moves)
move_set.moves.map { |m| game.board.cord(m) }.should eq(%w[e5 d5 f5])
else
raise "no moves"
end
end
it "does not generates captures pawns capturing own pieces" do
game = LxChess::Game.new
game.board["e4"] = LxChess::Piece.from_fen('P')
game.board["f5"] = LxChess::Piece.from_fen('P')
game.board["d5"] = LxChess::Piece.from_fen('P')
if move_set = game.moves("e4")
debug_board(game, move_set.moves)
move_set.moves.map { |m| game.board.cord(m) }.should eq(%w[e5])
else
raise "no moves"
end
end
it "correctly generates knight moves" do
game = LxChess::Game.new
game.board["c3"] = LxChess::Piece.from_fen('N')

View file

@ -31,27 +31,44 @@ OptionParser.parse do |parser|
end
end
log = [] of String
fen = LxChess::Fen.parse(options["fen_string"])
game = LxChess::Game.new(board: fen.board)
gb = LxChess::TermBoard.new(game.board)
term = LxChess::Terminal.new
loop do
term.move 0, 0
puts fen.placement
puts
gb.draw
puts
print " > "
puts
if game.turn == 0
print " #{game.full_moves + 1}. "
else
print " #{game.full_moves + 1}. ... "
end
term.trunc
input = gets
if input
notation = LxChess::Notation.new(input)
from, to = game.parse_san(notation)
if from && to
gb.clear
piece = game.board.move(from, to)
puts "#{notation.to_s}: #{game.board.cord(from)} => #{game.board.cord(to)}"
game.next_turn
gb.highlight([from.to_i16, to.to_i16])
log.unshift "#{notation.to_s}: #{game.board.cord(from)} => #{game.board.cord(to)}"
end
end
rescue e : LxChess::Notation::InvalidNotation
puts e.message
rescue e : LxChess::Game::SanError
puts e.message
rescue e : LxChess::Notation::InvalidNotation | LxChess::Game::SanError
if msg = e.message
log.unshift msg
end
ensure
puts
log.each { |l| puts l }
end
# gb.flip!

View file

@ -8,7 +8,7 @@ module LxChess
property :width, :height, :squares
def initialize(@width : Int = 8, @height : Int = 8)
def initialize(@width : Int16 = 8, @height : Int16 = 8)
@squares = Array(Piece | Nil).new(@width * @height) { nil }
end
@ -42,6 +42,12 @@ module LxChess
self[index(x, y)]
end
# Retrieve a piece relative to an index
def from(index : Int, x : Int, y : Int)
index = index + x + (y * @width)
self[index]
end
# Set a piece an the board at a certain *index*
def []=(index : Int, piece : Piece | Nil)
piece.index = index.to_i16 if piece
@ -56,7 +62,7 @@ module LxChess
# Convert an *x* and *y* position into an index.
# Ex: `4, 4` => `36`
def index(x : Int, y : Int)
(y * @width) + x
((y * @width) + x).to_i16
end
# Convert human *cord* into an index on the board.

View file

@ -28,7 +28,7 @@ module LxChess
height = ranks.size
rank = height - 1
board = Board.new(width, height)
board = Board.new(width.to_i16, height.to_i16)
ranks.map { |r| r.chars }.each do |pieces|
file = 0
@ -71,5 +71,12 @@ module LxChess
@fullmove_counter : Int16
)
end
def placement
@board.map { |piece| piece ? piece.fen_symbol : nil }
.each_slice(@board.width)
.map { |row| row.chunks { |r| r.nil? }.map { |chunked, values| chunked ? values.size : values.join }.first }
.join('/')
end
end
end

View file

@ -9,9 +9,20 @@ module LxChess
class SanError < Error; end
property turn : Int8, board : Board
property move_clock : Int16
def initialize(@board : Board = Board.new, @players = [] of Player)
@turn = 0
@move_clock = 0
end
def next_turn
@turn = (@turn == 0 ? 1 : 0).to_i8
@move_clock += 1
end
def full_moves
(@move_clock / 2).to_i16
end
# Parse standard algebraic notation
@ -27,11 +38,12 @@ module LxChess
end
raise SanError.new("Ambiguous SAN") if pieces.size > 1
raise SanError.new("Illegal move") if pieces.size == 0
piece = pieces.first
# from, to
[piece.as(Piece).index, index]
if piece = pieces.first?
# from, to
[piece.index, index]
else
raise SanError.new("#{notation.to_s} is an illegal move")
end
end
# Generate the psuedo-legal moves for a given *square*
@ -44,8 +56,12 @@ module LxChess
case piece.fen_symbol
when 'P' # White pawn
set.add_vector(x: 0, y: 1, limit: (@board.rank(index) == 1 ? 2 : 1).to_i16)
set.add_vector(x: -1, y: 1, limit: 1) if @board.from(index, x: -1, y: 1)
set.add_vector(x: 1, y: 1, limit: 1) if @board.from(index, x: 1, y: 1)
when 'p' # Black pawn
set.add_vector(x: 0, y: -1, limit: (@board.rank(index) == @board.height - 2 ? 2 : 1).to_i16)
set.add_vector(x: -1, y: -1, limit: 1) if @board.from(index, x: -1, y: -1)
set.add_vector(x: 1, y: -1, limit: 1) if @board.from(index, x: 1, y: -1)
when 'B', 'b' # Bishop
set.add_vector(x: -1, y: 1, limit: 8)
set.add_vector(x: 1, y: 1, limit: 8)

View file

@ -24,6 +24,20 @@ module LxChess
end
def initialize(@io = STDOUT)
@x = 0
@y = 0
end
# Move cursor to line, column
def move(x, y)
@x = x
@y = y
@io.print "\033[#{@x};#{@y}H"
end
# Delete the rest of the line from cursor pos
def trunc
@io.print "\033[K"
end
end
end