CPG/Chess/nimfish/nimfishpkg/transpositions.nim

82 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 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
depth: uint8
TTable* = ref object
data: seq[TTEntry]
size: uint64
proc newTranspositionTable*(size: uint64): TTable =
## Initializes a new transposition table of
## size bytes
new(result)
result.data = newSeq[TTEntry](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, depth: uint8, score: Score, hash: ZobristKey, flag: TTentryFlag) =
## Stores an entry in the transposition table
self.data[self.getIndex(hash)] = TTEntry(flag: flag, score: int16(score), hash: hash, depth: depth)
proc get*(self: TTable, hash: ZobristKey, depth: uint8): tuple[success: bool, entry: TTEntry] =
## Attempts to get the entry with the given
## zobrist key at the given depth in the table.
## The success parameter is set to false upon detection
## of a hash collision or if the provided depth is greater
## than the one stored in the table: the result should be
## considered invalid unless it's true
result.entry = self.data[self.getIndex(hash)]
result.success = result.entry.hash == hash and result.entry.depth >= depth