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