From c9988cd93987dc975913231f9f9e7371965a9288 Mon Sep 17 00:00:00 2001 From: nocturn9x Date: Mon, 15 Apr 2024 12:45:47 +0200 Subject: [PATCH] Improve bitboard interface --- src/Chess/board.nim | 89 +++++++++++++++++++++++++++++++++------------ 1 file changed, 66 insertions(+), 23 deletions(-) diff --git a/src/Chess/board.nim b/src/Chess/board.nim index a8170be..52dae45 100644 --- a/src/Chess/board.nim +++ b/src/Chess/board.nim @@ -131,15 +131,16 @@ type # A bunch of simple utility functions and forward declarations + func emptyPiece*: Piece {.inline.} = Piece(kind: Empty, color: None) func emptySquare*: Square {.inline.} = (-1 , -1) func opposite*(c: PieceColor): PieceColor {.inline.} = (if c == White: Black else: White) -proc algebraicToSquare*(s: string): Square {.inline.} +func algebraicToSquare*(s: string): Square {.inline.} proc makeMove*(self: ChessBoard, move: Move): Move {.discardable.} func emptyMove*: Move {.inline.} = Move(startSquare: emptySquare(), targetSquare: emptySquare()) -func `+`*(a, b: Square): Square = (a.rank + b.rank, a.file + b.file) -func `-`*(a: Square): Square = (-a.rank, -a.file) -func `-`*(a, b: Square): Square = (a.rank - b.rank, a.file - b.file) +func `+`*(a, b: Square): Square {.inline.} = (a.rank + b.rank, a.file + b.file) +func `-`*(a: Square): Square {.inline.} = (-a.rank, -a.file) +func `-`*(a, b: Square): Square{.inline.} = (a.rank - b.rank, a.file - b.file) func isValid*(a: Square): bool {.inline.} = a.rank in 0..7 and a.file in 0..7 func isLightSquare(a: Square): bool {.inline.} = (a.rank + a.file and 2) == 0 proc generateMoves(self: ChessBoard, square: Square): seq[Move] @@ -169,6 +170,16 @@ proc extend[T](self: var seq[T], other: openarray[T]) {.inline.} = proc updateBoard*(self: ChessBoard) +func createMove(startSquare, targetSquare: string, flags: seq[MoveFlag] = @[]): Move = + result = Move(startSquare: startSquare.algebraicToSquare(), + targetSquare: targetSquare.algebraicToSquare(), flags: Default.uint16) + for flag in flags: + result.flags = result.flags or flag.uint16 + + +func makeSquare(rank, file: SomeInteger): Square = (rank: rank.int8, file: file.int8) + + # Due to our board layout, directions of movement are reversed for white and black, so # we need these helpers to avoid going mad with integer tuples and minus signs everywhere func topLeftDiagonal(color: PieceColor): Square {.inline.} = (if color == White: (-1, -1) else: (1, 1)) @@ -285,7 +296,7 @@ func getStartRank(piece: Piece): int {.inline.} = return 0 -func getKingStartingPosition(color: PieceColor): Square {.inline.} = +func getKingStartingSquare(color: PieceColor): Square {.inline.} = ## Retrieves the starting square of the king ## for the given color case color: @@ -334,7 +345,7 @@ proc newChessboard: ChessBoard = # Handy wrappers and utilities for handling low-level stuff func coordToIndex(row, col: SomeInteger): int {.inline.} = (row * 8) + col func coordToIndex(square: Square): int {.inline.} = coordToIndex(square.rank, square.file) -func indexToCoord(index: SomeInteger): Square {.inline.} = (index / 8, index mod 8) +func indexToCoord(index: SomeInteger): Square {.inline.} = ((index div 8).int8, (index mod 8).int8) # Indexing operations func `[]`(self: array[64, Piece], row, column: Natural): Piece {.inline.} = self[coordToIndex(row, column)] @@ -360,6 +371,40 @@ func toBin(x: Bitboard, b: Positive = 64): string = toBin(BiggestInt(x), b) func toBin(x: uint64, b: Positive = 64): string = toBin(Bitboard(x), b) +iterator items(self: Bitboard): Square = + ## Iterates ove the given bitboard + ## and returns all the squares that + ## are set + var bits = self.uint64 + while bits != 0: + yield Square(bits.countTrailingZeroBits().indexToCoord()) + bits = bits and bits - 1 + + +func pretty(self: Bitboard): string = + + iterator items(self: Bitboard): uint8 = + ## Iterates over all the bits in the + ## given bitboard + for i in 0..63: + yield self.uint64.bitsliced(i..i).uint8 + + + iterator pairs(self: Bitboard): (int, uint8) = + var i = 0 + for bit in self: + yield (i, bit) + inc(i) + + ## Returns a prettyfied version of + ## the given bitboard + for i, bit in self: + if i > 0 and i mod 8 == 0: + result &= "\n" + result &= $bit + + + func computeDiagonalBitboards: array[14, Bitboard] {.compileTime.} = ## Precomputes all the bitboards for diagonals ## at compile time @@ -702,7 +747,7 @@ func rowToFile(row: int): int8 {.inline.} = return indeces[row] -proc algebraicToSquare*(s: string): Square = +func algebraicToSquare*(s: string): Square = ## Converts a square square from algebraic ## notation to its corresponding row and column ## in the chess grid (0 indexed) @@ -722,7 +767,7 @@ proc algebraicToSquare*(s: string): Square = func squareToAlgebraic*(square: Square): string {.inline.} = - ## Converts a square from our internal row, column + ## Converts a square from our internal rank/file ## notation to a square in algebraic notation return &"{char(uint8(square.file) + uint8('a'))}{rowToFile(square.rank)}" @@ -875,7 +920,7 @@ proc canCastle*(self: ChessBoard, color: PieceColor = None): tuple[queen, king: # perform them because they're less expensive # King is not on its starting square - if self.getKing(color) != getKingStartingPosition(color): + if self.getKing(color) != getKingStartingSquare(color): return (false, false) if self.inCheck(color): # King can not castle out of check @@ -2334,7 +2379,7 @@ proc handleMoveCommand(board: ChessBoard, command: seq[string]): Move {.discarda var move = Move(startSquare: startSquare, targetSquare: targetSquare, flags: flags) - if board.getPiece(move.startSquare).kind == King and move.startSquare == board.getActiveColor().getKingStartingPosition(): + if board.getPiece(move.startSquare).kind == King and move.startSquare == board.getActiveColor().getKingStartingSquare(): if move.targetSquare == move.startSquare + longCastleKing(): move.flags = move.flags or CastleLong.uint16 elif move.targetSquare == move.startSquare + shortCastleKing(): @@ -2527,13 +2572,6 @@ proc main: int = return 0 -func createMove(startSquare, targetSquare: string, flags: seq[MoveFlag] = @[]): Move = - result = Move(startSquare: startSquare.algebraicToSquare(), - targetSquare: targetSquare.algebraicToSquare(), flags: Default.uint16) - for flag in flags: - result.flags = result.flags or flag.uint16 - - when isMainModule: @@ -2588,10 +2626,15 @@ when isMainModule: # Queens testPiece(b.getPiece("d1"), Queen, White) testPiece(b.getPiece("d8"), Queen, Black) - echo b.getBitboard(b.getPiece("a2")).toBin() - b.makeMove(createMove("a2", "a4", @[DoublePush])) - echo b.getBitboard(b.getPiece("a4")).toBin() - echo b.getEnPassantTarget() - setControlCHook(proc () {.noconv.} = quit(0)) - # quit(main()) \ No newline at end of file + b.makeMove(createMove("a2", "a4", @[DoublePush])) + let whitePawns = b.getBitboard(Pawn, White) + echo whitePawns.pretty() + for square in whitePawns: + echo square + doAssert b.getEnPassantTarget() == makeSquare(5, 0) + + + #[setControlCHook(proc () {.noconv.} = quit(0)) + + quit(main())]# \ No newline at end of file