Make position bitboard management more idiomatic
This commit is contained in:
parent
2b16b5ec61
commit
77ff697df7
|
@ -14,7 +14,6 @@
|
|||
|
||||
import std/strutils
|
||||
import std/strformat
|
||||
import std/bitops
|
||||
|
||||
|
||||
import nimfishpkg/bitboards
|
||||
|
@ -49,9 +48,9 @@ type
|
|||
# The side to move
|
||||
sideToMove: PieceColor
|
||||
# Positional bitboards for all pieces
|
||||
pieces: tuple[white, black: tuple[king, queens, rooks, bishops, knights, pawns: Bitboard]]
|
||||
# Pinned pieces for each side
|
||||
pins: tuple[white, black: Bitboard]
|
||||
pieces: array[2, array[6, Bitboard]]
|
||||
# Pinned pieces for the current side to move
|
||||
pins: Bitboard
|
||||
# Pieces checking the current side to move
|
||||
checkers: Bitboard
|
||||
|
||||
|
@ -135,6 +134,7 @@ func shortCastleRook*(color: PieceColor): Square {.inline.} = (if color == White
|
|||
|
||||
|
||||
proc inCheck*(self: ChessBoard): bool
|
||||
proc fromChar*(c: char): Piece
|
||||
|
||||
|
||||
proc newChessboard: ChessBoard =
|
||||
|
@ -142,50 +142,25 @@ proc newChessboard: ChessBoard =
|
|||
new(result)
|
||||
for i in 0..63:
|
||||
result.grid[i] = nullPiece()
|
||||
result.position = Position(enPassantSquare: nullSquare(), sideToMove: White)
|
||||
result.position = Position(enPassantSquare: nullSquare(), sideToMove: White, pieces: [
|
||||
[Bitboard(0), Bitboard(0), Bitboard(0), Bitboard(0), Bitboard(0), Bitboard(0)],
|
||||
[Bitboard(0), Bitboard(0), Bitboard(0), Bitboard(0), Bitboard(0), Bitboard(0)]])
|
||||
|
||||
|
||||
# Indexing operations
|
||||
func `[]`(self: array[64, Piece], square: Square): Piece {.inline.} = self[square.int8]
|
||||
func `[]=`(self: var array[64, Piece], square: Square, piece: Piece) {.inline.} = self[square.int8] = piece
|
||||
|
||||
|
||||
func `[]`(self: array[2, array[6, Bitboard]], color: PieceColor): ptr array[6, Bitboard] {.inline.} = addr self[color.int]
|
||||
func `[]`(self: array[6, Bitboard], kind: PieceKind): Bitboard {.inline.} = self[kind.int]
|
||||
func `[]=`(self: var array[6, Bitboard], kind: PieceKind, bitboard: Bitboard) {.inline.} = self[kind.int] = 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:
|
||||
case kind:
|
||||
of Pawn:
|
||||
return self.position.pieces.white.pawns
|
||||
of Knight:
|
||||
return self.position.pieces.white.knights
|
||||
of Bishop:
|
||||
return self.position.pieces.white.bishops
|
||||
of Rook:
|
||||
return self.position.pieces.white.rooks
|
||||
of Queen:
|
||||
return self.position.pieces.white.queens
|
||||
of King:
|
||||
return self.position.pieces.white.king
|
||||
else:
|
||||
discard
|
||||
of Black:
|
||||
case kind:
|
||||
of Pawn:
|
||||
return self.position.pieces.black.pawns
|
||||
of Knight:
|
||||
return self.position.pieces.black.knights
|
||||
of Bishop:
|
||||
return self.position.pieces.black.bishops
|
||||
of Rook:
|
||||
return self.position.pieces.black.rooks
|
||||
of Queen:
|
||||
return self.position.pieces.black.queens
|
||||
of King:
|
||||
return self.position.pieces.black.king
|
||||
else:
|
||||
discard
|
||||
else:
|
||||
discard
|
||||
return self.position.pieces[color.int][kind.int]
|
||||
|
||||
|
||||
|
||||
func getBitboard*(self: ChessBoard, piece: Piece): Bitboard =
|
||||
|
@ -221,52 +196,11 @@ proc newChessboardFromFEN*(fen: string): ChessBoard =
|
|||
case c.toLowerAscii():
|
||||
# Piece
|
||||
of 'r', 'n', 'b', 'q', 'k', 'p':
|
||||
let
|
||||
square: Square = makeSquare(row, column)
|
||||
bitIndex = square.int8
|
||||
# We know for a fact these values are in our
|
||||
# enumeration, so all is good
|
||||
{.warning[HoleEnumConv]:off.}
|
||||
piece = Piece(kind: PieceKind(c.toLowerAscii()), color: if c.isUpperAscii(): White else: Black)
|
||||
case piece.color:
|
||||
of Black:
|
||||
case piece.kind:
|
||||
of Pawn:
|
||||
result.position.pieces.black.pawns.uint64.uint64.setBit(bitIndex)
|
||||
of Bishop:
|
||||
result.position.pieces.black.bishops.uint64.setBit(bitIndex)
|
||||
of Knight:
|
||||
result.position.pieces.black.knights.uint64.setBit(bitIndex)
|
||||
of Rook:
|
||||
result.position.pieces.black.rooks.uint64.setBit(bitIndex)
|
||||
of Queen:
|
||||
result.position.pieces.black.queens.uint64.setBit(bitIndex)
|
||||
of King:
|
||||
if result.position.pieces.black.king != 0:
|
||||
raise newException(ValueError, "invalid position: exactly one king of each color must be present")
|
||||
result.position.pieces.black.king.uint64.setBit(bitIndex)
|
||||
else:
|
||||
discard
|
||||
of White:
|
||||
case piece.kind:
|
||||
of Pawn:
|
||||
result.position.pieces.white.pawns.uint64.setBit(bitIndex)
|
||||
of Bishop:
|
||||
result.position.pieces.white.bishops.uint64.setBit(bitIndex)
|
||||
of Knight:
|
||||
result.position.pieces.white.knights.uint64.setBit(bitIndex)
|
||||
of Rook:
|
||||
result.position.pieces.white.rooks.uint64.setBit(bitIndex)
|
||||
of Queen:
|
||||
result.position.pieces.white.queens.uint64.setBit(bitIndex)
|
||||
of King:
|
||||
if result.position.pieces.white.king != 0:
|
||||
raise newException(ValueError, "invalid position: exactly one king of each color must be present")
|
||||
result.position.pieces.white.king.uint64.setBit(bitIndex)
|
||||
else:
|
||||
discard
|
||||
else:
|
||||
discard
|
||||
let square: Square = makeSquare(row, column)
|
||||
piece = c.fromChar()
|
||||
var b = result.position.pieces[piece.color][piece.kind]
|
||||
b.setBit(square)
|
||||
result.position.pieces[piece.color][piece.kind] = b
|
||||
result.grid[square] = piece
|
||||
inc(column)
|
||||
of '/':
|
||||
|
@ -340,9 +274,6 @@ proc newChessboardFromFEN*(fen: string): ChessBoard =
|
|||
else:
|
||||
raise newException(ValueError, "invalid FEN: too many fields in FEN string")
|
||||
inc(index)
|
||||
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")
|
||||
|
||||
|
||||
proc newDefaultChessboard*: ChessBoard {.inline.} =
|
||||
|
@ -355,41 +286,8 @@ proc countPieces*(self: ChessBoard, kind: PieceKind, color: PieceColor): int =
|
|||
## Returns the number of pieces with
|
||||
## the given color and type in the
|
||||
## current position
|
||||
case color:
|
||||
of White:
|
||||
case kind:
|
||||
of Pawn:
|
||||
return self.position.pieces.white.pawns.uint64.countSetBits()
|
||||
of Bishop:
|
||||
return self.position.pieces.white.bishops.uint64.countSetBits()
|
||||
of Knight:
|
||||
return self.position.pieces.white.knights.uint64.countSetBits()
|
||||
of Rook:
|
||||
return self.position.pieces.white.rooks.uint64.countSetBits()
|
||||
of Queen:
|
||||
return self.position.pieces.white.queens.uint64.countSetBits()
|
||||
of King:
|
||||
return self.position.pieces.white.king.uint64.countSetBits()
|
||||
else:
|
||||
raise newException(ValueError, "invalid piece type")
|
||||
of Black:
|
||||
case kind:
|
||||
of Pawn:
|
||||
return self.position.pieces.black.pawns.uint64.countSetBits()
|
||||
of Bishop:
|
||||
return self.position.pieces.black.bishops.uint64.countSetBits()
|
||||
of Knight:
|
||||
return self.position.pieces.black.knights.uint64.countSetBits()
|
||||
of Rook:
|
||||
return self.position.pieces.black.rooks.uint64.countSetBits()
|
||||
of Queen:
|
||||
return self.position.pieces.black.queens.uint64.countSetBits()
|
||||
of King:
|
||||
return self.position.pieces.black.king.uint64.countSetBits()
|
||||
else:
|
||||
raise newException(ValueError, "invalid piece type")
|
||||
of None:
|
||||
raise newException(ValueError, "invalid piece color")
|
||||
return self.position.pieces[color][kind].countSetBits()
|
||||
|
||||
|
||||
|
||||
func countPieces*(self: ChessBoard, piece: Piece): int {.inline.} =
|
||||
|
@ -474,16 +372,9 @@ func getFlags*(move: Move): seq[MoveFlag] =
|
|||
|
||||
proc getOccupancyFor(self: ChessBoard, color: PieceColor): Bitboard =
|
||||
## Get the occupancy bitboard for every piece of the given color
|
||||
case color:
|
||||
of White:
|
||||
let b = self.position.pieces.white
|
||||
return b.pawns or b.knights or b.bishops or b.rooks or b.queens or b.king
|
||||
of Black:
|
||||
let b = self.position.pieces.black
|
||||
return b.pawns or b.knights or b.bishops or b.rooks or b.queens or b.king
|
||||
else:
|
||||
# huh?
|
||||
discard
|
||||
result = Bitboard(0)
|
||||
for b in self.position.pieces[color][]:
|
||||
result = result or b
|
||||
|
||||
|
||||
proc getOccupancy(self: ChessBoard): Bitboard =
|
||||
|
@ -738,81 +629,17 @@ proc removePieceFromBitboard(self: ChessBoard, square: Square) =
|
|||
## Removes a piece at the given square in the chessboard from
|
||||
## its respective bitboard
|
||||
let piece = self.grid[square]
|
||||
case piece.color:
|
||||
of White:
|
||||
case piece.kind:
|
||||
of Pawn:
|
||||
self.position.pieces.white.pawns.uint64.clearBit(square.int8)
|
||||
of Bishop:
|
||||
self.position.pieces.white.bishops.uint64.clearBit(square.int8)
|
||||
of Knight:
|
||||
self.position.pieces.white.knights.uint64.clearBit(square.int8)
|
||||
of Rook:
|
||||
self.position.pieces.white.rooks.uint64.clearBit(square.int8)
|
||||
of Queen:
|
||||
self.position.pieces.white.queens.uint64.clearBit(square.int8)
|
||||
of King:
|
||||
self.position.pieces.white.king.uint64.clearBit(square.int8)
|
||||
of Empty:
|
||||
doAssert false, &"cannot remove empty white piece from {square}"
|
||||
of Black:
|
||||
case piece.kind:
|
||||
of Pawn:
|
||||
self.position.pieces.black.pawns.uint64.clearBit(square.int8)
|
||||
of Bishop:
|
||||
self.position.pieces.black.bishops.uint64.clearBit(square.int8)
|
||||
of Knight:
|
||||
self.position.pieces.black.knights.uint64.clearBit(square.int8)
|
||||
of Rook:
|
||||
self.position.pieces.black.rooks.uint64.clearBit(square.int8)
|
||||
of Queen:
|
||||
self.position.pieces.black.queens.uint64.clearBit(square.int8)
|
||||
of King:
|
||||
self.position.pieces.black.king.uint64.clearBit(square.int8)
|
||||
of Empty:
|
||||
doAssert false, &"cannot remove empty black piece from {square}"
|
||||
else:
|
||||
doAssert false, &"cannot remove empty piece from colorless square {square}"
|
||||
var b = self.position.pieces[piece.color][piece.kind]
|
||||
b.clearBit(square)
|
||||
self.position.pieces[piece.color][piece.kind] = b
|
||||
|
||||
|
||||
proc addPieceToBitboard(self: ChessBoard, square: Square, piece: Piece) =
|
||||
## Adds the given piece at the given square in the chessboard to
|
||||
## its respective bitboard
|
||||
case piece.color:
|
||||
of White:
|
||||
case piece.kind:
|
||||
of Pawn:
|
||||
self.position.pieces.white.pawns.uint64.setBit(square.int8)
|
||||
of Bishop:
|
||||
self.position.pieces.white.bishops.uint64.setBit(square.int8)
|
||||
of Knight:
|
||||
self.position.pieces.white.knights.uint64.setBit(square.int8)
|
||||
of Rook:
|
||||
self.position.pieces.white.rooks.uint64.setBit(square.int8)
|
||||
of Queen:
|
||||
self.position.pieces.white.queens.uint64.setBit(square.int8)
|
||||
of King:
|
||||
self.position.pieces.white.king.uint64.setBit(square.int8)
|
||||
else:
|
||||
discard
|
||||
of Black:
|
||||
case piece.kind:
|
||||
of Pawn:
|
||||
self.position.pieces.black.pawns.uint64.setBit(square.int8)
|
||||
of Bishop:
|
||||
self.position.pieces.black.bishops.uint64.setBit(square.int8)
|
||||
of Knight:
|
||||
self.position.pieces.black.knights.uint64.setBit(square.int8)
|
||||
of Rook:
|
||||
self.position.pieces.black.rooks.uint64.setBit(square.int8)
|
||||
of Queen:
|
||||
self.position.pieces.black.queens.uint64.setBit(square.int8)
|
||||
of King:
|
||||
self.position.pieces.black.king.uint64.setBit(square.int8)
|
||||
else:
|
||||
discard
|
||||
else:
|
||||
discard
|
||||
var b = self.position.pieces[piece.color][piece.kind]
|
||||
b.setBit(square)
|
||||
self.position.pieces[piece.color][piece.kind] = b
|
||||
|
||||
|
||||
proc removePiece(self: ChessBoard, square: Square) =
|
||||
|
@ -924,29 +751,29 @@ proc update*(self: ChessBoard) =
|
|||
## in the chessboard
|
||||
for i in 0..63:
|
||||
self.grid[i] = nullPiece()
|
||||
for sq in self.position.pieces.white.pawns:
|
||||
for sq in self.position.pieces[White][Pawn]:
|
||||
self.grid[sq] = Piece(color: White, kind: Pawn)
|
||||
for sq in self.position.pieces.black.pawns:
|
||||
for sq in self.position.pieces[Black][Pawn]:
|
||||
self.grid[sq] = Piece(color: Black, kind: Pawn)
|
||||
for sq in self.position.pieces.white.bishops:
|
||||
for sq in self.position.pieces[White][Bishop]:
|
||||
self.grid[sq] = Piece(color: White, kind: Bishop)
|
||||
for sq in self.position.pieces.black.bishops:
|
||||
for sq in self.position.pieces[Black][Bishop]:
|
||||
self.grid[sq] = Piece(color: Black, kind: Bishop)
|
||||
for sq in self.position.pieces.white.knights:
|
||||
for sq in self.position.pieces[White][Knight]:
|
||||
self.grid[sq] = Piece(color: White, kind: Knight)
|
||||
for sq in self.position.pieces.black.knights:
|
||||
for sq in self.position.pieces[Black][Knight]:
|
||||
self.grid[sq] = Piece(color: Black, kind: Knight)
|
||||
for sq in self.position.pieces.white.rooks:
|
||||
for sq in self.position.pieces[White][Rook]:
|
||||
self.grid[sq] = Piece(color: White, kind: Rook)
|
||||
for sq in self.position.pieces.black.rooks:
|
||||
for sq in self.position.pieces[Black][Rook]:
|
||||
self.grid[sq] = Piece(color: Black, kind: Rook)
|
||||
for sq in self.position.pieces.white.queens:
|
||||
for sq in self.position.pieces[White][Queen]:
|
||||
self.grid[sq] = Piece(color: White, kind: Queen)
|
||||
for sq in self.position.pieces.black.queens:
|
||||
for sq in self.position.pieces[Black][Queen]:
|
||||
self.grid[sq] = Piece(color: Black, kind: Queen)
|
||||
for sq in self.position.pieces.white.king:
|
||||
for sq in self.position.pieces[White][King]:
|
||||
self.grid[sq] = Piece(color: White, kind: King)
|
||||
for sq in self.position.pieces.black.king:
|
||||
for sq in self.position.pieces[Black][King]:
|
||||
self.grid[sq] = Piece(color: Black, kind: King)
|
||||
|
||||
|
||||
|
@ -973,9 +800,47 @@ proc makeMove*(self: ChessBoard, move: Move): Move {.discardable.} =
|
|||
|
||||
|
||||
proc toChar*(piece: Piece): char =
|
||||
case piece.kind:
|
||||
of Bishop:
|
||||
result = 'b'
|
||||
of King:
|
||||
result = 'k'
|
||||
of Knight:
|
||||
result = 'n'
|
||||
of Pawn:
|
||||
result = 'p'
|
||||
of Queen:
|
||||
result = 'q'
|
||||
of Rook:
|
||||
result = 'r'
|
||||
else:
|
||||
discard
|
||||
if piece.color == White:
|
||||
return char(piece.kind).toUpperAscii()
|
||||
return char(piece.kind)
|
||||
result = result.toUpperAscii()
|
||||
|
||||
|
||||
proc fromChar*(c: char): Piece =
|
||||
var
|
||||
kind: PieceKind
|
||||
color = Black
|
||||
case c.toLowerAscii():
|
||||
of 'b':
|
||||
kind = Bishop
|
||||
of 'k':
|
||||
kind = King
|
||||
of 'n':
|
||||
kind = Knight
|
||||
of 'p':
|
||||
kind = Pawn
|
||||
of 'q':
|
||||
kind = Queen
|
||||
of 'r':
|
||||
kind = Rook
|
||||
else:
|
||||
discard
|
||||
if c.isUpperAscii():
|
||||
color = White
|
||||
result = Piece(kind: kind, color: color)
|
||||
|
||||
|
||||
proc `$`*(self: ChessBoard): string =
|
||||
|
|
|
@ -45,6 +45,13 @@ func `==`*(a, b: Bitboard): bool {.inline.} = a.uint64 == b.uint64
|
|||
func `==`*(a: Bitboard, b: SomeInteger): bool {.inline.} = a.uint64 == b.uint64
|
||||
func `!=`*(a, b: Bitboard): bool {.inline.} = a.uint64 != b.uint64
|
||||
func `!=`*(a: Bitboard, b: SomeInteger): bool {.inline.} = a.uint64 != b.uint64
|
||||
func countSetBits*(a: Bitboard): int = a.uint64.countSetBits()
|
||||
func countLeadingZeroBits*(a: Bitboard): int = a.uint64.countLeadingZeroBits()
|
||||
func countTrailingZeroBits*(a: Bitboard): int = a.uint64.countTrailingZeroBits()
|
||||
func clearBit*(a: var Bitboard, bit: SomeInteger) = a.uint64.clearBit(bit)
|
||||
func setBit*(a: var Bitboard, bit: SomeInteger) = a.uint64.setBit(bit)
|
||||
func clearBit*(a: var Bitboard, bit: Square) = a.uint64.clearBit(bit.int)
|
||||
func setBit*(a: var Bitboard, bit: Square) = a.uint64.setBit(bit.int)
|
||||
|
||||
|
||||
func getFileMask*(file: int): Bitboard = Bitboard(0x101010101010101'u64) shl file.uint64
|
||||
|
|
|
@ -9,25 +9,26 @@ type
|
|||
|
||||
PieceColor* = enum
|
||||
## A piece color enumeration
|
||||
None = 0'i8
|
||||
White
|
||||
Black
|
||||
White = 0'i8
|
||||
Black = 1
|
||||
None
|
||||
|
||||
PieceKind* = enum
|
||||
## A chess piece enumeration
|
||||
Empty = 0'i8, # No piece
|
||||
Bishop = 'b',
|
||||
King = 'k'
|
||||
Knight = 'n',
|
||||
Pawn = 'p',
|
||||
Queen = 'q',
|
||||
Rook = 'r',
|
||||
Bishop = 0'i8
|
||||
King = 1
|
||||
Knight = 2
|
||||
Pawn = 3
|
||||
Queen = 4
|
||||
Rook = 5
|
||||
Empty = 6 # No piece
|
||||
|
||||
|
||||
Piece* = object
|
||||
## A chess piece
|
||||
color*: PieceColor
|
||||
kind*: PieceKind
|
||||
|
||||
|
||||
|
||||
func nullPiece*: Piece {.inline.} = Piece(kind: Empty, color: None)
|
||||
func nullSquare*: Square {.inline.} = Square(-1'i8)
|
||||
|
|
|
@ -421,6 +421,8 @@ proc commandLoop*: int =
|
|||
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(): \"yes\" else: \"no\")}"
|
||||
of "quit":
|
||||
return 0
|
||||
else:
|
||||
echo &"Unknown command '{cmd[0]}'. Type 'help' for more information."
|
||||
except IOError:
|
||||
|
|
Loading…
Reference in New Issue