Initial work on transposition table and repetition detection
Signed-off-by: Mattia Giambirtone <nocturn9x@nocturn9x.space>
This commit is contained in:
parent
7d6cc98614
commit
d47040feed
|
@ -41,8 +41,6 @@ type
|
||||||
position*: Position
|
position*: Position
|
||||||
# List of all previously reached positions
|
# List of all previously reached positions
|
||||||
positions*: seq[Position]
|
positions*: seq[Position]
|
||||||
# Zobrist hash of the given position
|
|
||||||
zobristKey*: ZobristKey
|
|
||||||
|
|
||||||
|
|
||||||
# A bunch of simple utility functions and forward declarations
|
# A bunch of simple utility functions and forward declarations
|
||||||
|
@ -590,25 +588,40 @@ proc toFEN*(self: Chessboard): string =
|
||||||
result &= $self.position.fullMoveCount
|
result &= $self.position.fullMoveCount
|
||||||
|
|
||||||
|
|
||||||
|
proc drawByRepetition*(self: Chessboard): bool =
|
||||||
|
## Returns whether the current position is a draw
|
||||||
|
## by repetition
|
||||||
|
# Naive version. TODO: Improve
|
||||||
|
var i = self.positions.high()
|
||||||
|
var count = 0
|
||||||
|
while i > 0:
|
||||||
|
if count == 2:
|
||||||
|
self.position.repetitionDraw = true
|
||||||
|
return true
|
||||||
|
if self.position.zobristKey == self.positions[i].zobristKey:
|
||||||
|
inc(count)
|
||||||
|
dec(i)
|
||||||
|
|
||||||
|
|
||||||
proc hash*(self: Chessboard) =
|
proc hash*(self: Chessboard) =
|
||||||
## Computes the zobrist hash of the current
|
## Computes the zobrist hash of the current
|
||||||
## position
|
## position
|
||||||
self.zobristKey = ZobristKey(0)
|
self.position.zobristKey = ZobristKey(0)
|
||||||
|
|
||||||
if self.position.sideToMove == Black:
|
if self.position.sideToMove == Black:
|
||||||
self.zobristKey = self.zobristKey xor getBlackToMoveKey()
|
self.position.zobristKey = self.position.zobristKey xor getBlackToMoveKey()
|
||||||
|
|
||||||
for sq in self.getOccupancy():
|
for sq in self.getOccupancy():
|
||||||
self.zobristKey = self.zobristKey xor self.getPiece(sq).getKey(sq)
|
self.position.zobristKey = self.position.zobristKey xor self.getPiece(sq).getKey(sq)
|
||||||
|
|
||||||
if self.position.castlingAvailability[White.int].king:
|
if self.position.castlingAvailability[White.int].king:
|
||||||
self.zobristKey = self.zobristKey xor getKingSideCastlingKey(White)
|
self.position.zobristKey = self.position.zobristKey xor getKingSideCastlingKey(White)
|
||||||
if self.position.castlingAvailability[White.int].queen:
|
if self.position.castlingAvailability[White.int].queen:
|
||||||
self.zobristKey = self.zobristKey xor getQueenSideCastlingKey(White)
|
self.position.zobristKey = self.position.zobristKey xor getQueenSideCastlingKey(White)
|
||||||
if self.position.castlingAvailability[Black.int].king:
|
if self.position.castlingAvailability[Black.int].king:
|
||||||
self.zobristKey = self.zobristKey xor getKingSideCastlingKey(Black)
|
self.position.zobristKey = self.position.zobristKey xor getKingSideCastlingKey(Black)
|
||||||
if self.position.castlingAvailability[Black.int].king:
|
if self.position.castlingAvailability[Black.int].king:
|
||||||
self.zobristKey = self.zobristKey xor getQueenSideCastlingKey(Black)
|
self.position.zobristKey = self.position.zobristKey xor getQueenSideCastlingKey(Black)
|
||||||
|
|
||||||
if self.position.enPassantSquare != nullSquare():
|
if self.position.enPassantSquare != nullSquare():
|
||||||
self.zobristKey = self.zobristKey xor getEnPassantKey(fileFromSquare(self.position.enPassantSquare))
|
self.position.zobristKey = self.position.zobristKey xor getEnPassantKey(fileFromSquare(self.position.enPassantSquare))
|
|
@ -172,6 +172,10 @@ const
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
func lowestEval*: Score {.inline.} = Score(-20_000)
|
||||||
|
func highestEval*: Score {.inline.} = Score(20_000)
|
||||||
|
func mateScore*: Score {.inline.} = lowestEval()
|
||||||
|
|
||||||
|
|
||||||
proc getPieceValue(kind: PieceKind): Score =
|
proc getPieceValue(kind: PieceKind): Score =
|
||||||
## Returns the absolute value of a piece
|
## Returns the absolute value of a piece
|
||||||
|
|
|
@ -18,6 +18,7 @@ import board
|
||||||
|
|
||||||
|
|
||||||
import std/strformat
|
import std/strformat
|
||||||
|
import std/strutils
|
||||||
|
|
||||||
|
|
||||||
proc testPiece(piece: Piece, kind: PieceKind, color: PieceColor) =
|
proc testPiece(piece: Piece, kind: PieceKind, color: PieceColor) =
|
||||||
|
@ -36,7 +37,14 @@ proc testPieceBitboard(bitboard: Bitboard, squares: seq[Square]) =
|
||||||
doAssert false, &"bitboard.len() ({i}) != squares.len() ({squares.len()})"
|
doAssert false, &"bitboard.len() ({i}) != squares.len() ({squares.len()})"
|
||||||
|
|
||||||
|
|
||||||
|
const fens = staticRead("../../tests/all.txt").splitLines()
|
||||||
|
|
||||||
|
|
||||||
proc basicTests* =
|
proc basicTests* =
|
||||||
|
|
||||||
|
for fen in fens:
|
||||||
|
doAssert fen == newChessboardFromFEN(fen).toFEN()
|
||||||
|
|
||||||
var b = newDefaultChessboard()
|
var b = newDefaultChessboard()
|
||||||
# Ensure correct number of pieces
|
# Ensure correct number of pieces
|
||||||
testPieceCount(b, Pawn, White, 8)
|
testPieceCount(b, Pawn, White, 8)
|
||||||
|
|
|
@ -277,9 +277,10 @@ proc generateMoves*(self: Chessboard, moves: var MoveList) =
|
||||||
if self.position.halfMoveClock >= 100:
|
if self.position.halfMoveClock >= 100:
|
||||||
# Draw by 50-move rule
|
# Draw by 50-move rule
|
||||||
return
|
return
|
||||||
let sideToMove = self.position.sideToMove
|
|
||||||
# TODO: Check for draw by insufficient material
|
# TODO: Check for draw by insufficient material
|
||||||
# TODO: Check for repetitions (requires zobrist hashing + table)
|
if self.position.repetitionDraw:
|
||||||
|
return
|
||||||
|
let sideToMove = self.position.sideToMove
|
||||||
self.generateKingMoves(moves)
|
self.generateKingMoves(moves)
|
||||||
if self.position.checkers.countSquares() > 1:
|
if self.position.checkers.countSquares() > 1:
|
||||||
# King is in double check: no need to generate any more
|
# King is in double check: no need to generate any more
|
||||||
|
@ -426,6 +427,7 @@ proc doMove*(self: Chessboard, move: Move) =
|
||||||
self.updateChecksAndPins()
|
self.updateChecksAndPins()
|
||||||
# Update zobrist key
|
# Update zobrist key
|
||||||
self.hash()
|
self.hash()
|
||||||
|
discard self.drawByRepetition()
|
||||||
|
|
||||||
|
|
||||||
proc isLegal*(self: Chessboard, move: Move): bool {.inline.} =
|
proc isLegal*(self: Chessboard, move: Move): bool {.inline.} =
|
||||||
|
@ -438,6 +440,7 @@ proc isLegal*(self: Chessboard, move: Move): bool {.inline.} =
|
||||||
proc makeMove*(self: Chessboard, move: Move): Move {.discardable.} =
|
proc makeMove*(self: Chessboard, move: Move): Move {.discardable.} =
|
||||||
## Makes a move on the board
|
## Makes a move on the board
|
||||||
result = move
|
result = move
|
||||||
|
echo move
|
||||||
# Updates checks and pins for the side to move
|
# Updates checks and pins for the side to move
|
||||||
if not self.isLegal(move):
|
if not self.isLegal(move):
|
||||||
return nullMove()
|
return nullMove()
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
import bitboards
|
import bitboards
|
||||||
import magics
|
import magics
|
||||||
import pieces
|
import pieces
|
||||||
|
import zobrist
|
||||||
|
|
||||||
|
|
||||||
type
|
type
|
||||||
|
@ -50,6 +50,10 @@ type
|
||||||
orthogonalPins*: Bitboard # Pinned orthogonally (by a queen or rook)
|
orthogonalPins*: Bitboard # Pinned orthogonally (by a queen or rook)
|
||||||
# Pieces checking the current side to move
|
# Pieces checking the current side to move
|
||||||
checkers*: Bitboard
|
checkers*: Bitboard
|
||||||
|
# Zobrist hash of this position
|
||||||
|
zobristKey*: ZobristKey
|
||||||
|
# Cached result of drawByRepetition()
|
||||||
|
repetitionDraw*: bool
|
||||||
|
|
||||||
|
|
||||||
func getKingStartingSquare*(color: PieceColor): Square {.inline.} =
|
func getKingStartingSquare*(color: PieceColor): Square {.inline.} =
|
||||||
|
|
|
@ -25,11 +25,6 @@ import std/monotimes
|
||||||
import std/strformat
|
import std/strformat
|
||||||
|
|
||||||
|
|
||||||
func lowestEval*: Score {.inline.} = Score(20_000)
|
|
||||||
func highestEval*: Score {.inline.} = Score(-20_000)
|
|
||||||
func mateScore*: Score {.inline.} = lowestEval()
|
|
||||||
|
|
||||||
|
|
||||||
type
|
type
|
||||||
SearchManager* = ref object
|
SearchManager* = ref object
|
||||||
## A simple state storage
|
## A simple state storage
|
||||||
|
@ -130,23 +125,26 @@ proc search*(self: SearchManager, depth, ply: int, alpha, beta: Score): Score {.
|
||||||
for i, move in moves:
|
for i, move in moves:
|
||||||
if ply == 0 and self.searchMoves.len() > 0 and move notin self.searchMoves:
|
if ply == 0 and self.searchMoves.len() > 0 and move notin self.searchMoves:
|
||||||
continue
|
continue
|
||||||
self.board.makeMove(move)
|
self.board.doMove(move)
|
||||||
inc(self.nodeCount)
|
inc(self.nodeCount)
|
||||||
# Find the best move for us (worst move
|
# Find the best move for us (worst move
|
||||||
# for our opponent, hence the negative sign)
|
# for our opponent, hence the negative sign)
|
||||||
let eval = -self.search(depth - 1, ply + 1, -beta, -alpha)
|
var score = -self.search(depth - 1, ply + 1, -beta, -alpha)
|
||||||
|
if self.board.position.repetitionDraw:
|
||||||
|
# Drawing by repetition is *bad*
|
||||||
|
score = lowestEval() div 2
|
||||||
self.board.unmakeMove()
|
self.board.unmakeMove()
|
||||||
# When a search is cancelled or times out, we need
|
# When a search is cancelled or times out, we need
|
||||||
# to make sure the entire call stack unwindss back
|
# to make sure the entire call stack unwindss back
|
||||||
# to the root move. This is why the check is duplicated
|
# to the root move. This is why the check is duplicated
|
||||||
if self.shouldStop():
|
if self.shouldStop():
|
||||||
return
|
return
|
||||||
bestScore = max(eval, bestScore)
|
bestScore = max(score, bestScore)
|
||||||
if eval >= beta:
|
if score >= beta:
|
||||||
# This move was too good for us, opponent will not search it
|
# This move was too good for us, opponent will not search it
|
||||||
break
|
break
|
||||||
if eval > alpha:
|
if score > alpha:
|
||||||
alpha = eval
|
alpha = score
|
||||||
if ply == 0:
|
if ply == 0:
|
||||||
self.bestMoveRoot = move
|
self.bestMoveRoot = move
|
||||||
return bestScore
|
return bestScore
|
||||||
|
@ -175,12 +173,16 @@ proc findBestMove*(self: SearchManager, maxSearchTime, maxDepth: int, maxNodes:
|
||||||
# Search the previous best move first
|
# Search the previous best move first
|
||||||
self.previousBestMove = self.bestMoveRoot
|
self.previousBestMove = self.bestMoveRoot
|
||||||
self.search(i, 0, lowestEval(), highestEval())
|
self.search(i, 0, lowestEval(), highestEval())
|
||||||
self.log(i)
|
let shouldStop = self.shouldStop()
|
||||||
|
if shouldStop:
|
||||||
|
self.log(i - 1)
|
||||||
|
else:
|
||||||
|
self.log(i)
|
||||||
# Since we always search the best move from the
|
# Since we always search the best move from the
|
||||||
# previous iteration, we can use partial search
|
# previous iteration, we can use partial search
|
||||||
# results: the engine will either not have changed
|
# results: the engine will either not have changed
|
||||||
# its mind, or it will have found an even better move
|
# its mind, or it will have found an even better move
|
||||||
# in the meantime, which we should obviously use!
|
# in the meantime, which we should obviously use!
|
||||||
result = self.bestMoveRoot
|
result = self.bestMoveRoot
|
||||||
if self.shouldStop():
|
if shouldStop:
|
||||||
break
|
break
|
||||||
|
|
|
@ -15,9 +15,11 @@
|
||||||
## Implementation of a transposition table
|
## Implementation of a transposition table
|
||||||
|
|
||||||
import zobrist
|
import zobrist
|
||||||
import pieces
|
|
||||||
import eval
|
|
||||||
import moves
|
import moves
|
||||||
|
import eval
|
||||||
|
|
||||||
|
|
||||||
|
import nint128
|
||||||
|
|
||||||
|
|
||||||
type
|
type
|
||||||
|
@ -28,22 +30,55 @@ type
|
||||||
LowerBound = 1'i8
|
LowerBound = 1'i8
|
||||||
UpperBound = 2'i8
|
UpperBound = 2'i8
|
||||||
|
|
||||||
TTEntry = object
|
TTEntry* {.packed.} = object
|
||||||
## An entry in the transposition table
|
## An entry in the transposition table
|
||||||
flag: TTentryFlag
|
flag*: TTentryFlag
|
||||||
# Scores are int32s for convenience (less chance
|
# Scores are int32s for convenience (less chance
|
||||||
# of overflows and stuff), but they are capped to
|
# of overflows and stuff), but they are capped to
|
||||||
# fit into an int16
|
# fit into an int16
|
||||||
score: int16
|
score*: int16
|
||||||
hash: ZobristKey
|
hash*: ZobristKey
|
||||||
bestMove: Move
|
bestMove*: Move
|
||||||
|
|
||||||
TTable = object
|
TTable = ref object
|
||||||
data: ptr UncheckedArray[TTEntry]
|
data: ptr UncheckedArray[TTEntry]
|
||||||
# Just for statistical purposes
|
|
||||||
collisions: uint32
|
|
||||||
overwrites: uint32
|
|
||||||
# Size metadata
|
|
||||||
size: uint64
|
size: uint64
|
||||||
occupancy: uint64
|
|
||||||
|
|
||||||
|
|
||||||
|
proc newTranspositionTable(size: uint64): TTable =
|
||||||
|
## Initializes a new transposition table of
|
||||||
|
## size bytes
|
||||||
|
let size = size div sizeof(TTEntry).uint64
|
||||||
|
result.data = cast[ptr UncheckedArray[TTEntry]](alloc(size))
|
||||||
|
|
||||||
|
|
||||||
|
func getIndex(self: TTable, key: ZobristKey): uint64 =
|
||||||
|
## Retrieves the index of the given
|
||||||
|
## zobrist key in our transposition table
|
||||||
|
|
||||||
|
# Apparently this is a trick to get fast arbitrary indexing into the
|
||||||
|
# TT even when its size is not a multiple of 2. The alternative would
|
||||||
|
# be a modulo operation (slooow) or restricting the TT size to be a
|
||||||
|
# multiple of 2 and replacing x mod y with x and 1 (fast!), but thanks
|
||||||
|
# to @ciekce on the Engine Programming discord we now have neither of
|
||||||
|
# those limitations. Also, source: https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/
|
||||||
|
result = (u128(key.uint64) * u128(self.size)).hi
|
||||||
|
|
||||||
|
|
||||||
|
func store(self: TTable, score: Score, hash: ZobristKey, bestMove: Move, flag: TTentryFlag) =
|
||||||
|
## Stores an entry in the transposition table
|
||||||
|
self.data[self.getIndex(hash)] = TTEntry(flag: flag, score: int16(score), hash: hash, bestMove: bestMove)
|
||||||
|
|
||||||
|
|
||||||
|
func get(self: TTable, hash: ZobristKey): tuple[success: bool, entry: TTEntry] =
|
||||||
|
## Attempts to get the entry with the given
|
||||||
|
## zobrist key in the transposition table.
|
||||||
|
## The success parameter is set to false upon
|
||||||
|
## detection of a hash collision and the result
|
||||||
|
## should be considered invalid unless it's true
|
||||||
|
result.entry = self.data[self.getIndex(hash)]
|
||||||
|
result.success = result.entry.hash == hash
|
||||||
|
|
||||||
|
|
||||||
|
proc `destroy=`(self: TTable) =
|
||||||
|
dealloc(self.data)
|
||||||
|
|
|
@ -447,7 +447,9 @@ proc commandLoop*: int =
|
||||||
of "quit":
|
of "quit":
|
||||||
return 0
|
return 0
|
||||||
of "zobrist":
|
of "zobrist":
|
||||||
echo board.zobristKey.uint64
|
echo board.position.zobristKey.uint64
|
||||||
|
of "rep":
|
||||||
|
echo board.position.repetitionDraw
|
||||||
else:
|
else:
|
||||||
echo &"Unknown command '{cmd[0]}'. Type 'help' for more information."
|
echo &"Unknown command '{cmd[0]}'. Type 'help' for more information."
|
||||||
except IOError:
|
except IOError:
|
||||||
|
|
|
@ -29,6 +29,7 @@ type
|
||||||
board: Chessboard
|
board: Chessboard
|
||||||
searching: bool
|
searching: bool
|
||||||
currentSearch: SearchManager
|
currentSearch: SearchManager
|
||||||
|
hashTableSize: uint64
|
||||||
|
|
||||||
UCICommandType = enum
|
UCICommandType = enum
|
||||||
Unknown,
|
Unknown,
|
||||||
|
@ -44,10 +45,13 @@ type
|
||||||
UCICommand = object
|
UCICommand = object
|
||||||
case kind: UCICommandType
|
case kind: UCICommandType
|
||||||
of Debug:
|
of Debug:
|
||||||
value: bool
|
on: bool
|
||||||
of Position:
|
of Position:
|
||||||
fen: string
|
fen: string
|
||||||
moves: seq[string]
|
moves: seq[string]
|
||||||
|
of SetOption:
|
||||||
|
name: string
|
||||||
|
value: string
|
||||||
of Unknown:
|
of Unknown:
|
||||||
reason: string
|
reason: string
|
||||||
of Go:
|
of Go:
|
||||||
|
@ -106,7 +110,8 @@ proc parseUCIMove(session: UCISession, move: string): tuple[move: Move, command:
|
||||||
if piece.kind == King and startSquare == session.board.position.sideToMove.getKingStartingSquare():
|
if piece.kind == King and startSquare == session.board.position.sideToMove.getKingStartingSquare():
|
||||||
if targetSquare in [piece.kingSideCastling(), piece.queenSideCastling()]:
|
if targetSquare in [piece.kingSideCastling(), piece.queenSideCastling()]:
|
||||||
flags.add(Castle)
|
flags.add(Castle)
|
||||||
elif targetSquare == session.board.position.enPassantSquare:
|
elif piece.kind == Pawn and targetSquare == session.board.position.enPassantSquare:
|
||||||
|
# I hate en passant I hate en passant I hate en passant I hate en passant I hate en passant I hate en passant
|
||||||
flags.add(EnPassant)
|
flags.add(EnPassant)
|
||||||
result.move = createMove(startSquare, targetSquare, flags)
|
result.move = createMove(startSquare, targetSquare, flags)
|
||||||
|
|
||||||
|
@ -220,15 +225,14 @@ proc handleUCIPositionCommand(session: UCISession, command: seq[string]): UCICom
|
||||||
of "moves":
|
of "moves":
|
||||||
var j = i + 1
|
var j = i + 1
|
||||||
while j < args.len():
|
while j < args.len():
|
||||||
while j < args.len():
|
let r = handleUCIMove(session, args[j])
|
||||||
let r = handleUCIMove(session, args[j])
|
if r.move == nullMove():
|
||||||
if r.move == nullMove():
|
if r.cmd.reason.len() > 0:
|
||||||
if r.cmd.reason.len() > 0:
|
return UCICommand(kind: Unknown, reason: &"move {args[j]} is illegal or invalid ({r.cmd.reason})")
|
||||||
return UCICommand(kind: Unknown, reason: &"move {args[j]} is illegal or invalid ({r.cmd.reason})")
|
else:
|
||||||
else:
|
return UCICommand(kind: Unknown, reason: &"move {args[j]} is illegal or invalid")
|
||||||
return UCICommand(kind: Unknown, reason: &"move {args[j]} is illegal or invalid")
|
result.moves.add(args[j])
|
||||||
result.moves.add(args[j])
|
inc(j)
|
||||||
inc(j)
|
|
||||||
inc(i)
|
inc(i)
|
||||||
else:
|
else:
|
||||||
return UCICommand(kind: Unknown, reason: &"unknown subcomponent '{command[1]}'")
|
return UCICommand(kind: Unknown, reason: &"unknown subcomponent '{command[1]}'")
|
||||||
|
@ -254,15 +258,29 @@ proc parseUCICommand(session: UCISession, command: string): UCICommand =
|
||||||
return
|
return
|
||||||
case cmd[current + 1]:
|
case cmd[current + 1]:
|
||||||
of "on":
|
of "on":
|
||||||
return UCICommand(kind: Debug, value: true)
|
return UCICommand(kind: Debug, on: true)
|
||||||
of "off":
|
of "off":
|
||||||
return UCICommand(kind: Debug, value: false)
|
return UCICommand(kind: Debug, on: false)
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
of "position":
|
of "position":
|
||||||
return session.handleUCIPositionCommand(cmd)
|
return session.handleUCIPositionCommand(cmd)
|
||||||
of "go":
|
of "go":
|
||||||
return session.handleUCIGoCommand(cmd)
|
return session.handleUCIGoCommand(cmd)
|
||||||
|
of "setoption":
|
||||||
|
result = UCICommand(kind: SetOption)
|
||||||
|
while current < cmd.len():
|
||||||
|
inc(current)
|
||||||
|
case cmd[current]:
|
||||||
|
of "name":
|
||||||
|
inc(current)
|
||||||
|
result.name = cmd[current]
|
||||||
|
of "value":
|
||||||
|
inc(current)
|
||||||
|
result.value = cmd[current]
|
||||||
|
else:
|
||||||
|
discard
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# Unknown UCI commands should be ignored. Attempt
|
# Unknown UCI commands should be ignored. Attempt
|
||||||
# to make sense of the input regardless
|
# to make sense of the input regardless
|
||||||
|
@ -304,6 +322,7 @@ proc startUCISession* =
|
||||||
## Begins listening for UCI commands
|
## Begins listening for UCI commands
|
||||||
echo "id name Nimfish 0.1"
|
echo "id name Nimfish 0.1"
|
||||||
echo "id author Nocturn9x & Contributors (see LICENSE)"
|
echo "id author Nocturn9x & Contributors (see LICENSE)"
|
||||||
|
echo "option name Hash type spin default 64 min 1 max 33554432"
|
||||||
echo "uciok"
|
echo "uciok"
|
||||||
var
|
var
|
||||||
cmd: UCICommand
|
cmd: UCICommand
|
||||||
|
@ -329,7 +348,7 @@ proc startUCISession* =
|
||||||
of IsReady:
|
of IsReady:
|
||||||
echo "readyok"
|
echo "readyok"
|
||||||
of Debug:
|
of Debug:
|
||||||
session.debug = cmd.value
|
session.debug = cmd.on
|
||||||
of NewGame:
|
of NewGame:
|
||||||
session.board = newDefaultChessboard()
|
session.board = newDefaultChessboard()
|
||||||
of Go:
|
of Go:
|
||||||
|
@ -338,6 +357,14 @@ proc startUCISession* =
|
||||||
of Stop:
|
of Stop:
|
||||||
if session.searching:
|
if session.searching:
|
||||||
session.currentSearch.stopFlag.store(true)
|
session.currentSearch.stopFlag.store(true)
|
||||||
|
of SetOption:
|
||||||
|
case cmd.name:
|
||||||
|
of "Hash":
|
||||||
|
session.hashTableSize = cmd.value.parseBiggestUInt()
|
||||||
|
if session.debug:
|
||||||
|
echo &"info string set TT hash table size to {session.hashTableSize} MiB"
|
||||||
|
else:
|
||||||
|
discard
|
||||||
of Position:
|
of Position:
|
||||||
discard
|
discard
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -26,6 +26,7 @@ type
|
||||||
|
|
||||||
|
|
||||||
func `xor`*(a, b: ZobristKey): ZobristKey = ZobristKey(a.uint64 xor b.uint64)
|
func `xor`*(a, b: ZobristKey): ZobristKey = ZobristKey(a.uint64 xor b.uint64)
|
||||||
|
func `==`*(a, b: ZobristKey): bool = a.uint64 == b.uint64
|
||||||
|
|
||||||
|
|
||||||
proc computeZobristKeys: array[781, ZobristKey] =
|
proc computeZobristKeys: array[781, ZobristKey] =
|
||||||
|
|
Loading…
Reference in New Issue