diff --git a/src/Chess/board.nim b/src/Chess/board.nim index f4cdf20..90c7200 100644 --- a/src/Chess/board.nim +++ b/src/Chess/board.nim @@ -154,6 +154,7 @@ 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] func invalidateCache(self: ChessBoard) {.inline.} +proc inCheck*(self: ChessBoard, color: PieceColor = None): bool proc extend[T](self: var seq[T], other: openarray[T]) {.inline.} = @@ -458,7 +459,9 @@ proc newChessboardFromFEN*(state: string): ChessBoard = raise newException(ValueError, "too many fields in FEN string") inc(index) result.updateAttackedSquares() - + if result.inCheck(result.getActiveColor().opposite): + # Opponent king cannot be captured! + raise newException(ValueError, "invalid position: opponent king can be captured") proc newDefaultChessboard*: ChessBoard {.inline.} = @@ -1187,28 +1190,36 @@ proc getSlidingAttacks(self: ChessBoard, loc: Location): tuple[attacks: Attacked # Empty square, keep going if otherPiece.color == None: continue - if otherPiece.color == piece.color.opposite and otherPiece.kind != King: - # We found an enemy piece that is not - # the enemy king. We don't break out - # immediately because we first want - # to check if we've pinned a piece - var - otherSquare: Location = square - behindPiece: Piece - while true: - otherSquare = otherSquare + direction - if not otherSquare.isValid(): - break - behindPiece = self.grid[otherSquare.row, otherSquare.col] - if behindPiece.color == None: - continue - if behindPiece.color == piece.color.opposite and behindPiece.kind == King: - # The enemy king is behind this enemy piece: pin it in - # this direction relative to them (that's why we have the - # minus sign: up for us is down for them and vice versa) - result.pins.add((loc, square, -direction)) - else: - break + if otherPiece.color == piece.color.opposite(): + if otherPiece.kind != King: + # We found an enemy piece that is not + # the enemy king. We don't break out + # immediately because we first want + # to check if we've pinned a piece + var + otherSquare: Location = square + behindPiece: Piece + while true: + otherSquare = otherSquare + direction + if not otherSquare.isValid(): + break + behindPiece = self.grid[otherSquare.row, otherSquare.col] + if behindPiece.color == None: + continue + if behindPiece.color == piece.color.opposite and behindPiece.kind == King: + # The enemy king is behind this enemy piece: pin it in + # this direction relative to them (that's why we have the + # minus sign: up for us is down for them and vice versa) + result.pins.add((loc, square, -direction)) + else: + break + else: + # Enemy king is here: ensure it cannot move backwards by + # attacking the square behind it (if one exists and is + # valid) + let target = square + direction + if target.isValid(): + result.attacks.add((square, target, direction)) break diff --git a/src/Chess/compare_positions.py b/src/Chess/compare_positions.py index 22735c8..26fd05b 100644 --- a/src/Chess/compare_positions.py +++ b/src/Chess/compare_positions.py @@ -102,10 +102,7 @@ def main(args: Namespace) -> int: r"(?:\s\s-\sPromotions:\s(?P[0-9]+))", re.MULTILINE) extra: re.Match | None = None - if args.bulk: - print("Note: Nimfish was run in bulk-counting mode, so a detailed breakdown of each move type is not available. " - "To fix this, re-run the program without the --bulk option") - else: + if not args.bulk: extra = pattern.search(nimfish_output) if missing["stockfish"] or missing["nimfish"] or mistakes: print(f"Found {len(missing['stockfish']) + len(missing['nimfish'])} missed moves and {len(mistakes)} counting mistakes, more info below: ")