Work on magic bitboard generation
This commit is contained in:
parent
a07e9cc475
commit
244ad1725a
|
@ -25,12 +25,13 @@ type
|
||||||
BackwardRight
|
BackwardRight
|
||||||
|
|
||||||
# Overloaded operators and functions for our bitboard type
|
# Overloaded operators and functions for our bitboard type
|
||||||
func `shl`*(a: Bitboard, x: Positive): Bitboard = Bitboard(a.uint64 shl x)
|
func `shl`*(a: Bitboard, x: Natural): Bitboard = Bitboard(a.uint64 shl x)
|
||||||
func `shr`*(a: Bitboard, x: Positive): Bitboard = Bitboard(a.uint64 shr 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 `and`*(a, b: Bitboard): Bitboard = Bitboard(a.uint64 and b.uint64)
|
||||||
func `or`*(a, b: Bitboard): Bitboard = Bitboard(a.uint64 or b.uint64)
|
func `or`*(a, b: Bitboard): Bitboard = Bitboard(a.uint64 or b.uint64)
|
||||||
func `not`*(a: Bitboard): Bitboard = Bitboard(not a.uint64)
|
func `not`*(a: Bitboard): Bitboard = Bitboard(not a.uint64)
|
||||||
func `shr`*(a, b: Bitboard): Bitboard = Bitboard(a.uint64 and b.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 `-`*(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)
|
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 `-`*(a: Bitboard, b: SomeUnsignedInt): Bitboard = Bitboard(a.uint64 - b.uint64)
|
||||||
func `div`*(a: Bitboard, b: SomeUnsignedInt): Bitboard = Bitboard(a.uint64 div b)
|
func `div`*(a: Bitboard, b: SomeUnsignedInt): Bitboard = Bitboard(a.uint64 div b)
|
||||||
func `*`*(a: Bitboard, b: SomeUnsignedInt): Bitboard = Bitboard(a.uint64 * 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, b: Bitboard): bool {.inline.} = a.uint64 == b.uint64
|
||||||
func `==`*(a: Bitboard, b: SomeInteger): 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
|
func `!=`*(a, b: Bitboard): bool {.inline.} = a.uint64 != b.uint64
|
||||||
|
@ -77,6 +79,19 @@ iterator items*(self: Bitboard): Square =
|
||||||
bits = bits and bits - 1
|
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] =
|
iterator pairs*(self: Bitboard): tuple[i: int, sq: Square] =
|
||||||
var i = 0
|
var i = 0
|
||||||
for item in self:
|
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!
|
# 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] =
|
func computeKingBitboards: array[64, Bitboard] =
|
||||||
## Precomputes all the movement bitboards for the king
|
## Precomputes all the movement bitboards for the king
|
||||||
|
@ -271,8 +266,6 @@ func computeKnightBitboards: array[64, Bitboard] =
|
||||||
result[i] = movements
|
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()
|
# For some reason nim freaks out if I try to call computeKingBitboards()
|
||||||
# at compile-time. ¯\_(ツ)_/¯
|
# at compile-time. ¯\_(ツ)_/¯
|
||||||
let
|
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.} =
|
proc toSquare*(s: string): Square {.discardable.} =
|
||||||
|
|
Loading…
Reference in New Issue