diff --git a/Chess/nimfish/nimfish.nim b/Chess/nimfish/nimfish.nim index 3ae169c..6f259f1 100644 --- a/Chess/nimfish/nimfish.nim +++ b/Chess/nimfish/nimfish.nim @@ -14,8 +14,6 @@ import std/strutils import std/strformat -import std/times -import std/math import std/bitops @@ -29,8 +27,6 @@ export bitboards, magics, pieces, moves type - CountData = tuple[nodes: uint64, captures: uint64, castles: uint64, checks: uint64, promotions: uint64, enPassant: uint64, checkmates: uint64] - Position* = object ## A chess position @@ -56,8 +52,8 @@ type pieces: tuple[white, black: tuple[king, queens, rooks, bishops, knights, pawns: Bitboard]] # Pinned pieces for each side pins: tuple[white, black: Bitboard] - # Checking pieces - checkers: tuple[white, black: Bitboard] + # Pieces checking the current side to move + checkers: Bitboard ChessBoard* = ref object @@ -83,10 +79,6 @@ proc movePiece(self: ChessBoard, move: Move) proc removePiece(self: ChessBoard, square: Square) -proc extend[T](self: var seq[T], other: openarray[T]) {.inline.} = - for x in other: - self.add(x) - proc update*(self: ChessBoard) @@ -121,27 +113,6 @@ func getHalfMoveCount*(self: ChessBoard): int {.inline.} = return self.position.halfMoveClock -func getStartRank(piece: Piece): int {.inline.} = - ## Retrieves the starting row of - ## the given piece inside our 8x8 - ## grid - case piece.color: - of None: - return -1 - of White: - case piece.kind: - of Pawn: - return 6 - else: - return 7 - of Black: - case piece.kind: - of Pawn: - return 1 - else: - return 0 - - func getKingStartingSquare*(color: PieceColor): Square {.inline.} = ## Retrieves the starting square of the king ## for the given color @@ -163,7 +134,7 @@ func longCastleRook*(color: PieceColor): Square {.inline.} = (if color == White: func shortCastleRook*(color: PieceColor): Square {.inline.} = (if color == White: "f1".toSquare() else: "f8".toSquare()) -proc inCheck*(self: ChessBoard, side: PieceColor): bool +proc inCheck*(self: ChessBoard): bool proc newChessboard: ChessBoard = @@ -178,13 +149,7 @@ func `[]`(self: array[64, Piece], square: Square): Piece {.inline.} = self[squar func `[]=`(self: var array[64, Piece], square: Square, piece: Piece) {.inline.} = self[square.int8] = piece -func getDirectionMask(self: ChessBoard, square: Square, direction: Direction): Bitboard = - ## Like getDirectionMask(), but used within the board context - ## with a piece square and direction only - return getDirectionMask(square, self.grid[square].color, direction) - - -func getBitboard(self: ChessBoard, kind: PieceKind, color: PieceColor): Bitboard = +func getBitboard*(self: ChessBoard, kind: PieceKind, color: PieceColor): Bitboard = ## Returns the positional bitboard for the given piece kind and color case color: of White: @@ -223,7 +188,7 @@ func getBitboard(self: ChessBoard, kind: PieceKind, color: PieceColor): Bitboard discard -func getBitboard(self: ChessBoard, piece: Piece): Bitboard = +func getBitboard*(self: ChessBoard, piece: Piece): Bitboard = ## Returns the positional bitboard for the given piece type return self.getBitboard(piece.kind, piece.color) @@ -242,7 +207,6 @@ proc newChessboardFromFEN*(fen: string): ChessBoard = index = 0 # Temporary variable to store a piece piece: Piece - pieces: int # See https://en.wikipedia.org/wiki/Forsyth%E2%80%93Edwards_Notation while index <= fen.high(): var c = fen[index] @@ -376,9 +340,6 @@ proc newChessboardFromFEN*(fen: string): ChessBoard = else: raise newException(ValueError, "invalid FEN: too many fields in FEN string") inc(index) - if result.inCheck(result.getSideToMove().opposite): - # Opponent king cannot be captured on the next move - raise newException(ValueError, "invalid position: opponent king can be captured") if result.position.pieces.white.king == 0 or result.position.pieces.black.king == 0: # Both kings must be on the board raise newException(ValueError, "invalid position: exactly one king of each color must be present") @@ -511,18 +472,6 @@ func getFlags*(move: Move): seq[MoveFlag] = result.add(Default) -func getKingSquare*(self: ChessBoard, color: PieceColor): Square {.inline.} = - ## Returns the square of the king for the given - ## side - case color: - of White: - return self.position.pieces.white.king.toSquare() - of Black: - return self.position.pieces.black.king.toSquare() - else: - discard - - proc getOccupancyFor(self: ChessBoard, color: PieceColor): Bitboard = ## Get the occupancy bitboard for every piece of the given color case color: @@ -559,57 +508,48 @@ proc getKingAttacks(self: ChessBoard, square: Square, attacker: PieceColor): Bit ## the king of the given side result = Bitboard(0) let - sq = square.toBitboard() king = self.getBitboard(King, attacker) if (KING_BITBOARDS[square.uint] and king) != 0: - result = result or sq + result = result or king proc getKnightAttacks(self: ChessBoard, square: Square, attacker: PieceColor): Bitboard = ## Returns the attack bitboard for the given square from ## the knights of the given side let - sq = square.toBitboard() knights = self.getBitboard(Knight, attacker) result = Bitboard(0) for knight in knights: - if (KNIGHT_BITBOARDS[square.uint] and knight.toBitboard()) != 0: - result = result or knight.toBitboard() + let knightBB = knight.toBitboard() + if (KNIGHT_BITBOARDS[square.uint] and knightBB) != 0: + result = result or knightBB proc getSlidingAttacks(self: ChessBoard, square: Square, attacker: PieceColor): Bitboard = ## Returns the attack bitboard for the given square from ## the sliding pieces of the given side let - sq = square.toBitboard() queens = self.getBitboard(Queen, attacker) - rooks = self.getBitboard(Rook, attacker) - bishops = self.getBitboard(Bishop, attacker) - occupancy = self.getOccupancy() + rooks = self.getBitboard(Rook, attacker) or queens + bishops = self.getBitboard(Bishop, attacker) or queens result = Bitboard(0) for rook in rooks: let blockers = Rook.getRelevantBlockers(square) - if (getRookMoves(square, blockers) and rook.toBitboard()) != 0: - result = result or rook.toBitboard() + let rookBB = rook.toBitboard() + if (getRookMoves(square, blockers) and rookBB) != 0: + result = result or rookBB for bishop in bishops: - let blockers = Bishop.getRelevantBlockers(square) - if (getBishopMoves(square, blockers) and bishop.toBitboard()) != 0: - result = result or bishop.toBitboard() - for queen in queens: - let rookBlockers = Rook.getRelevantBlockers(square) - if (getRookMoves(square, rookBlockers) and queen.toBitboard()) != 0: - result = result or queen.toBitboard() - let bishopBlockers = Bishop.getRelevantBlockers(square) - if (getBishopMoves(square, bishopBlockers) and queen.toBitboard()) != 0: - result = result or queen.toBitboard() + let + blockers = Bishop.getRelevantBlockers(square) + bishopBB = bishop.toBitboard() + if (getBishopMoves(square, blockers) and bishopBB) != 0: + result = result or bishopBB proc getAttacksTo*(self: ChessBoard, square: Square, attacker: PieceColor): Bitboard = ## Computes the attack bitboard for the given square from ## the given side result = Bitboard(0) - let - squareBitboard = square.toBitboard() result = result or self.getPawnAttacks(square, attacker) result = result or self.getKingAttacks(square, attacker) result = result or self.getKnightAttacks(square, attacker) @@ -617,26 +557,15 @@ proc getAttacksTo*(self: ChessBoard, square: Square, attacker: PieceColor): Bitb proc updateCheckers(self: ChessBoard) = - let side = self.getSideToMove() - let checkers = self.getAttacksTo(self.getKingSquare(side), side.opposite()) - case side: - of White: - self.position.checkers.white = checkers - of Black: - self.position.checkers.black = checkers - else: - discard + let + side = self.getSideToMove() + king = self.getBitboard(King, side).toSquare() + self.position.checkers = self.getAttacksTo(king, side.opposite()) -proc inCheck(self: ChessBoard, side: PieceColor): bool = +proc inCheck(self: ChessBoard): bool = ## Returns if the current side to move is in check - case self.getSideToMove(): - of White: - return self.position.checkers.white != 0 - of Black: - return self.position.checkers.black != 0 - else: - discard + return self.position.checkers != 0 proc canCastle*(self: ChessBoard, side: PieceColor): tuple[king, queen: bool] = @@ -644,14 +573,6 @@ proc canCastle*(self: ChessBoard, side: PieceColor): tuple[king, queen: bool] = return (false, false) # TODO -proc getCapturablePieces(self: ChessBoard, side: PieceColor): Bitboard {.inline.} = - ## Returns the set of pieces of the given color that can - ## be captured - - # Just a handy helper to filter out the king and avoid code duplication - return self.getOccupancyFor(side) and not self.getBitboard(King, side) - - proc generatePawnMovements(self: ChessBoard, moves: var MoveList) = ## Helper of generatePawnMoves for generating all non-capture ## and non-promotion pawn moves @@ -679,7 +600,7 @@ proc generatePawnCaptures(self: ChessBoard, moves: var MoveList) = nonSideToMove = sideToMove.opposite() pawns = self.getBitboard(Pawn, sideToMove) # We can only capture enemy pieces (except the king) - enemyPieces = self.getCapturablePieces(nonSideToMove) + enemyPieces = self.getOccupancyFor(nonSideToMove) enemyPawns = self.getBitboard(Pawn, nonSideToMove) rightMovement = pawns.forwardRightRelativeTo(sideToMove) leftMovement = pawns.forwardLeftRelativeTo(sideToMove) @@ -726,12 +647,12 @@ proc generateRookMoves(self: ChessBoard, moves: var MoveList) = let sideToMove = self.getSideToMove() occupancy = self.getOccupancy() - enemyPieces = self.getCapturablePieces(sideToMove.opposite()) - rooks = self.getBitboard(Rook, sideToMove) + enemyPieces = self.getOccupancyFor(sideToMove.opposite()) + rooks = self.getBitboard(Rook, sideToMove) or self.getBitboard(Queen, sideToMove) for square in rooks: - let blockers = occupancy and Rook.getRelevantBlockers(square) - # Quiet moves - var moveset = getRookMoves(square, blockers) + let + blockers = occupancy and Rook.getRelevantBlockers(square) + moveset = getRookMoves(square, blockers) for target in moveset and not occupancy: moves.add(createMove(square, target)) # Captures @@ -741,51 +662,27 @@ proc generateRookMoves(self: ChessBoard, moves: var MoveList) = proc generateBishopMoves(self: ChessBoard, moves: var MoveList) = ## Helper of generateSlidingMoves to generate bishop moves - # self.generateBishopMovements(moves) - # self.generateBishopCaptures(moves) let sideToMove = self.getSideToMove() enemyPieces = self.getOccupancyFor(sideToMove.opposite()) occupancy = self.getOccupancy() - bishops = self.getBitboard(Bishop, sideToMove) + bishops = self.getBitboard(Bishop, sideToMove) or self.getBitboard(Queen, sideToMove) for square in bishops: - let blockers = occupancy and Bishop.getRelevantBlockers(square) - let moveset = getBishopMoves(square, blockers) - # Can't move over other pieces + let + blockers = occupancy and Bishop.getRelevantBlockers(square) + moveset = getBishopMoves(square, blockers) for target in moveset and not occupancy: moves.add(createMove(square, target)) for target in moveset and enemyPieces: moves.add(createMove(square, target, Capture)) -proc generateQueenMoves(self: ChessBoard, moves: var MoveList) = - ## Helper of generateSlidingMoves to generate queen moves - let - sideToMove = self.getSideToMove() - occupancy = self.getOccupancy() - enemyPieces = self.getCapturablePieces(sideToMove.opposite()) - queens = self.getBitboard(Queen, sideToMove) - for square in queens: - # A queen is just a rook plus a bishop in terms of move - # generation - let - rookBlockers = Rook.getRelevantBlockers(square) and occupancy - bishopBlockers = Bishop.getRelevantBlockers(square) and occupancy - rookMoves = getRookMoves(square, rookBlockers) - bishopMoves = getBishopMoves(square, bishopBlockers) - let moveset = rookMoves or bishopMoves - # Can't move over other pieces - for target in moveset and not occupancy: - moves.add(createMove(square, target)) - for target in moveset and enemyPieces: - moves.add(createMove(square, target)) - proc generateSlidingMoves(self: ChessBoard, moves: var MoveList) = ## Generates all legal sliding moves for the side to move self.generateRookMoves(moves) self.generateBishopMoves(moves) - self.generateQueenMoves(moves) + # Queens are just handled rooks + bishops proc generateKingMoves(self: ChessBoard, moves: var MoveList) = @@ -794,11 +691,11 @@ proc generateKingMoves(self: ChessBoard, moves: var MoveList) = sideToMove = self.getSideToMove() king = self.getBitboard(King, sideToMove) moveIdx = king.toSquare().uint64 - allowedSquares = not self.getOccupancy() + occupancy = self.getOccupancy() nonSideToMove = sideToMove.opposite() - enemyPieces = self.getCapturablePieces(nonSideToMove) + enemyPieces = self.getOccupancyFor(nonSideToMove) # Regular moves - for square in KING_BITBOARDS[moveIdx] and allowedSquares: + for square in KING_BITBOARDS[moveIdx] and not occupancy: moves.add(createMove(king, square)) # Captures for square in KING_BITBOARDS[moveIdx] and enemyPieces: @@ -810,12 +707,12 @@ proc generateKnightMoves(self: ChessBoard, moves: var MoveList)= let sideToMove = self.getSideToMove() knights = self.getBitboard(Knight, sideToMove) - allowedSquares = not self.getOccupancy() + occupancy = self.getOccupancy() nonSideToMove = sideToMove.opposite() - enemyPieces = self.getCapturablePieces(nonSideToMove) + enemyPieces = self.getOccupancyFor(nonSideToMove) for square in knights: # Regular moves - for target in KNIGHT_BITBOARDS[square.uint64] and allowedSquares: + for target in KNIGHT_BITBOARDS[square.uint64] and not occupancy: moves.add(createMove(square, target)) # Captures for target in KNIGHT_BITBOARDS[square.uint64] and enemyPieces: @@ -834,6 +731,7 @@ proc generateMoves*(self: ChessBoard, moves: var MoveList) = self.generatePawnMoves(moves) self.generateKnightMoves(moves) self.generateSlidingMoves(moves) + # self.updateCheckers() proc removePieceFromBitboard(self: ChessBoard, square: Square) = @@ -942,11 +840,6 @@ proc movePiece(self: ChessBoard, move: Move) = self.spawnPiece(move.targetSquare, piece) -proc movePiece(self: ChessBoard, startSquare, targetSquare: Square) = - ## Like the other movePiece(), but with two squares - self.movePiece(Move(startSquare: startSquare, targetSquare: targetSquare)) - - proc doMove*(self: ChessBoard, move: Move) = ## Internal function called by makeMove after ## performing legality checks. Can be used in @@ -1220,113 +1113,11 @@ proc toFEN*(self: ChessBoard): string = when isMainModule: + import nimfishpkg/tui + import nimfishpkg/misc - - proc testPiece(piece: Piece, kind: PieceKind, color: PieceColor) = - doAssert piece.kind == kind and piece.color == color, &"expected piece of kind {kind} and color {color}, got {piece.kind} / {piece.color} instead" - - proc testPieceCount(board: ChessBoard, kind: PieceKind, color: PieceColor, count: int) = - let pieces = board.countPieces(kind, color) - doAssert pieces == count, &"expected {count} pieces of kind {kind} and color {color}, got {pieces} instead" - - proc testPieceBitboard(bitboard: Bitboard, squares: seq[Square]) = - var i = 0 - for square in bitboard: - doAssert squares[i] == square, &"squares[{i}] != bitboard[i]: {squares[i]} != {square}" - inc(i) - if i != squares.len(): - doAssert false, &"bitboard.len() ({i}) != squares.len() ({squares.len()})" - - - var b = newDefaultChessboard() - # Ensure correct number of pieces - testPieceCount(b, Pawn, White, 8) - testPieceCount(b, Pawn, Black, 8) - testPieceCount(b, Knight, White, 2) - testPieceCount(b, Knight, Black, 2) - testPieceCount(b, Bishop, White, 2) - testPieceCount(b, Bishop, Black, 2) - testPieceCount(b, Rook, White, 2) - testPieceCount(b, Rook, Black, 2) - testPieceCount(b, Queen, White, 1) - testPieceCount(b, Queen, Black, 1) - testPieceCount(b, King, White, 1) - testPieceCount(b, King, Black, 1) - - # Ensure pieces are in the correct squares. This is testing the FEN - # parser - - # Pawns - for loc in ["a2", "b2", "c2", "d2", "e2", "f2", "g2", "h2"]: - testPiece(b.getPiece(loc), Pawn, White) - for loc in ["a7", "b7", "c7", "d7", "e7", "f7", "g7", "h7"]: - testPiece(b.getPiece(loc), Pawn, Black) - # Rooks - testPiece(b.getPiece("a1"), Rook, White) - testPiece(b.getPiece("h1"), Rook, White) - testPiece(b.getPiece("a8"), Rook, Black) - testPiece(b.getPiece("h8"), Rook, Black) - # Knights - testPiece(b.getPiece("b1"), Knight, White) - testPiece(b.getPiece("g1"), Knight, White) - testPiece(b.getPiece("b8"), Knight, Black) - testPiece(b.getPiece("g8"), Knight, Black) - # Bishops - testPiece(b.getPiece("c1"), Bishop, White) - testPiece(b.getPiece("f1"), Bishop, White) - testPiece(b.getPiece("c8"), Bishop, Black) - testPiece(b.getPiece("f8"), Bishop, Black) - # Kings - testPiece(b.getPiece("e1"), King, White) - testPiece(b.getPiece("e8"), King, Black) - # Queens - testPiece(b.getPiece("d1"), Queen, White) - testPiece(b.getPiece("d8"), Queen, Black) - - # Ensure our bitboards match with the board - let - whitePawns = b.getBitboard(Pawn, White) - whiteKnights = b.getBitboard(Knight, White) - whiteBishops = b.getBitboard(Bishop, White) - whiteRooks = b.getBitboard(Rook, White) - whiteQueens = b.getBitboard(Queen, White) - whiteKing = b.getBitboard(King, White) - blackPawns = b.getBitboard(Pawn, Black) - blackKnights = b.getBitboard(Knight, Black) - blackBishops = b.getBitboard(Bishop, Black) - blackRooks = b.getBitboard(Rook, Black) - blackQueens = b.getBitboard(Queen, Black) - blackKing = b.getBitboard(King, Black) - whitePawnSquares = @[makeSquare(6'i8, 0'i8), makeSquare(6, 1), makeSquare(6, 2), makeSquare(6, 3), makeSquare(6, 4), makeSquare(6, 5), makeSquare(6, 6), makeSquare(6, 7)] - whiteKnightSquares = @[makeSquare(7'i8, 1'i8), makeSquare(7, 6)] - whiteBishopSquares = @[makeSquare(7'i8, 2'i8), makeSquare(7, 5)] - whiteRookSquares = @[makeSquare(7'i8, 0'i8), makeSquare(7, 7)] - whiteQueenSquares = @[makeSquare(7'i8, 3'i8)] - whiteKingSquares = @[makeSquare(7'i8, 4'i8)] - blackPawnSquares = @[makeSquare(1'i8, 0'i8), makeSquare(1, 1), makeSquare(1, 2), makeSquare(1, 3), makeSquare(1, 4), makeSquare(1, 5), makeSquare(1, 6), makeSquare(1, 7)] - blackKnightSquares = @[makeSquare(0'i8, 1'i8), makeSquare(0, 6)] - blackBishopSquares = @[makeSquare(0'i8, 2'i8), makeSquare(0, 5)] - blackRookSquares = @[makeSquare(0'i8, 0'i8), makeSquare(0, 7)] - blackQueenSquares = @[makeSquare(0'i8, 3'i8)] - blackKingSquares = @[makeSquare(0'i8, 4'i8)] - - - testPieceBitboard(whitePawns, whitePawnSquares) - testPieceBitboard(whiteKnights, whiteKnightSquares) - testPieceBitboard(whiteBishops, whiteBishopSquares) - testPieceBitboard(whiteRooks, whiteRookSquares) - testPieceBitboard(whiteQueens, whiteQueenSquares) - testPieceBitboard(whiteKing, whiteKingSquares) - testPieceBitboard(blackPawns, blackPawnSquares) - testPieceBitboard(blackKnights, blackKnightSquares) - testPieceBitboard(blackBishops, blackBishopSquares) - testPieceBitboard(blackRooks, blackRookSquares) - testPieceBitboard(blackQueens, blackQueenSquares) - testPieceBitboard(blackKing, blackKingSquares) - + basicTests() setControlCHook(proc () {.noconv.} = quit(0)) - import tui - - quit(tui.commandLoop()) + quit(commandLoop()) diff --git a/Chess/nimfish/nimfishpkg/magics.nim b/Chess/nimfish/nimfishpkg/magics.nim index eed7845..f4eadd9 100644 --- a/Chess/nimfish/nimfishpkg/magics.nim +++ b/Chess/nimfish/nimfishpkg/magics.nim @@ -132,7 +132,7 @@ var BISHOP_MOVES: array[64, seq[Bitboard]] -proc getRookMoves*(square: Square, blockers: Bitboard): Bitboard = +proc getRookMoves*(square: Square, blockers: Bitboard): Bitboard = ## Returns the move bitboard for the rook at the given ## square with the given blockers bitboard let @@ -141,7 +141,7 @@ proc getRookMoves*(square: Square, blockers: Bitboard): Bitboard = return moves[getIndex(magic, blockers)] -proc getBishopMoves*(square: Square, blockers: Bitboard): Bitboard = +proc getBishopMoves*(square: Square, blockers: Bitboard): Bitboard = ## Returns the move bitboard for the bishop at the given ## square with the given blockers bitboard let @@ -159,7 +159,7 @@ const BISHOP_BLOCKERS = generateBishopBlockers() -func getRelevantBlockers*(kind: PieceKind, square: Square): Bitboard = +func getRelevantBlockers*(kind: PieceKind, square: Square): Bitboard {.inline.} = ## Returns the relevant blockers mask for the given piece ## type at the given square case kind: diff --git a/Chess/nimfish/nimfishpkg/misc.nim b/Chess/nimfish/nimfishpkg/misc.nim new file mode 100644 index 0000000..53b5128 --- /dev/null +++ b/Chess/nimfish/nimfishpkg/misc.nim @@ -0,0 +1,107 @@ +import ../nimfish + +import std/strformat + + +proc testPiece(piece: Piece, kind: PieceKind, color: PieceColor) = + doAssert piece.kind == kind and piece.color == color, &"expected piece of kind {kind} and color {color}, got {piece.kind} / {piece.color} instead" + +proc testPieceCount(board: ChessBoard, kind: PieceKind, color: PieceColor, count: int) = + let pieces = board.countPieces(kind, color) + doAssert pieces == count, &"expected {count} pieces of kind {kind} and color {color}, got {pieces} instead" + +proc testPieceBitboard(bitboard: Bitboard, squares: seq[Square]) = + var i = 0 + for square in bitboard: + doAssert squares[i] == square, &"squares[{i}] != bitboard[i]: {squares[i]} != {square}" + inc(i) + if i != squares.len(): + doAssert false, &"bitboard.len() ({i}) != squares.len() ({squares.len()})" + + +proc basicTests* = + var b = newDefaultChessboard() + # Ensure correct number of pieces + testPieceCount(b, Pawn, White, 8) + testPieceCount(b, Pawn, Black, 8) + testPieceCount(b, Knight, White, 2) + testPieceCount(b, Knight, Black, 2) + testPieceCount(b, Bishop, White, 2) + testPieceCount(b, Bishop, Black, 2) + testPieceCount(b, Rook, White, 2) + testPieceCount(b, Rook, Black, 2) + testPieceCount(b, Queen, White, 1) + testPieceCount(b, Queen, Black, 1) + testPieceCount(b, King, White, 1) + testPieceCount(b, King, Black, 1) + + # Ensure pieces are in the correct squares. This is testing the FEN + # parser + + # Pawns + for loc in ["a2", "b2", "c2", "d2", "e2", "f2", "g2", "h2"]: + testPiece(b.getPiece(loc), Pawn, White) + for loc in ["a7", "b7", "c7", "d7", "e7", "f7", "g7", "h7"]: + testPiece(b.getPiece(loc), Pawn, Black) + # Rooks + testPiece(b.getPiece("a1"), Rook, White) + testPiece(b.getPiece("h1"), Rook, White) + testPiece(b.getPiece("a8"), Rook, Black) + testPiece(b.getPiece("h8"), Rook, Black) + # Knights + testPiece(b.getPiece("b1"), Knight, White) + testPiece(b.getPiece("g1"), Knight, White) + testPiece(b.getPiece("b8"), Knight, Black) + testPiece(b.getPiece("g8"), Knight, Black) + # Bishops + testPiece(b.getPiece("c1"), Bishop, White) + testPiece(b.getPiece("f1"), Bishop, White) + testPiece(b.getPiece("c8"), Bishop, Black) + testPiece(b.getPiece("f8"), Bishop, Black) + # Kings + testPiece(b.getPiece("e1"), King, White) + testPiece(b.getPiece("e8"), King, Black) + # Queens + testPiece(b.getPiece("d1"), Queen, White) + testPiece(b.getPiece("d8"), Queen, Black) + + # Ensure our bitboards match with the board + let + whitePawns = b.getBitboard(Pawn, White) + whiteKnights = b.getBitboard(Knight, White) + whiteBishops = b.getBitboard(Bishop, White) + whiteRooks = b.getBitboard(Rook, White) + whiteQueens = b.getBitboard(Queen, White) + whiteKing = b.getBitboard(King, White) + blackPawns = b.getBitboard(Pawn, Black) + blackKnights = b.getBitboard(Knight, Black) + blackBishops = b.getBitboard(Bishop, Black) + blackRooks = b.getBitboard(Rook, Black) + blackQueens = b.getBitboard(Queen, Black) + blackKing = b.getBitboard(King, Black) + whitePawnSquares = @[makeSquare(6'i8, 0'i8), makeSquare(6, 1), makeSquare(6, 2), makeSquare(6, 3), makeSquare(6, 4), makeSquare(6, 5), makeSquare(6, 6), makeSquare(6, 7)] + whiteKnightSquares = @[makeSquare(7'i8, 1'i8), makeSquare(7, 6)] + whiteBishopSquares = @[makeSquare(7'i8, 2'i8), makeSquare(7, 5)] + whiteRookSquares = @[makeSquare(7'i8, 0'i8), makeSquare(7, 7)] + whiteQueenSquares = @[makeSquare(7'i8, 3'i8)] + whiteKingSquares = @[makeSquare(7'i8, 4'i8)] + blackPawnSquares = @[makeSquare(1'i8, 0'i8), makeSquare(1, 1), makeSquare(1, 2), makeSquare(1, 3), makeSquare(1, 4), makeSquare(1, 5), makeSquare(1, 6), makeSquare(1, 7)] + blackKnightSquares = @[makeSquare(0'i8, 1'i8), makeSquare(0, 6)] + blackBishopSquares = @[makeSquare(0'i8, 2'i8), makeSquare(0, 5)] + blackRookSquares = @[makeSquare(0'i8, 0'i8), makeSquare(0, 7)] + blackQueenSquares = @[makeSquare(0'i8, 3'i8)] + blackKingSquares = @[makeSquare(0'i8, 4'i8)] + + + testPieceBitboard(whitePawns, whitePawnSquares) + testPieceBitboard(whiteKnights, whiteKnightSquares) + testPieceBitboard(whiteBishops, whiteBishopSquares) + testPieceBitboard(whiteRooks, whiteRookSquares) + testPieceBitboard(whiteQueens, whiteQueenSquares) + testPieceBitboard(whiteKing, whiteKingSquares) + testPieceBitboard(blackPawns, blackPawnSquares) + testPieceBitboard(blackKnights, blackKnightSquares) + testPieceBitboard(blackBishops, blackBishopSquares) + testPieceBitboard(blackRooks, blackRookSquares) + testPieceBitboard(blackQueens, blackQueenSquares) + testPieceBitboard(blackKing, blackKingSquares) \ No newline at end of file diff --git a/Chess/nimfish/nimfishpkg/pieces.nim b/Chess/nimfish/nimfishpkg/pieces.nim index 69a61f2..fa4c874 100644 --- a/Chess/nimfish/nimfishpkg/pieces.nim +++ b/Chess/nimfish/nimfishpkg/pieces.nim @@ -9,8 +9,8 @@ type PieceColor* = enum ## A piece color enumeration - None = 0'i8, - White, + None = 0'i8 + White Black PieceKind* = enum diff --git a/Chess/nimfish/tui.nim b/Chess/nimfish/nimfishpkg/tui.nim similarity index 97% rename from Chess/nimfish/tui.nim rename to Chess/nimfish/nimfishpkg/tui.nim index 38d215d..099c1f1 100644 --- a/Chess/nimfish/tui.nim +++ b/Chess/nimfish/nimfishpkg/tui.nim @@ -1,4 +1,4 @@ -import nimfish +import ../nimfish import std/strformat @@ -20,7 +20,7 @@ proc perft*(self: ChessBoard, ply: int, verbose: bool = false, divide: bool = fa var moves = MoveList() self.generateMoves(moves) if not bulk: - if len(moves) == 0 and self.inCheck(self.getSideToMove()): + if len(moves) == 0 and self.inCheck(): result.checkmates = 1 # TODO: Should we count stalemates/draws? if ply == 0: @@ -54,7 +54,7 @@ proc perft*(self: ChessBoard, ply: int, verbose: bool = false, divide: bool = fa echo &"Turn: {self.getSideToMove()}" echo &"Piece: {self.getPiece(move.startSquare).kind}" echo &"Flags: {move.getFlags()}" - echo &"In check: {(if self.inCheck(self.getSideToMove()): \"yes\" else: \"no\")}" + echo &"In check: {(if self.inCheck(): \"yes\" else: \"no\")}" echo &"Can castle:\n - King side: {(if canCastle.king: \"yes\" else: \"no\")}\n - Queen side: {(if canCastle.queen: \"yes\" else: \"no\")}" echo &"Position before move: {self.toFEN()}" stdout.write("En Passant target: ") @@ -73,13 +73,13 @@ proc perft*(self: ChessBoard, ply: int, verbose: bool = false, divide: bool = fa inc(result.promotions) if move.isEnPassant(): inc(result.enPassant) - if self.inCheck(self.getSideToMove()): + if self.inCheck(): # Opponent king is in check inc(result.checks) if verbose: let canCastle = self.canCastle(self.getSideToMove()) echo "\n" - echo &"Opponent in check: {(if self.inCheck(self.getSideToMove()): \"yes\" else: \"no\")}" + echo &"Opponent in check: {(if self.inCheck(): \"yes\" else: \"no\")}" echo &"Opponent can castle:\n - King side: {(if canCastle.king: \"yes\" else: \"no\")}\n - Queen side: {(if canCastle.queen: \"yes\" else: \"no\")}" echo &"Position after move: {self.toFEN()}" echo "\n", self.pretty() @@ -420,7 +420,7 @@ proc commandLoop*: int = let canCastle = board.canCastle(board.getSideToMove()) echo &"Castling rights for {($board.getSideToMove()).toLowerAscii()}:\n - King side: {(if canCastle.king: \"yes\" else: \"no\")}\n - Queen side: {(if canCastle.queen: \"yes\" else: \"no\")}" of "check": - echo &"{board.getSideToMove()} king in check: {(if board.inCheck(board.getSideToMove()): \"yes\" else: \"no\")}" + echo &"{board.getSideToMove()} king in check: {(if board.inCheck(): \"yes\" else: \"no\")}" else: echo &"Unknown command '{cmd[0]}'. Type 'help' for more information." except IOError: