Drop old attack tracking system in preparation for bitboards

This commit is contained in:
Mattia Giambirtone 2024-04-16 08:50:42 +02:00
parent c9988cd939
commit e1ccdc728e
1 changed files with 67 additions and 64 deletions

View File

@ -104,12 +104,7 @@ type
fullMoveCount: int8
# En passant target square (see https://en.wikipedia.org/wiki/En_passant)
enPassantSquare*: Square
# Squares of all pieces
pieces: tuple[white, black: Pieces]
# Squares attacked by both sides
attacked: tuple[white, black: Attacked]
# Pieces pinned by both sides (only absolute pins)
pinned: tuple[white, black: Attacked]
# Active color
turn: PieceColor
@ -144,18 +139,18 @@ 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]
proc getAttackers*(self: ChessBoard, square: Square, color: PieceColor): seq[Square]
proc getAttackFor*(self: ChessBoard, source, target: Square): tuple[source, target, direction: Square]
proc isAttacked*(self: ChessBoard, square: Square, color: PieceColor = None): bool
# proc getAttackers*(self: ChessBoard, square: Square, color: PieceColor): seq[Square]
# proc getAttackFor*(self: ChessBoard, source, target: Square): tuple[source, target, direction: Square]
# proc isAttacked*(self: ChessBoard, square: Square, color: PieceColor = None): bool
proc isLegal(self: ChessBoard, move: Move): bool {.inline.}
proc doMove(self: ChessBoard, move: Move)
proc pretty*(self: ChessBoard): string
proc spawnPiece(self: ChessBoard, square: Square, piece: Piece)
proc updateAttackedSquares(self: ChessBoard)
proc updateSlidingAttacks(self: ChessBoard)
proc getPinnedDirections(self: ChessBoard, square: Square): seq[Square]
proc getAttacks*(self: ChessBoard, square: Square): Attacked
proc getSlidingAttacks(self: ChessBoard, square: Square): tuple[attacks: Attacked, pins: Attacked]
# proc updateAttackedSquares(self: ChessBoard)
# proc updateSlidingAttacks(self: ChessBoard)
# proc getPinnedDirections(self: ChessBoard, square: Square): seq[Square]
# proc getAttacks*(self: ChessBoard, square: Square): Attacked
# proc getSlidingAttacks(self: ChessBoard, square: Square): tuple[attacks: Attacked, pins: Attacked]
proc inCheck*(self: ChessBoard, color: PieceColor = None): bool
proc toFEN*(self: ChessBoard): string
proc undoLastMove*(self: ChessBoard)
@ -325,22 +320,8 @@ proc newChessboard: ChessBoard =
new(result)
for i in 0..63:
result.grid[i] = emptyPiece()
result.position = Position(attacked: (@[], @[]),
enPassantSquare: emptySquare(),
turn: White,
fullMoveCount: 1,
pieces: (white: (king: emptySquare(),
queens: @[],
rooks: @[],
bishops: @[],
knights: @[],
pawns: @[]),
black: (king: emptySquare(),
queens: @[],
rooks: @[],
bishops: @[],
knights: @[],
pawns: @[])))
result.position = Position(enPassantSquare: emptySquare(), turn: White)
# Handy wrappers and utilities for handling low-level stuff
func coordToIndex(row, col: SomeInteger): int {.inline.} = (row * 8) + col
@ -358,6 +339,14 @@ func `shl`(a: Bitboard, x: Positive): Bitboard = Bitboard(a.uint64 shl x)
func `shr`(a: Bitboard, x: Positive): Bitboard = Bitboard(a.uint64 shr x)
func `and`(a, b: Bitboard): Bitboard = Bitboard(a.uint64 and b.uint64)
func `shr`(a, b: Bitboard): Bitboard = Bitboard(a.uint64 and b.uint64)
func `+`(a, b: Bitboard): Bitboard = Bitboard(a.uint64 + b.uint64)
func `-`(a, b: Bitboard): Bitboard = Bitboard(a.uint64 - b.uint64)
func `div`(a, b: Bitboard): Bitboard = Bitboard(a.uint64 div b.uint64)
func `*`(a, b: Bitboard): Bitboard = Bitboard(a.uint64 * b.uint64)
func `+`(a: Bitboard, b: SomeUnsignedInt): Bitboard = Bitboard(a.uint64 + b)
func `-`(a: Bitboard, b: SomeUnsignedInt): Bitboard = Bitboard(a.uint64 - b.uint64)
func `div`(a: Bitboard, b: SomeUnsignedInt): Bitboard = Bitboard(a.uint64 div b)
func `*`(a: Bitboard, b: SomeUnsignedInt): Bitboard = Bitboard(a.uint64 * b)
func `==`(a, b: Bitboard): bool {.inline.} = a.uint64 == b.uint64
func `==`(a: Bitboard, b: SomeInteger): bool {.inline.} = a.uint64 == b.uint64
@ -367,6 +356,8 @@ func getRankMask(rank: Positive): Bitboard = Bitboard(uint64.high()) shl Positiv
func squareToBitboard(square: SomeInteger): Bitboard = Bitboard(1'u64) shl square.uint64
func squareToBitboard(square: Square): Bitboard = squareToBitboard(coordToIndex(square))
proc bitboardToSquare(b: Bitboard): Square = Square(b.uint64.countTrailingZeroBits().indexToCoord())
func toBin(x: Bitboard, b: Positive = 64): string = toBin(BiggestInt(x), b)
func toBin(x: uint64, b: Positive = 64): string = toBin(Bitboard(x), b)
@ -375,9 +366,9 @@ iterator items(self: Bitboard): Square =
## Iterates ove the given bitboard
## and returns all the squares that
## are set
var bits = self.uint64
var bits = self
while bits != 0:
yield Square(bits.countTrailingZeroBits().indexToCoord())
yield bits.bitboardToSquare()
bits = bits and bits - 1
@ -668,10 +659,10 @@ proc newChessboardFromFEN*(fen: string): ChessBoard =
else:
raise newException(ValueError, "invalid FEN: too many fields in FEN string")
inc(index)
result.updateAttackedSquares()
if result.inCheck(result.getActiveColor().opposite):
# result.updateAttackedSquares()
#[if result.inCheck(result.getActiveColor().opposite):
# Opponent king cannot be captured on the next move
raise newException(ValueError, "invalid position: opponent king can be captured")
raise newException(ValueError, "invalid position: opponent king can be captured")]#
if result.position.bitboards.white.king == Bitboard(0) or result.position.bitboards.black.king == Bitboard(0):
# Both kings must be on the board
raise newException(ValueError, "invalid position: exactly one king of each color must be present")
@ -854,13 +845,17 @@ func getKing(self: ChessBoard, color: PieceColor = None): Square {.inline.} =
color = self.getActiveColor()
case color:
of White:
return self.position.pieces.white.king
return self.position.bitboards.white.king.uint64.countTrailingZeroBits().indexToCoord()
of Black:
return self.position.pieces.black.king
return self.position.bitboards.black.king.uint64.countTrailingZeroBits().indexToCoord()
else:
discard
# TODO
proc inCheck*(self: ChessBoard, color: PieceColor = None): bool = false
proc canCastle*(self: ChessBoard, color: PieceColor = None): tuple[queen, king: bool] {.inline.} = (false, false)
#[
proc inCheck*(self: ChessBoard, color: PieceColor = None): bool =
## Returns whether the given color's
## king is in check. If the color is
@ -869,7 +864,7 @@ proc inCheck*(self: ChessBoard, color: PieceColor = None): bool =
var color = color
if color == None:
color = self.getActiveColor()
case color:
#[case color:
of White:
result = self.isAttacked(self.position.pieces.white.king, Black)
of Black:
@ -877,6 +872,7 @@ proc inCheck*(self: ChessBoard, color: PieceColor = None): bool =
else:
# Unreachable
discard
]#
proc inDoubleCheck*(self: ChessBoard, color: PieceColor = None): bool =
@ -935,11 +931,11 @@ proc canCastle*(self: ChessBoard, color: PieceColor = None): tuple[queen, king:
# is temporarily prohibited on that side
case color:
of White:
square = self.position.pieces.white.king
square = self.position.bitboards.white.king.bitboardToSquare()
queenSide = color.leftSide()
kingSide = color.rightSide()
of Black:
square = self.position.pieces.black.king
square = self.position.bitboards.black.king.bitboardToSquare()
queenSide = color.rightSide()
kingSide = color.leftSide()
of None:
@ -1049,12 +1045,13 @@ proc getCheckResolutions(self: ChessBoard, color: PieceColor): seq[Square] =
if not square.isValid():
break
result.add(square)
]#
proc generatePawnMoves(self: ChessBoard, square: Square): seq[Move] =
## Generates the possible moves for the pawn in the given
## square
var
#[var
piece = self.grid[square.rank, square.file]
directions: seq[Square] = @[]
assert piece.kind == Pawn, &"generatePawnMoves called on a {piece.kind}"
@ -1136,10 +1133,12 @@ proc generatePawnMoves(self: ChessBoard, square: Square): seq[Move] =
result.add(Move(startSquare: square, targetSquare: target, flags: promotionType.uint16 or flags))
continue
result.add(Move(startSquare: square, targetSquare: target, flags: flags))
]#
proc generateSlidingMoves(self: ChessBoard, square: Square): seq[Move] =
## Generates moves for the sliding piece in the given square
#[
let piece = self.grid[square.rank, square.file]
assert piece.kind in [Bishop, Rook, Queen], &"generateSlidingMoves called on a {piece.kind}"
var directions: seq[Square] = @[]
@ -1198,10 +1197,12 @@ proc generateSlidingMoves(self: ChessBoard, square: Square): seq[Move] =
break
# Target square is empty, keep going
result.add(Move(startSquare: square, targetSquare: square))
]#
proc generateKingMoves(self: ChessBoard, square: Square): seq[Move] =
## Generates moves for the king in the given square
#[
var
piece = self.grid[square.rank, square.file]
assert piece.kind == King, &"generateKingMoves called on a {piece.kind}"
@ -1243,10 +1244,12 @@ proc generateKingMoves(self: ChessBoard, square: Square): seq[Move] =
# Target square is empty or contains an enemy piece:
# All good for us!
result.add(Move(startSquare: square, targetSquare: square, flags: flag.uint16))
]#
proc generateKnightMoves(self: ChessBoard, square: Square): seq[Move] =
## Generates moves for the knight in the given square
#[
var
piece = self.grid[square.rank, square.file]
assert piece.kind == Knight, &"generateKnightMoves called on a {piece.kind}"
@ -1283,6 +1286,7 @@ proc generateKnightMoves(self: ChessBoard, square: Square): seq[Move] =
else:
# Target square is empty
result.add(Move(startSquare: square, targetSquare: square))
]#
proc checkInsufficientMaterialPieceCount(self: ChessBoard, color: PieceColor): bool =
@ -1327,12 +1331,12 @@ proc checkInsufficientMaterial(self: ChessBoard): bool =
var
darkSquare = 0
lightSquare = 0
for bishop in self.position.pieces.black.bishops:
for bishop in self.position.bitboards.black.bishops:
if bishop.isLightSquare():
lightSquare += 1
else:
darkSquare += 1
for bishop in self.position.pieces.white.bishops:
for bishop in self.position.bitboards.white.bishops:
if bishop.isLightSquare():
lightSquare += 1
else:
@ -1375,8 +1379,8 @@ proc generateAllMoves*(self: ChessBoard): seq[Move] =
if self.grid[i, j].color == self.getActiveColor():
for move in self.generateMoves((int8(i), int8(j))):
result.add(move)
#[
proc isAttacked*(self: ChessBoard, square: Square, color: PieceColor = None): bool =
## Returns whether the given square is attacked
## by the given color
@ -1662,7 +1666,7 @@ proc updateAttackedSquares(self: ChessBoard) =
self.updateKnightAttacks()
# Kings
self.updateKingAttacks()
]#
proc removePieceFromBitboard(self: ChessBoard, square: Square) =
## Removes a piece at the given square in the chessboard from
@ -1751,8 +1755,8 @@ proc removePiece(self: ChessBoard, square: Square, attack: bool = true) =
var piece = self.grid[square]
self.grid[square] = emptyPiece()
self.removePieceFromBitboard(square)
if attack:
self.updateAttackedSquares()
#[if attack:
self.updateAttackedSquares()]#
proc updateMoveBitboards(self: ChessBoard, move: Move) =
@ -1826,8 +1830,8 @@ proc movePiece(self: ChessBoard, move: Move, attack: bool = true) =
self.grid[move.startSquare] = emptyPiece()
# Actually move the piece on the board
self.grid[move.targetSquare] = piece
if attack:
self.updateAttackedSquares()
#[if attack:
self.updateAttackedSquares()]#
proc movePiece(self: ChessBoard, startSquare, targetSquare: Square, attack: bool = true) =
@ -1925,7 +1929,6 @@ proc doMove(self: ChessBoard, move: Move) =
fullMoveCount: fullMoveCount,
turn: self.getActiveColor().opposite,
castlingAvailable: castlingAvailable,
pieces: self.position.pieces,
enPassantSquare: enPassantTarget,
bitboards: self.position.bitboards
)
@ -1975,7 +1978,7 @@ proc doMove(self: ChessBoard, move: Move) =
else:
# Unreachable
discard
self.updateAttackedSquares()
#self.updateAttackedSquares()
proc spawnPiece(self: ChessBoard, square: Square, piece: Piece) =
@ -1993,28 +1996,28 @@ proc updateBoard*(self: ChessBoard) =
## in the chessboard
for i in 0..63:
self.grid[i] = emptyPiece()
for sq in self.position.pieces.white.pawns:
for sq in self.position.bitboards.white.pawns:
self.grid[sq] = Piece(color: White, kind: Pawn)
for sq in self.position.pieces.black.pawns:
for sq in self.position.bitboards.black.pawns:
self.grid[sq] = Piece(color: Black, kind: Pawn)
for sq in self.position.pieces.white.bishops:
for sq in self.position.bitboards.white.bishops:
self.grid[sq] = Piece(color: White, kind: Bishop)
for sq in self.position.pieces.black.bishops:
for sq in self.position.bitboards.black.bishops:
self.grid[sq] = Piece(color: Black, kind: Bishop)
for sq in self.position.pieces.white.knights:
for sq in self.position.bitboards.white.knights:
self.grid[sq] = Piece(color: White, kind: Knight)
for sq in self.position.pieces.black.knights:
for sq in self.position.bitboards.black.knights:
self.grid[sq] = Piece(color: Black, kind: Knight)
for sq in self.position.pieces.white.rooks:
for sq in self.position.bitboards.white.rooks:
self.grid[sq] = Piece(color: White, kind: Rook)
for sq in self.position.pieces.black.rooks:
for sq in self.position.bitboards.black.rooks:
self.grid[sq] = Piece(color: Black, kind: Rook)
for sq in self.position.pieces.white.queens:
for sq in self.position.bitboards.white.queens:
self.grid[sq] = Piece(color: White, kind: Queen)
for sq in self.position.pieces.black.queens:
for sq in self.position.bitboards.black.queens:
self.grid[sq] = Piece(color: Black, kind: Queen)
self.grid[self.position.pieces.white.king] = Piece(color: White, kind: King)
self.grid[self.position.pieces.black.king] = Piece(color: Black, kind: King)
self.grid[self.position.bitboards.white.king.bitboardToSquare()] = Piece(color: White, kind: King)
self.grid[self.position.bitboards.black.king.bitboardToSquare()] = Piece(color: Black, kind: King)
proc undoLastMove*(self: ChessBoard) =