Fix bugs with en passant

This commit is contained in:
Mattia Giambirtone 2024-04-12 16:05:01 +02:00
parent 54a6217bd3
commit f75f7533f5
1 changed files with 30 additions and 27 deletions

View File

@ -133,12 +133,15 @@ proc doMove(self: ChessBoard, move: Move)
proc pretty*(self: ChessBoard): string proc pretty*(self: ChessBoard): string
proc spawnPiece(self: ChessBoard, location: Location, piece: Piece) proc spawnPiece(self: ChessBoard, location: Location, piece: Piece)
proc updateAttackedSquares(self: ChessBoard) proc updateAttackedSquares(self: ChessBoard)
proc updateSlidingAttacks(self: ChessBoard)
proc getPinnedDirections(self: ChessBoard, loc: Location): seq[Location] proc getPinnedDirections(self: ChessBoard, loc: Location): seq[Location]
proc getAttacks*(self: ChessBoard, loc: Location): Attacked proc getAttacks*(self: ChessBoard, loc: Location): Attacked
proc getSlidingAttacks(self: ChessBoard, loc: Location): tuple[attacks: Attacked, pins: Attacked] proc getSlidingAttacks(self: ChessBoard, loc: Location): tuple[attacks: Attacked, pins: Attacked]
proc inCheck*(self: ChessBoard, color: PieceColor = None): bool proc inCheck*(self: ChessBoard, color: PieceColor = None): bool
proc toFEN*(self: ChessBoard): string proc toFEN*(self: ChessBoard): string
proc undoLastMove*(self: ChessBoard) proc undoLastMove*(self: ChessBoard)
proc movePiece(self: ChessBoard, move: Move, attack: bool = true)
proc removePiece(self: ChessBoard, location: Location, attack: bool = true)
proc extend[T](self: var seq[T], other: openarray[T]) {.inline.} = proc extend[T](self: var seq[T], other: openarray[T]) {.inline.} =
@ -847,27 +850,21 @@ proc generatePawnMoves(self: ChessBoard, location: Location): seq[Move] =
if target.isValid(): if target.isValid():
let otherPiece = self.grid[target] let otherPiece = self.grid[target]
if target == self.position.enPassantSquare and self.grid[enPassantPawn].color == piece.color.opposite(): if target == self.position.enPassantSquare and self.grid[enPassantPawn].color == piece.color.opposite():
# Ensure en passant doesn't create a check # En passant may be possible
let king = self.getKing(piece.color) let targetPawn = self.grid[enPassantPawn]
var ok = true # Remove both pieces and see if the king ends up in check
if king.row == location.row: self.removePiece(enPassantPawn, attack=false)
var current = location self.removePiece(location, attack=false)
while true: self.updateAttackedSquares()
current = current + piece.color.rightSide() if not self.inCheck(piece.color):
if not current.isValid(): # King is not in check after en passant: move is legal
break
let p = self.grid[current.row, current.col]
if p.color == piece.color:
break
if p.color == None:
continue
# Bishops can't create checks through en passant (neither can knights)
if p.color == piece.color.opposite() and p.kind in [Queen, Rook]:
ok = false
break
if ok:
directions.add(diagonal) directions.add(diagonal)
# Reset what we just did and reupdate the attack metadata
self.spawnPiece(location, piece)
self.spawnPiece(enPassantPawn, targetPawn)
self.updateAttackedSquares()
elif otherPiece.color == piece.color.opposite() and otherPiece.kind != King: # Can't capture the king! elif otherPiece.color == piece.color.opposite() and otherPiece.kind != King: # Can't capture the king!
# A capture may be possible
directions.add(diagonal) directions.add(diagonal)
# Check for pins # Check for pins
let pinned = self.getPinnedDirections(location) let pinned = self.getPinnedDirections(location)
@ -924,8 +921,9 @@ proc generateSlidingMoves(self: ChessBoard, location: Location): seq[Move] =
if direction in pinned: if direction in pinned:
newDirections.add(direction) newDirections.add(direction)
directions = newDirections directions = newDirections
let checked = self.inCheck() let
let resolutions = if not checked: @[] else: self.getCheckResolutions(piece.color) checked = self.inCheck()
resolutions = if not checked: @[] else: self.getCheckResolutions(piece.color)
for direction in directions: for direction in directions:
# Slide in this direction as long as it's possible # Slide in this direction as long as it's possible
var var
@ -1333,9 +1331,10 @@ proc updateSlidingAttacks(self: ChessBoard) =
proc updateAttackedSquares(self: ChessBoard) = proc updateAttackedSquares(self: ChessBoard) =
## Updates internal metadata about which squares ## Updates internal metadata about which squares
## are attacked ## are attacked
self.position.attacked.white.setLen(0) self.position.attacked.white.setLen(0)
self.position.attacked.black.setLen(0) self.position.attacked.black.setLen(0)
self.position.pinned.white.setLen(0)
self.position.pinned.black.setLen(0)
# Pawns # Pawns
self.updatePawnAttacks() self.updatePawnAttacks()
# Sliding pieces # Sliding pieces
@ -1687,8 +1686,6 @@ proc undoLastMove*(self: ChessBoard) =
proc isLegal(self: ChessBoard, move: Move): bool {.inline.} = proc isLegal(self: ChessBoard, move: Move): bool {.inline.} =
## Returns whether the given move is legal ## Returns whether the given move is legal
if self.grid[move.startSquare.row, move.startSquare.col].color != self.getActiveColor():
return false
return move in self.generateMoves(move.startSquare) return move in self.generateMoves(move.startSquare)
@ -2047,6 +2044,8 @@ proc handleMoveCommand(board: ChessBoard, command: seq[string]): Move {.discarda
move.flags = move.flags or CastleLong.uint16 move.flags = move.flags or CastleLong.uint16
elif move.targetSquare == move.startSquare + shortCastleKing(): elif move.targetSquare == move.startSquare + shortCastleKing():
move.flags = move.flags or CastleShort.uint16 move.flags = move.flags or CastleShort.uint16
if move.targetSquare == board.getEnPassantTarget():
move.flags = move.flags or EnPassant.uint16
result = board.makeMove(move) result = board.makeMove(move)
if result == emptyMove(): if result == emptyMove():
echo &"Error: move: {moveString} is illegal" echo &"Error: move: {moveString} is illegal"
@ -2095,7 +2094,7 @@ proc handlePositionCommand(board: var ChessBoard, command: seq[string]) =
try: try:
tempBoard = newChessboardFromFEN(fenString) tempBoard = newChessboardFromFEN(fenString)
except ValueError: except ValueError:
echo &"Error: position: {getCurrentExceptionMsg()}" echo &"error: position: {getCurrentExceptionMsg()}"
return return
if args.len() > 0: if args.len() > 0:
var i = 0 var i = 0
@ -2113,6 +2112,9 @@ proc handlePositionCommand(board: var ChessBoard, command: seq[string]) =
echo board echo board
of "pretty": of "pretty":
echo board.pretty() echo board.pretty()
else:
echo &"error: position: unknown subcommand '{command[1]}'"
return
proc handleUCICommand(board: var ChessBoard, command: seq[string]) = proc handleUCICommand(board: var ChessBoard, command: seq[string]) =
@ -2154,7 +2156,8 @@ const HELP_TEXT = """Nimfish help menu:
- ep: Print the current en passant target - ep: Print the current en passant target
- pretty: Shorthand for "position pretty" - pretty: Shorthand for "position pretty"
- print: Shorthand for "position print" - print: Shorthand for "position print"
- fen: Shorthand for "position fen" (print only!) - fen: Shorthand for "position fen"
- pos <args>: Shorthand for "position <args>"
- get <square>: Get the piece on the given square - get <square>: Get the piece on the given square
- uci: enter UCI mode (WIP) - uci: enter UCI mode (WIP)
""" """
@ -2189,7 +2192,7 @@ proc main: int =
echo HELP_TEXT echo HELP_TEXT
of "go": of "go":
handleGoCommand(board, cmd) handleGoCommand(board, cmd)
of "position": of "position", "pos":
handlePositionCommand(board, cmd) handlePositionCommand(board, cmd)
of "move": of "move":
handleMoveCommand(board, cmd) handleMoveCommand(board, cmd)