159 lines
5.2 KiB
Nim
159 lines
5.2 KiB
Nim
## Low-level magic bitboard stuff
|
|
|
|
# Stolen from this amazing article: https://analog-hors.github.io/site/magic-bitboards/
|
|
|
|
import bitboards
|
|
import pieces
|
|
|
|
|
|
import std/random
|
|
|
|
|
|
export pieces
|
|
export bitboards
|
|
|
|
randomize()
|
|
|
|
|
|
type
|
|
MagicEntry = object
|
|
## A magic bitboard entry
|
|
mask: Bitboard
|
|
value: uint64
|
|
indexBits: uint8
|
|
|
|
|
|
proc generateRookBlockers: array[64, Bitboard] {.compileTime.} =
|
|
## Generates all blocker masks for rooks
|
|
for rank in 0..7:
|
|
for file in 0..7:
|
|
let
|
|
square = makeSquare(rank, file)
|
|
i = square.int
|
|
bitboard = square.toBitboard()
|
|
var
|
|
current = bitboard
|
|
last = makeSquare(rank, 7).toBitboard()
|
|
while true:
|
|
current = current.rightRelativeTo(White)
|
|
if current == last or current == 0:
|
|
break
|
|
result[i] = result[i] or current
|
|
current = bitboard
|
|
last = makeSquare(rank, 0).toBitboard()
|
|
while true:
|
|
current = current.leftRelativeTo(White)
|
|
if current == last or current == 0:
|
|
break
|
|
result[i] = result[i] or current
|
|
current = bitboard
|
|
last = makeSquare(0, file).toBitboard()
|
|
while true:
|
|
current = current.forwardRelativeTo(White)
|
|
if current == last or current == 0:
|
|
break
|
|
result[i] = result[i] or current
|
|
current = bitboard
|
|
last = makeSquare(7, file).toBitboard()
|
|
while true:
|
|
current = current.backwardRelativeTo(White)
|
|
if current == last or current == 0:
|
|
break
|
|
result[i] = result[i] or current
|
|
|
|
|
|
func generateBishopBlockers: array[64, Bitboard] {.compileTime.} =
|
|
for rank in 0..7:
|
|
for file in 0..7:
|
|
# Generate all possible movement masks
|
|
let
|
|
square = makeSquare(rank, file)
|
|
i = square.int
|
|
bitboard = square.toBitboard()
|
|
var
|
|
current = bitboard
|
|
while true:
|
|
current = current.backwardRightRelativeTo(White)
|
|
if current == 0:
|
|
break
|
|
result[i] = result[i] or current
|
|
current = bitboard
|
|
while true:
|
|
current = current.backwardLeftRelativeTo(White)
|
|
if current == 0:
|
|
break
|
|
result[i] = result[i] or current
|
|
current = bitboard
|
|
while true:
|
|
current = current.forwardLeftRelativeTo(White)
|
|
if current == 0:
|
|
break
|
|
result[i] = result[i] or current
|
|
current = bitboard
|
|
while true:
|
|
current = current.forwardRightRelativeTo(White)
|
|
if current == 0:
|
|
break
|
|
result[i] = result[i] or current
|
|
# Mask off the edges
|
|
result[i] = result[i] and not getFileMask(0)
|
|
result[i] = result[i] and not getFileMask(7)
|
|
result[i] = result[i] and not getRankMask(0)
|
|
result[i] = result[i] and not getRankMask(7)
|
|
|
|
|
|
func getIndex(magic: MagicEntry, blockers: Bitboard): uint {.inline.} =
|
|
## Computes an index into the magic bitboard table using
|
|
## the given magic entry and the blockers bitboard
|
|
let
|
|
blockers = blockers and magic.mask
|
|
hash = blockers * magic.value
|
|
index = hash shl (64 - magic.indexBits)
|
|
return index.uint
|
|
|
|
|
|
# Magic number tables and their corresponding moves
|
|
var
|
|
ROOK_MAGICS: seq[MagicEntry]
|
|
ROOK_MOVES: array[64, seq[Bitboard]]
|
|
BISHOP_MAGICS: seq[MagicEntry]
|
|
BISHOP_MOVES: array[64, seq[Bitboard]]
|
|
|
|
|
|
proc getRookMoves(square: Square, blockers: Bitboard): Bitboard =
|
|
## Returns the move bitboard for the rook at the given
|
|
## square with the given blockers bitboard
|
|
let
|
|
magic = ROOK_MAGICS[square.uint]
|
|
moves = ROOK_MOVES[square.uint]
|
|
return moves[getIndex(magic, blockers)]
|
|
|
|
|
|
proc getBishopMoves(square: Square, blockers: Bitboard): Bitboard =
|
|
## Returns the move bitboard for the bishop at the given
|
|
## square with the given blockers bitboard
|
|
let
|
|
magic = BISHOP_MAGICS[square.uint]
|
|
moves = BISHOP_MOVES[square.uint]
|
|
return moves[getIndex(magic, blockers)]
|
|
|
|
|
|
# Precomputed blocker masks. Only pieces on these bitboards
|
|
# are actually able to block the movement of a sliding piece,
|
|
# regardless of color
|
|
const
|
|
ROOK_BLOCKERS* = generateRookBlockers()
|
|
BISHOP_BLOCKERS* = generateBishopBlockers()
|
|
|
|
|
|
|
|
# func findMagic(slider: PieceKind, square: Square, indexBits: uint8): tuple[magic: MagicEntry, moves: seq[Bitboard]] =
|
|
# ## Given a slider piece, its starting square and the number of desired
|
|
# ## index bits, find a magic number that perfectly maps all the possible
|
|
# ## sliding moves for that piece at that square into an appropriately sized
|
|
# ## perfect hash table with at most 2^indexBits entries
|
|
# var mask: Bitboard
|
|
# case slider:
|
|
# of Rook:
|
|
# mask = ROOK_BLOCKERS[square.uint]
|