85 lines
2.9 KiB
Nim
85 lines
2.9 KiB
Nim
# Copyright 2024 Mattia Giambirtone & All Contributors
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
## Implementation of a transposition table
|
|
|
|
import zobrist
|
|
import moves
|
|
import eval
|
|
|
|
|
|
import nint128
|
|
|
|
|
|
type
|
|
TTentryFlag = enum
|
|
## A flag for an entry in the
|
|
## transposition table
|
|
Exact = 0'i8
|
|
LowerBound = 1'i8
|
|
UpperBound = 2'i8
|
|
|
|
TTEntry* {.packed.} = object
|
|
## An entry in the transposition table
|
|
flag*: TTentryFlag
|
|
# Scores are int32s for convenience (less chance
|
|
# of overflows and stuff), but they are capped to
|
|
# fit into an int16
|
|
score*: int16
|
|
hash*: ZobristKey
|
|
bestMove*: Move
|
|
|
|
TTable = ref object
|
|
data: ptr UncheckedArray[TTEntry]
|
|
size: 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)
|