Hopefully fix pondering bugs
This commit is contained in:
parent
6adae84bca
commit
d9e183caf3
|
@ -110,12 +110,7 @@ func getOccupancy*(self: Position): Bitboard {.inline.} =
|
|||
|
||||
proc getPawnAttacks*(self: Position, square: Square, attacker: PieceColor): Bitboard {.inline.} =
|
||||
## Returns the locations of the pawns attacking the given square
|
||||
try:
|
||||
return self.getBitboard(Pawn, attacker) and getPawnAttacks(attacker, square)
|
||||
except IndexDefect:
|
||||
echo square
|
||||
echo square.int
|
||||
echo square.toBitboard()
|
||||
return self.getBitboard(Pawn, attacker) and getPawnAttacks(attacker, square)
|
||||
|
||||
|
||||
proc getKingAttacks*(self: Position, square: Square, attacker: PieceColor): Bitboard {.inline.} =
|
||||
|
|
|
@ -107,9 +107,10 @@ type
|
|||
SearchManager* = object
|
||||
## A simple state storage
|
||||
## for our search
|
||||
searchFlag: ptr Atomic[bool]
|
||||
stopFlag: ptr Atomic[bool]
|
||||
board: Chessboard
|
||||
searching: Atomic[bool]
|
||||
stop: Atomic[bool]
|
||||
pondering: Atomic[bool]
|
||||
board*: Chessboard
|
||||
bestRootScore: Score
|
||||
searchStart: MonoTime
|
||||
hardLimit: MonoTime
|
||||
|
@ -126,29 +127,29 @@ type
|
|||
selectiveDepth: int
|
||||
|
||||
|
||||
proc newSearchManager*(position: Position, positions: seq[Position], transpositions: ptr TTable, stopFlag, searchFlag: ptr Atomic[bool],
|
||||
proc newSearchManager*(position: Position, positions: seq[Position], transpositions: ptr TTable,
|
||||
history: ptr HistoryTable, killers: ptr KillersTable): SearchManager =
|
||||
var board = newChessboard()
|
||||
board.position = position
|
||||
board.positions = positions
|
||||
result = SearchManager(board: board, transpositionTable: transpositions, stopFlag: stopFlag,
|
||||
searchFlag: searchFlag, history: history, killers: killers)
|
||||
result = SearchManager(board: newChessboard(), transpositionTable: transpositions, stop: Atomic[bool](),
|
||||
searching: Atomic[bool](), pondering: Atomic[bool](), history: history,
|
||||
killers: killers)
|
||||
result.board.position = position
|
||||
result.board.positions = positions
|
||||
for i in 0..MAX_DEPTH:
|
||||
for j in 0..MAX_DEPTH:
|
||||
result.pvMoves[i][j] = nullMove()
|
||||
|
||||
|
||||
proc isSearching*(self: SearchManager): bool =
|
||||
proc isSearching*(self: var SearchManager): bool =
|
||||
## Returns whether a search for the best
|
||||
## move is in progress
|
||||
result = self.searchFlag[].load()
|
||||
result = self.searching.load()
|
||||
|
||||
|
||||
proc stop*(self: SearchManager) =
|
||||
proc stop*(self: var SearchManager) =
|
||||
## Stops the search if it is
|
||||
## running
|
||||
if self.isSearching():
|
||||
self.stopFlag[].store(true)
|
||||
self.stop.store(true)
|
||||
|
||||
|
||||
proc isKillerMove(self: SearchManager, move: Move, ply: int): bool =
|
||||
|
@ -246,7 +247,9 @@ proc reorderMoves(self: SearchManager, moves: var MoveList, ply: int) =
|
|||
|
||||
|
||||
proc timedOut(self: SearchManager): bool = getMonoTime() >= self.hardLimit
|
||||
proc cancelled(self: SearchManager): bool = self.stopFlag[].load()
|
||||
proc isPondering*(self: var SearchManager): bool = self.pondering.load()
|
||||
proc stopPondering*(self: var SearchManager) = self.pondering.store(false)
|
||||
proc cancelled(self: var SearchManager): bool = self.stop.load()
|
||||
proc elapsedTime(self: SearchManager): int64 = (getMonoTime() - self.searchStart).inMilliseconds()
|
||||
|
||||
|
||||
|
@ -272,13 +275,13 @@ proc log(self: SearchManager, depth: int) =
|
|||
echo logMsg
|
||||
|
||||
|
||||
proc shouldStop(self: SearchManager): bool =
|
||||
proc shouldStop(self: var SearchManager): bool =
|
||||
## Returns whether searching should
|
||||
## stop
|
||||
if self.cancelled():
|
||||
# Search has been cancelled!
|
||||
return true
|
||||
if self.timedOut():
|
||||
if self.timedOut() and not self.isPondering():
|
||||
# We ran out of time!
|
||||
return true
|
||||
if self.maxNodes > 0 and self.nodeCount >= self.maxNodes:
|
||||
|
@ -644,6 +647,7 @@ proc findBestLine*(self: var SearchManager, timeRemaining, increment: int64, max
|
|||
result = @[]
|
||||
var pv: array[256, Move]
|
||||
self.maxNodes = maxNodes
|
||||
self.pondering.store(ponder)
|
||||
self.searchMoves = searchMoves
|
||||
self.searchStart = getMonoTime()
|
||||
self.hardLimit = self.searchStart + initDuration(milliseconds=maxSearchTime)
|
||||
|
@ -651,7 +655,7 @@ proc findBestLine*(self: var SearchManager, timeRemaining, increment: int64, max
|
|||
var maxDepth = maxDepth
|
||||
if maxDepth == -1:
|
||||
maxDepth = 30
|
||||
self.searchFlag[].store(true)
|
||||
self.searching.store(true)
|
||||
# Iterative deepening loop
|
||||
var score = Score(0)
|
||||
for depth in 1..min(MAX_DEPTH, maxDepth):
|
||||
|
@ -668,10 +672,10 @@ proc findBestLine*(self: var SearchManager, timeRemaining, increment: int64, max
|
|||
# Soft time management: don't start a new search iteration
|
||||
# if the soft limit has expired, as it is unlikely to complete
|
||||
# anyway
|
||||
if getMonoTime() >= self.softLimit:
|
||||
if getMonoTime() >= self.softLimit and not self.isPondering():
|
||||
break
|
||||
self.searchFlag[].store(false)
|
||||
self.stopFlag[].store(false)
|
||||
self.searching.store(false)
|
||||
self.stop.store(false)
|
||||
for move in pv:
|
||||
if move == nullMove():
|
||||
break
|
||||
|
|
|
@ -81,7 +81,7 @@ proc newTranspositionTable*(size: uint64): TTable =
|
|||
result.size = numEntries
|
||||
|
||||
|
||||
func clear*(self: var TTable) =
|
||||
func clear*(self: var TTable) {.inline.} =
|
||||
## Clears the transposition table
|
||||
## without releasing the memory
|
||||
## associated with it
|
||||
|
@ -97,13 +97,6 @@ func resize*(self: var TTable, newSize: uint64) =
|
|||
self.size = numEntries
|
||||
|
||||
|
||||
func destroy*(self: var TTable) =
|
||||
## Permanently and irreversibly
|
||||
## destroys the transposition table
|
||||
self.data = @[]
|
||||
self.size = 0
|
||||
|
||||
|
||||
func getIndex(self: TTable, key: ZobristKey): uint64 =
|
||||
## Retrieves the index of the given
|
||||
## zobrist key in our transposition table
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
## Implementation of a UCI compatible server
|
||||
import std/strutils
|
||||
import std/strformat
|
||||
import std/atomics
|
||||
|
||||
|
||||
import board
|
||||
|
@ -38,6 +37,7 @@ type
|
|||
## doesn't like sharing references across thread (despite
|
||||
## the fact that it should be safe to do so)
|
||||
searchState: ptr SearchManager
|
||||
printMove: ptr bool
|
||||
# Size of the transposition table (in megabytes)
|
||||
hashTableSize: uint64
|
||||
# # Atomic boolean flag to interrupt the search
|
||||
|
@ -173,6 +173,8 @@ proc handleUCIGoCommand(session: UCISession, command: seq[string]): UCICommand =
|
|||
of "infinite":
|
||||
result.wtime = int32.high()
|
||||
result.btime = int32.high()
|
||||
of "ponder":
|
||||
result.ponder = true
|
||||
of "wtime":
|
||||
result.wtime = command[current].parseInt()
|
||||
of "btime":
|
||||
|
@ -329,7 +331,6 @@ proc bestMove(args: tuple[session: UCISession, command: UCICommand]) {.thread.}
|
|||
board.position = session.position
|
||||
board.positions = session.history
|
||||
let command = args.command
|
||||
var searcher = session.searchState[]
|
||||
var
|
||||
timeRemaining = (if session.position.sideToMove == White: command.wtime else: command.btime)
|
||||
increment = (if session.position.sideToMove == White: command.winc else: command.binc)
|
||||
|
@ -339,11 +340,12 @@ proc bestMove(args: tuple[session: UCISession, command: UCICommand]) {.thread.}
|
|||
increment = 0
|
||||
elif timeRemaining == 0:
|
||||
timeRemaining = int32.high()
|
||||
var line = searcher.findBestLine(timeRemaining, increment, command.depth, command.nodes, command.searchmoves, timePerMove, command.ponder)
|
||||
if line.len() == 1:
|
||||
var line = session.searchState[].findBestLine(timeRemaining, increment, command.depth, command.nodes, command.searchmoves, timePerMove, command.ponder)
|
||||
if session.printMove[]:
|
||||
if line.len() == 1:
|
||||
echo &"bestmove {line[0].toAlgebraic()}"
|
||||
else:
|
||||
echo &"bestmove {line[0].toAlgebraic()} ponder {line[1].toAlgebraic()}"
|
||||
else:
|
||||
echo &"bestmove {line[0].toAlgebraic()} ponder {line[1].toAlgebraic()}"
|
||||
|
||||
|
||||
proc startUCISession* =
|
||||
|
@ -359,14 +361,13 @@ proc startUCISession* =
|
|||
# God forbid we try to use atomic ARC like it was intended. Raw pointers
|
||||
# it is then... sigh
|
||||
var
|
||||
stopFlag = cast[ptr Atomic[bool]](alloc0(sizeof(Atomic[bool])))
|
||||
searchFlag = cast[ptr Atomic[bool]](alloc0(sizeof(Atomic[bool])))
|
||||
transpositionTable = cast[ptr TTable](alloc0(sizeof(TTable)))
|
||||
historyTable = cast[ptr HistoryTable](alloc0(sizeof(HistoryTable)))
|
||||
killerMoves = cast[ptr KillersTable](alloc0(sizeof(KillersTable)))
|
||||
transpositionTable[] = newTranspositionTable(session.hashTableSize * 1024 * 1024)
|
||||
session.searchState = cast[ptr SearchManager](alloc0(sizeof(SearchManager)))
|
||||
session.searchState[] = newSearchManager(session.position, session.history, transpositionTable, stopFlag, searchFlag, historyTable, killerMoves)
|
||||
session.searchState[] = newSearchManager(session.position, session.history, transpositionTable, historyTable, killerMoves)
|
||||
session.printMove = cast[ptr bool](alloc0(sizeof(bool)))
|
||||
# Initialize history table
|
||||
for color in PieceColor.White..PieceColor.Black:
|
||||
for i in Square(0)..Square(63):
|
||||
|
@ -408,14 +409,9 @@ proc startUCISession* =
|
|||
of Debug:
|
||||
session.debug = cmd.on
|
||||
of NewGame:
|
||||
if transpositionTable[].size() == 0:
|
||||
if session.debug:
|
||||
echo &"info string allocating new TT of size {session.hashTableSize} MiB"
|
||||
transpositionTable[] = newTranspositionTable(session.hashTableSize * 1024 * 1024)
|
||||
else:
|
||||
if session.debug:
|
||||
echo &"info string clearing out TT of size {session.hashTableSize} MiB"
|
||||
transpositionTable[].clear()
|
||||
if session.debug:
|
||||
echo &"info string clearing out TT of size {session.hashTableSize} MiB"
|
||||
transpositionTable[].clear()
|
||||
# Re-Initialize history table
|
||||
for color in PieceColor.White..PieceColor.Black:
|
||||
for i in Square(0)..Square(63):
|
||||
|
@ -430,24 +426,25 @@ proc startUCISession* =
|
|||
echo "info string ponder move has ben hit"
|
||||
if not session.searchState[].isSearching():
|
||||
continue
|
||||
joinThread(searchThread)
|
||||
session.searchState[].stopPondering()
|
||||
if session.debug:
|
||||
echo "info string ponder search stopped"
|
||||
createThread(searchThread, bestMove, (session, cmd))
|
||||
if session.debug:
|
||||
echo "info string search started"
|
||||
echo "info string switched to normal search"
|
||||
of Go:
|
||||
when not defined(historyPenalty):
|
||||
# Scale our history coefficients
|
||||
for color in PieceColor.White..PieceColor.Black:
|
||||
for source in Square(0)..Square(63):
|
||||
for target in Square(0)..Square(63):
|
||||
historyTable[color][source][target] = historyTable[color][source][target] div 2
|
||||
if searchThread.running:
|
||||
joinThread(searchThread)
|
||||
createThread(searchThread, bestMove, (session, cmd))
|
||||
if session.debug:
|
||||
echo "info string search started"
|
||||
session.printMove[] = true
|
||||
if not cmd.ponder and session.searchState[].isPondering():
|
||||
session.searchState[].stopPondering()
|
||||
else:
|
||||
when not defined(historyPenalty):
|
||||
# Scale our history coefficients
|
||||
for color in PieceColor.White..PieceColor.Black:
|
||||
for source in Square(0)..Square(63):
|
||||
for target in Square(0)..Square(63):
|
||||
historyTable[color][source][target] = historyTable[color][source][target] div 2
|
||||
if searchThread.running:
|
||||
joinThread(searchThread)
|
||||
createThread(searchThread, bestMove, (session, cmd))
|
||||
if session.debug:
|
||||
echo "info string search started"
|
||||
of Stop:
|
||||
if not session.searchState[].isSearching():
|
||||
continue
|
||||
|
@ -474,10 +471,13 @@ proc startUCISession* =
|
|||
else:
|
||||
discard
|
||||
of Position:
|
||||
# Due to the way the whole thing is designed, the
|
||||
# position is actually set when the command is parsed
|
||||
# rather than when it is processed here
|
||||
discard
|
||||
if session.searchState[].isPondering():
|
||||
session.printMove[] = false
|
||||
session.searchState[].stopPondering()
|
||||
session.searchState[].stop()
|
||||
joinThread(searchThread)
|
||||
session.searchState[].board.position = session.position
|
||||
session.searchState[].board.positions = session.history
|
||||
else:
|
||||
discard
|
||||
except IOError:
|
||||
|
|
Loading…
Reference in New Issue