Minor refactoring and cleanup. Switch away from boehm GC as that's too slow
This commit is contained in:
parent
70490f5698
commit
da82878ebb
|
@ -3,7 +3,4 @@
|
|||
-d:danger
|
||||
--passL:"-flto"
|
||||
--passC:"-flto -march=native -mtune=native"
|
||||
--mm:boehm
|
||||
--stackTrace
|
||||
--lineTrace
|
||||
--debugger:native
|
||||
--mm:atomicArc
|
||||
|
|
|
@ -32,7 +32,7 @@ export pieces, position, bitboards, moves, magics, rays, zobrist
|
|||
|
||||
|
||||
type
|
||||
Chessboard* = ref object
|
||||
Chessboard* = object
|
||||
## A chessboard
|
||||
|
||||
# The actual board where pieces live
|
||||
|
@ -45,13 +45,12 @@ type
|
|||
|
||||
# A bunch of simple utility functions and forward declarations
|
||||
proc toFEN*(self: Chessboard): string
|
||||
proc updateChecksAndPins*(self: Chessboard)
|
||||
proc hash*(self: Chessboard)
|
||||
proc updateChecksAndPins*(self: var Chessboard)
|
||||
proc hash*(self: var Chessboard)
|
||||
|
||||
|
||||
proc newChessboard*: Chessboard =
|
||||
## Returns a new, empty chessboard
|
||||
new(result)
|
||||
for i in 0..63:
|
||||
result.grid[i] = nullPiece()
|
||||
result.position = Position(enPassantSquare: nullSquare(), sideToMove: White)
|
||||
|
@ -102,7 +101,7 @@ proc newChessboardFromFEN*(fen: string): Chessboard =
|
|||
of 'r', 'n', 'b', 'q', 'k', 'p':
|
||||
let square = makeSquare(row, column)
|
||||
piece = c.fromChar()
|
||||
result.position.pieces[piece.color][piece.kind][].setBit(square)
|
||||
result.position.pieces[piece.color][piece.kind].setBit(square)
|
||||
result.grid[square] = piece
|
||||
inc(column)
|
||||
of '/':
|
||||
|
@ -133,13 +132,13 @@ proc newChessboardFromFEN*(fen: string): Chessboard =
|
|||
of '-':
|
||||
discard
|
||||
of 'K':
|
||||
result.position.castlingAvailability[White.int].king = true
|
||||
result.position.castlingAvailability[White].king = true
|
||||
of 'Q':
|
||||
result.position.castlingAvailability[White.int].queen = true
|
||||
result.position.castlingAvailability[White].queen = true
|
||||
of 'k':
|
||||
result.position.castlingAvailability[Black.int].king = true
|
||||
result.position.castlingAvailability[Black].king = true
|
||||
of 'q':
|
||||
result.position.castlingAvailability[Black.int].queen = true
|
||||
result.position.castlingAvailability[Black].queen = true
|
||||
else:
|
||||
raise newException(ValueError, &"invalid FEN: unknown symbol '{c}' found in castlingRights availability section")
|
||||
of 3:
|
||||
|
@ -186,7 +185,7 @@ func countPieces*(self: Chessboard, kind: PieceKind, color: PieceColor): int {.i
|
|||
## Returns the number of pieces with
|
||||
## the given color and type in the
|
||||
## current position
|
||||
return self.position.pieces[color][kind][].countSquares()
|
||||
return self.position.pieces[color][kind].countSquares()
|
||||
|
||||
|
||||
func getPiece*(self: Chessboard, square: Square): Piece {.inline.} =
|
||||
|
@ -200,20 +199,20 @@ func getPiece*(self: Chessboard, square: string): Piece {.inline.} =
|
|||
return self.getPiece(square.toSquare())
|
||||
|
||||
|
||||
proc removePieceFromBitboard*(self: Chessboard, square: Square) =
|
||||
proc removePieceFromBitboard*(self: var Chessboard, square: Square) =
|
||||
## Removes a piece at the given square in the chessboard from
|
||||
## its respective bitboard
|
||||
let piece = self.getPiece(square)
|
||||
self.position.pieces[piece.color][piece.kind][].clearBit(square)
|
||||
self.position.pieces[piece.color][piece.kind].clearBit(square)
|
||||
|
||||
|
||||
proc addPieceToBitboard*(self: Chessboard, square: Square, piece: Piece) =
|
||||
proc addPieceToBitboard*(self: var Chessboard, square: Square, piece: Piece) =
|
||||
## Adds the given piece at the given square in the chessboard to
|
||||
## its respective bitboard
|
||||
self.position.pieces[piece.color][piece.kind][].setBit(square)
|
||||
self.position.pieces[piece.color][piece.kind].setBit(square)
|
||||
|
||||
|
||||
proc spawnPiece*(self: Chessboard, square: Square, piece: Piece) =
|
||||
proc spawnPiece*(self: var Chessboard, square: Square, piece: Piece) =
|
||||
## Internal helper to "spawn" a given piece at the given
|
||||
## square
|
||||
when not defined(danger):
|
||||
|
@ -222,7 +221,7 @@ proc spawnPiece*(self: Chessboard, square: Square, piece: Piece) =
|
|||
self.grid[square] = piece
|
||||
|
||||
|
||||
proc removePiece*(self: Chessboard, square: Square) =
|
||||
proc removePiece*(self: var Chessboard, square: Square) =
|
||||
## Removes a piece from the board, updating necessary
|
||||
## metadata
|
||||
when not defined(danger):
|
||||
|
@ -232,7 +231,7 @@ proc removePiece*(self: Chessboard, square: Square) =
|
|||
self.grid[square] = nullPiece()
|
||||
|
||||
|
||||
proc movePiece*(self: Chessboard, move: Move) =
|
||||
proc movePiece*(self: var Chessboard, move: Move) =
|
||||
## Internal helper to move a piece from
|
||||
## its current square to a target square
|
||||
let piece = self.getPiece(move.startSquare)
|
||||
|
@ -245,7 +244,7 @@ proc movePiece*(self: Chessboard, move: Move) =
|
|||
self.spawnPiece(move.targetSquare, piece)
|
||||
|
||||
|
||||
proc movePiece*(self: Chessboard, startSquare, targetSquare: Square) =
|
||||
proc movePiece*(self: var Chessboard, startSquare, targetSquare: Square) =
|
||||
self.movePiece(createMove(startSquare, targetSquare))
|
||||
|
||||
|
||||
|
@ -377,7 +376,7 @@ proc isOccupancyAttacked*(self: Chessboard, square: Square, occupancy: Bitboard)
|
|||
return true
|
||||
|
||||
|
||||
proc updateChecksAndPins*(self: Chessboard) =
|
||||
proc updateChecksAndPins*(self: var Chessboard) =
|
||||
## Updates internal metadata about checks and
|
||||
## pinned pieces
|
||||
|
||||
|
@ -428,7 +427,7 @@ proc canCastle*(self: Chessboard): tuple[queen, king: bool] =
|
|||
let
|
||||
sideToMove = self.position.sideToMove
|
||||
occupancy = self.getOccupancy()
|
||||
result = self.position.castlingAvailability[sideToMove.int]
|
||||
result = self.position.castlingAvailability[sideToMove]
|
||||
if result.king:
|
||||
result.king = (kingSideCastleRay(sideToMove) and occupancy) == 0
|
||||
if result.queen:
|
||||
|
@ -458,35 +457,35 @@ proc canCastle*(self: Chessboard): tuple[queen, king: bool] =
|
|||
break
|
||||
|
||||
|
||||
proc update*(self: Chessboard) =
|
||||
proc update*(self: var Chessboard) =
|
||||
## Updates the internal grid representation
|
||||
## according to the positional data stored
|
||||
## in the chessboard
|
||||
for i in 0..63:
|
||||
self.grid[i] = nullPiece()
|
||||
for sq in self.position.pieces[White][Pawn][]:
|
||||
for sq in self.position.pieces[White][Pawn]:
|
||||
self.grid[sq] = Piece(color: White, kind: Pawn)
|
||||
for sq in self.position.pieces[Black][Pawn][]:
|
||||
for sq in self.position.pieces[Black][Pawn]:
|
||||
self.grid[sq] = Piece(color: Black, kind: Pawn)
|
||||
for sq in self.position.pieces[White][Bishop][]:
|
||||
for sq in self.position.pieces[White][Bishop]:
|
||||
self.grid[sq] = Piece(color: White, kind: Bishop)
|
||||
for sq in self.position.pieces[Black][Bishop][]:
|
||||
for sq in self.position.pieces[Black][Bishop]:
|
||||
self.grid[sq] = Piece(color: Black, kind: Bishop)
|
||||
for sq in self.position.pieces[White][Knight][]:
|
||||
for sq in self.position.pieces[White][Knight]:
|
||||
self.grid[sq] = Piece(color: White, kind: Knight)
|
||||
for sq in self.position.pieces[Black][Knight][]:
|
||||
for sq in self.position.pieces[Black][Knight]:
|
||||
self.grid[sq] = Piece(color: Black, kind: Knight)
|
||||
for sq in self.position.pieces[White][Rook][]:
|
||||
for sq in self.position.pieces[White][Rook]:
|
||||
self.grid[sq] = Piece(color: White, kind: Rook)
|
||||
for sq in self.position.pieces[Black][Rook][]:
|
||||
for sq in self.position.pieces[Black][Rook]:
|
||||
self.grid[sq] = Piece(color: Black, kind: Rook)
|
||||
for sq in self.position.pieces[White][Queen][]:
|
||||
for sq in self.position.pieces[White][Queen]:
|
||||
self.grid[sq] = Piece(color: White, kind: Queen)
|
||||
for sq in self.position.pieces[Black][Queen][]:
|
||||
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)
|
||||
|
||||
|
||||
|
@ -559,8 +558,8 @@ proc toFEN*(self: Chessboard): string =
|
|||
result &= (if self.position.sideToMove == White: "w" else: "b")
|
||||
result &= " "
|
||||
# Castling availability
|
||||
let castleWhite = self.position.castlingAvailability[White.int]
|
||||
let castleBlack = self.position.castlingAvailability[Black.int]
|
||||
let castleWhite = self.position.castlingAvailability[White]
|
||||
let castleBlack = self.position.castlingAvailability[Black]
|
||||
if not (castleBlack.king or castleBlack.queen or castleWhite.king or castleWhite.queen):
|
||||
result &= "-"
|
||||
else:
|
||||
|
@ -586,7 +585,7 @@ proc toFEN*(self: Chessboard): string =
|
|||
result &= $self.position.fullMoveCount
|
||||
|
||||
|
||||
proc drawByRepetition*(self: Chessboard): bool =
|
||||
proc drawByRepetition*(self: var Chessboard): bool =
|
||||
## Returns whether the current position is a draw
|
||||
## by repetition
|
||||
# TODO: Improve this
|
||||
|
@ -601,7 +600,7 @@ proc drawByRepetition*(self: Chessboard): bool =
|
|||
dec(i)
|
||||
|
||||
|
||||
proc hash*(self: Chessboard) =
|
||||
proc hash*(self: var Chessboard) =
|
||||
## Computes the zobrist hash of the current
|
||||
## position. This only needs to be called when
|
||||
## a position is loaded the first time, as all
|
||||
|
@ -615,13 +614,13 @@ proc hash*(self: Chessboard) =
|
|||
for sq in self.getOccupancy():
|
||||
self.position.zobristKey = self.position.zobristKey xor self.getPiece(sq).getKey(sq)
|
||||
|
||||
if self.position.castlingAvailability[White.int].king:
|
||||
if self.position.castlingAvailability[White].king:
|
||||
self.position.zobristKey = self.position.zobristKey xor getKingSideCastlingKey(White)
|
||||
if self.position.castlingAvailability[White.int].queen:
|
||||
if self.position.castlingAvailability[White].queen:
|
||||
self.position.zobristKey = self.position.zobristKey xor getQueenSideCastlingKey(White)
|
||||
if self.position.castlingAvailability[Black.int].king:
|
||||
if self.position.castlingAvailability[Black].king:
|
||||
self.position.zobristKey = self.position.zobristKey xor getKingSideCastlingKey(Black)
|
||||
if self.position.castlingAvailability[Black.int].queen:
|
||||
if self.position.castlingAvailability[Black].queen:
|
||||
self.position.zobristKey = self.position.zobristKey xor getQueenSideCastlingKey(Black)
|
||||
|
||||
if self.position.enPassantSquare != nullSquare():
|
||||
|
|
|
@ -32,7 +32,7 @@ export bitboards, magics, pieces, moves, position, rays, board
|
|||
|
||||
|
||||
|
||||
proc generatePawnMoves(self: Chessboard, moves: var MoveList, destinationMask: Bitboard) =
|
||||
proc generatePawnMoves(self: var Chessboard, moves: var MoveList, destinationMask: Bitboard) =
|
||||
let
|
||||
sideToMove = self.position.sideToMove
|
||||
nonSideToMove = sideToMove.opposite()
|
||||
|
@ -272,7 +272,7 @@ proc generateCastling(self: Chessboard, moves: var MoveList) =
|
|||
moves.add(createMove(kingSquare, kingPiece.queenSideCastling(), Castle))
|
||||
|
||||
|
||||
proc generateMoves*(self: Chessboard, moves: var MoveList, capturesOnly: bool = false) =
|
||||
proc generateMoves*(self: var Chessboard, moves: var MoveList, capturesOnly: bool = false) =
|
||||
## Generates the list of all possible legal moves
|
||||
## in the current position. If capturesOnly is
|
||||
## true, only capture moves are generated
|
||||
|
@ -330,7 +330,7 @@ proc generateMoves*(self: Chessboard, moves: var MoveList, capturesOnly: bool =
|
|||
# Queens are just handled rooks + bishops
|
||||
|
||||
|
||||
proc doMove*(self: Chessboard, move: Move) =
|
||||
proc doMove*(self: var Chessboard, move: Move) =
|
||||
## Internal function called by makeMove after
|
||||
## performing legality checks. Can be used in
|
||||
## performance-critical paths where a move is
|
||||
|
@ -388,7 +388,7 @@ proc doMove*(self: Chessboard, move: Move) =
|
|||
if move.isCastling() or piece.kind == King:
|
||||
# If the king has moved, all castling rights for the side to
|
||||
# move are revoked
|
||||
self.position.castlingAvailability[piece.color.int] = (false, false)
|
||||
self.position.castlingAvailability[piece.color] = (false, false)
|
||||
if move.isCastling():
|
||||
# Move the rook where it belongs
|
||||
var
|
||||
|
@ -414,9 +414,9 @@ proc doMove*(self: Chessboard, move: Move) =
|
|||
# If a rook on either side moves, castling rights are permanently revoked
|
||||
# on that side
|
||||
if move.startSquare == piece.color.kingSideRook():
|
||||
self.position.castlingAvailability[piece.color.int].king = false
|
||||
self.position.castlingAvailability[piece.color].king = false
|
||||
elif move.startSquare == piece.color.queenSideRook():
|
||||
self.position.castlingAvailability[piece.color.int].queen = false
|
||||
self.position.castlingAvailability[piece.color].queen = false
|
||||
|
||||
if move.isCapture():
|
||||
# Get rid of captured pieces
|
||||
|
@ -426,9 +426,9 @@ proc doMove*(self: Chessboard, move: Move) =
|
|||
# If a rook has been captured, castling on that side is prohibited
|
||||
if captured.kind == Rook:
|
||||
if move.targetSquare == captured.color.kingSideRook():
|
||||
self.position.castlingAvailability[captured.color.int].king = false
|
||||
self.position.castlingAvailability[captured.color].king = false
|
||||
elif move.targetSquare == captured.color.queenSideRook():
|
||||
self.position.castlingAvailability[captured.color.int].queen = false
|
||||
self.position.castlingAvailability[captured.color].queen = false
|
||||
|
||||
# Move the piece to its target square
|
||||
self.movePiece(move)
|
||||
|
@ -457,21 +457,21 @@ proc doMove*(self: Chessboard, move: Move) =
|
|||
# Updates checks and pins for the (new) side to move
|
||||
self.updateChecksAndPins()
|
||||
# Last updates to zobrist key
|
||||
if self.position.castlingAvailability[piece.color.int].king:
|
||||
if self.position.castlingAvailability[piece.color].king:
|
||||
self.position.zobristKey = self.position.zobristKey xor getKingSideCastlingKey(piece.color)
|
||||
if self.position.castlingAvailability[piece.color.int].queen:
|
||||
if self.position.castlingAvailability[piece.color].queen:
|
||||
self.position.zobristKey = self.position.zobristKey xor getQueenSideCastlingKey(piece.color)
|
||||
discard self.drawByRepetition()
|
||||
|
||||
|
||||
proc isLegal*(self: Chessboard, move: Move): bool {.inline.} =
|
||||
proc isLegal*(self: var Chessboard, move: Move): bool {.inline.} =
|
||||
## Returns whether the given move is legal
|
||||
var moves = newMoveList()
|
||||
self.generateMoves(moves)
|
||||
return move in moves
|
||||
|
||||
|
||||
proc makeMove*(self: Chessboard, move: Move): Move {.discardable.} =
|
||||
proc makeMove*(self: var Chessboard, move: Move): Move {.discardable.} =
|
||||
## Makes a move on the board
|
||||
result = move
|
||||
# Updates checks and pins for the side to move
|
||||
|
@ -480,7 +480,7 @@ proc makeMove*(self: Chessboard, move: Move): Move {.discardable.} =
|
|||
self.doMove(move)
|
||||
|
||||
|
||||
proc unmakeMove*(self: Chessboard) =
|
||||
proc unmakeMove*(self: var Chessboard) =
|
||||
## Reverts to the previous board position
|
||||
if self.positions.len() == 0:
|
||||
return
|
||||
|
|
|
@ -25,7 +25,7 @@ type
|
|||
# of whether the king or the rooks on either side
|
||||
# moved, the actual checks for the legality of castling
|
||||
# are done elsewhere
|
||||
castlingAvailability*: array[2, tuple[queen, king: bool]]
|
||||
castlingAvailability*: array[PieceColor.White..PieceColor.Black, tuple[queen, king: bool]]
|
||||
# Number of half-moves that were performed
|
||||
# to reach this position starting from the
|
||||
# root of the tree
|
||||
|
@ -42,7 +42,7 @@ type
|
|||
# The side to move
|
||||
sideToMove*: PieceColor
|
||||
# Positional bitboards for all pieces
|
||||
pieces*: array[2, array[6, Bitboard]]
|
||||
pieces*: array[PieceColor.White..PieceColor.Black, array[PieceKind.Bishop..PieceKind.Rook, Bitboard]]
|
||||
# Pieces pinned for the current side to move
|
||||
diagonalPins*: Bitboard # Pinned diagonally (by a queen or bishop)
|
||||
orthogonalPins*: Bitboard # Pinned orthogonally (by a queen or rook)
|
||||
|
@ -66,14 +66,9 @@ func getKingStartingSquare*(color: PieceColor): Square {.inline.} =
|
|||
discard
|
||||
|
||||
|
||||
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): ptr Bitboard {.inline.} = addr self[kind.int]
|
||||
func `[]=`*(self: var array[6, Bitboard], kind: PieceKind, bitboard: Bitboard) {.inline.} = self[kind.int] = bitboard
|
||||
|
||||
|
||||
func getBitboard*(self: Position, kind: PieceKind, color: PieceColor): Bitboard =
|
||||
## Returns the positional bitboard for the given piece kind and color
|
||||
return self.pieces[color.int][kind.int]
|
||||
return self.pieces[color][kind]
|
||||
|
||||
|
||||
func getBitboard*(self: Position, piece: Piece): Bitboard =
|
||||
|
@ -84,7 +79,7 @@ func getBitboard*(self: Position, piece: Piece): Bitboard =
|
|||
func getOccupancyFor*(self: Position, color: PieceColor): Bitboard =
|
||||
## Get the occupancy bitboard for every piece of the given color
|
||||
result = Bitboard(0)
|
||||
for b in self.pieces[color][]:
|
||||
for b in self.pieces[color]:
|
||||
result = result or b
|
||||
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ type
|
|||
CountData = tuple[nodes: uint64, captures: uint64, castles: uint64, checks: uint64, promotions: uint64, enPassant: uint64, checkmates: uint64]
|
||||
|
||||
|
||||
proc perft*(board: Chessboard, ply: int, verbose = false, divide = false, bulk = false, capturesOnly = false): CountData =
|
||||
proc perft*(board: var Chessboard, ply: int, verbose = false, divide = false, bulk = false, capturesOnly = false): CountData =
|
||||
## Counts (and debugs) the number of legal positions reached after
|
||||
## the given number of ply
|
||||
|
||||
|
@ -134,7 +134,7 @@ proc perft*(board: Chessboard, ply: int, verbose = false, divide = false, bulk =
|
|||
result.checkmates += next.checkmates
|
||||
|
||||
|
||||
proc handleGoCommand(board: Chessboard, command: seq[string]) =
|
||||
proc handleGoCommand(board: var Chessboard, command: seq[string]) =
|
||||
if len(command) < 2:
|
||||
echo &"Error: go: invalid number of arguments"
|
||||
return
|
||||
|
@ -191,7 +191,7 @@ proc handleGoCommand(board: Chessboard, command: seq[string]) =
|
|||
echo &"Error: go: unknown subcommand '{command[1]}'"
|
||||
|
||||
|
||||
proc handleMoveCommand(board: Chessboard, command: seq[string]): Move {.discardable.} =
|
||||
proc handleMoveCommand(board: var Chessboard, command: seq[string]): Move {.discardable.} =
|
||||
if len(command) != 2:
|
||||
echo &"Error: move: invalid number of arguments"
|
||||
return
|
||||
|
|
|
@ -303,7 +303,7 @@ proc bestMove(args: tuple[session: UCISession, command: UCICommand]) {.thread.}
|
|||
echo &"info string created {session.hashTableSize} MiB TT"
|
||||
session.transpositionTable = newTranspositionTable(session.hashTableSize * 1024 * 1024)
|
||||
var command = args.command
|
||||
var searcher = newSearchManager(session.board.deepCopy(), session.transpositionTable)
|
||||
var searcher = newSearchManager(session.board, session.transpositionTable)
|
||||
session.currentSearch.store(searcher)
|
||||
var
|
||||
timeRemaining = (if session.board.position.sideToMove == White: command.wtime else: command.btime)
|
||||
|
|
Loading…
Reference in New Issue