Work on magic bitboard generation
This commit is contained in:
parent
eede621bb6
commit
e3225bb4bc
|
@ -25,12 +25,13 @@ type
|
|||
BackwardRight
|
||||
|
||||
# Overloaded operators and functions for our bitboard type
|
||||
func `shl`*(a: Bitboard, x: Positive): Bitboard = Bitboard(a.uint64 shl x)
|
||||
func `shr`*(a: Bitboard, x: Positive): Bitboard = Bitboard(a.uint64 shr x)
|
||||
func `shl`*(a: Bitboard, x: Natural): Bitboard = Bitboard(a.uint64 shl x)
|
||||
func `shr`*(a: Bitboard, x: Natural): Bitboard = Bitboard(a.uint64 shr x)
|
||||
func `and`*(a, b: Bitboard): Bitboard = Bitboard(a.uint64 and b.uint64)
|
||||
func `or`*(a, b: Bitboard): Bitboard = Bitboard(a.uint64 or b.uint64)
|
||||
func `not`*(a: Bitboard): Bitboard = Bitboard(not a.uint64)
|
||||
func `shr`*(a, b: Bitboard): Bitboard = Bitboard(a.uint64 and b.uint64)
|
||||
func `xor`*(a, b: Bitboard): Bitboard = Bitboard(a.uint64 xor b.uint64)
|
||||
func `+`*(a, b: Bitboard): Bitboard = Bitboard(a.uint64 + b.uint64)
|
||||
func `-`*(a, b: Bitboard): Bitboard = Bitboard(a.uint64 - b.uint64)
|
||||
func `div`*(a, b: Bitboard): Bitboard = Bitboard(a.uint64 div b.uint64)
|
||||
|
@ -39,6 +40,7 @@ func `+`*(a: Bitboard, b: SomeUnsignedInt): Bitboard = Bitboard(a.uint64 + b)
|
|||
func `-`*(a: Bitboard, b: SomeUnsignedInt): Bitboard = Bitboard(a.uint64 - b.uint64)
|
||||
func `div`*(a: Bitboard, b: SomeUnsignedInt): Bitboard = Bitboard(a.uint64 div b)
|
||||
func `*`*(a: Bitboard, b: SomeUnsignedInt): Bitboard = Bitboard(a.uint64 * b)
|
||||
func `*`*(a: SomeUnsignedInt, b: Bitboard): Bitboard = Bitboard(a.uint64 * b)
|
||||
func `==`*(a, b: Bitboard): bool {.inline.} = a.uint64 == b.uint64
|
||||
func `==`*(a: Bitboard, b: SomeInteger): bool {.inline.} = a.uint64 == b.uint64
|
||||
func `!=`*(a, b: Bitboard): bool {.inline.} = a.uint64 != b.uint64
|
||||
|
@ -77,6 +79,19 @@ iterator items*(self: Bitboard): Square =
|
|||
bits = bits and bits - 1
|
||||
|
||||
|
||||
iterator subsets*(self: Bitboard): Bitboard =
|
||||
## Iterates over all the subsets of the given
|
||||
## bitboard using the Carry-Rippler trick
|
||||
|
||||
# Thanks analog-hors :D
|
||||
var subset = Bitboard(0)
|
||||
while true:
|
||||
subset = (subset - self) and self
|
||||
if subset == 0:
|
||||
break
|
||||
yield subset
|
||||
|
||||
|
||||
iterator pairs*(self: Bitboard): tuple[i: int, sq: Square] =
|
||||
var i = 0
|
||||
for item in self:
|
||||
|
@ -211,26 +226,6 @@ func shortKnightDownRightRelativeTo*(self: Bitboard, side: PieceColor): Bitboard
|
|||
|
||||
# We precompute as much stuff as possible: lookup tables are fast!
|
||||
|
||||
func computeDiagonalBitboards: array[14, Bitboard] {.compileTime.} =
|
||||
## Precomputes all the bitboards for diagonals
|
||||
result[0] = Bitboard(0x8040201008040201'u64)
|
||||
var
|
||||
col = 1
|
||||
i = 0
|
||||
# Left to right
|
||||
while col < 8:
|
||||
result[col] = Bitboard(0x8040201008040201'u64) shl (8 * col)
|
||||
inc(col)
|
||||
inc(i)
|
||||
result[i] = Bitboard(0x102040810204080'u64)
|
||||
inc(i)
|
||||
col = 1
|
||||
# Right to left
|
||||
while col < 7:
|
||||
result[i] = Bitboard(0x102040810204080'u64) shr (8 * col)
|
||||
inc(i)
|
||||
inc(col)
|
||||
|
||||
|
||||
func computeKingBitboards: array[64, Bitboard] =
|
||||
## Precomputes all the movement bitboards for the king
|
||||
|
@ -271,8 +266,6 @@ func computeKnightBitboards: array[64, Bitboard] =
|
|||
result[i] = movements
|
||||
|
||||
|
||||
# Precomputing stuff is *very* helpful for chess, it turns out
|
||||
const DIAGONAL_BITBOARDS* = computeDiagonalBitboards()
|
||||
# For some reason nim freaks out if I try to call computeKingBitboards()
|
||||
# at compile-time. ¯\_(ツ)_/¯
|
||||
let
|
||||
|
|
|
@ -0,0 +1,158 @@
|
|||
## 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]
|
|
@ -48,7 +48,7 @@ func rowFromSquare*(square: Square): int8 = square.int8 div 8 + 1
|
|||
|
||||
|
||||
|
||||
func makeSquare*(rank, file: SomeInteger): Square = Square(rank * 8 + file)
|
||||
func makeSquare*(rank, file: SomeInteger): Square = Square((rank * 8) + file)
|
||||
|
||||
|
||||
proc toSquare*(s: string): Square {.discardable.} =
|
||||
|
|
Loading…
Reference in New Issue