213 lines
7.3 KiB
Nim
213 lines
7.3 KiB
Nim
## Implements low-level bit operations
|
|
|
|
|
|
import std/bitops
|
|
import std/strutils
|
|
|
|
|
|
import pieces
|
|
import moves
|
|
|
|
|
|
type
|
|
Bitboard* = distinct uint64
|
|
## A bitboard
|
|
|
|
Direction* = enum
|
|
## A move direction enumeration
|
|
Forward,
|
|
Backward,
|
|
Left,
|
|
Right
|
|
ForwardLeft,
|
|
ForwardRight,
|
|
BackwardLeft,
|
|
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 `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 `+`*(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 `*`*(a, b: Bitboard): Bitboard = Bitboard(a.uint64 * b.uint64)
|
|
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, 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
|
|
func `!=`*(a: Bitboard, b: SomeInteger): bool {.inline.} = a.uint64 != b.uint64
|
|
|
|
|
|
func getFileMask*(file: int): Bitboard = Bitboard(0x101010101010101'u64) shl file.uint64
|
|
func getRankMask*(rank: int): Bitboard = Bitboard(0xff) shl uint64(8 * rank)
|
|
func toBitboard*(square: SomeInteger): Bitboard = Bitboard(1'u64) shl square.uint64
|
|
func toBitboard*(square: Square): Bitboard = toBitboard(square.int8)
|
|
|
|
proc toSquare*(b: Bitboard): Square = Square(b.uint64.countTrailingZeroBits())
|
|
func createMove*(startSquare: Bitboard, targetSquare: Square, flags: varargs[MoveFlag]): Move =
|
|
result = createMove(startSquare.toSquare(), targetSquare, flags)
|
|
|
|
|
|
func createMove*(startSquare: Square, targetSquare: Bitboard, flags: varargs[MoveFlag]): Move =
|
|
result = createMove(startSquare, targetSquare.toSquare(), flags)
|
|
|
|
|
|
func createMove*(startSquare, targetSquare: Bitboard, flags: varargs[MoveFlag]): Move =
|
|
result = createMove(startSquare.toSquare(), targetSquare.toSquare(), flags)
|
|
|
|
|
|
func toBin*(x: Bitboard, b: Positive = 64): string = toBin(BiggestInt(x), b)
|
|
func toBin*(x: uint64, b: Positive = 64): string = toBin(Bitboard(x), b)
|
|
|
|
|
|
iterator items*(self: Bitboard): Square =
|
|
## Iterates ove the given bitboard
|
|
## and returns all the squares that
|
|
## are set
|
|
var bits = self
|
|
while bits != 0:
|
|
yield bits.toSquare()
|
|
bits = bits and bits - 1
|
|
|
|
|
|
iterator pairs*(self: Bitboard): tuple[i: int, sq: Square] =
|
|
var i = 0
|
|
for item in self:
|
|
yield (i, item)
|
|
inc(i)
|
|
|
|
|
|
func pretty*(self: Bitboard): string =
|
|
|
|
iterator items(self: Bitboard): uint8 =
|
|
## Iterates over all the bits in the
|
|
## given bitboard
|
|
for i in 0..63:
|
|
yield self.uint64.bitsliced(i..i).uint8
|
|
|
|
|
|
iterator pairs(self: Bitboard): (int, uint8) =
|
|
var i = 0
|
|
for bit in self:
|
|
yield (i, bit)
|
|
inc(i)
|
|
|
|
## Returns a prettyfied version of
|
|
## the given bitboard
|
|
result &= "- - - - - - - -\n"
|
|
for i, bit in self:
|
|
if i > 0 and i mod 8 == 0:
|
|
result &= "\n"
|
|
result &= $bit & " "
|
|
result &= "\n- - - - - - - -"
|
|
|
|
|
|
func `$`*(self: Bitboard): string = self.pretty()
|
|
|
|
|
|
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)
|
|
|
|
|
|
const diagonalBitboards = computeDiagonalBitboards()
|
|
|
|
|
|
func getDirectionMask*(bitboard: Bitboard, color: PieceColor, direction: Direction): Bitboard =
|
|
## Get a bitmask relative to the given bitboard
|
|
## for the given direction for a piece of the
|
|
## given color
|
|
case color:
|
|
of White:
|
|
case direction:
|
|
of Forward:
|
|
return bitboard shr 8
|
|
of Backward:
|
|
return bitboard shl 8
|
|
of ForwardRight:
|
|
return bitboard shr 7
|
|
of ForwardLeft:
|
|
return bitboard shr 9
|
|
of BackwardRight:
|
|
return bitboard shl 9
|
|
of BackwardLeft:
|
|
return bitboard shr 7
|
|
else:
|
|
discard
|
|
of Black:
|
|
# The directions for black are just the opposite of those for white,
|
|
# so we avoid duplicating any code
|
|
case direction:
|
|
of Forward:
|
|
return bitboard shl 8
|
|
of Backward:
|
|
return bitboard shr 8
|
|
of ForwardLeft:
|
|
return bitboard shl 9
|
|
of ForwardRight:
|
|
return bitboard shl 7
|
|
of BackwardRight:
|
|
return bitboard shr 9
|
|
of BackwardLeft:
|
|
return bitboard shl 7
|
|
else:
|
|
discard
|
|
else:
|
|
discard
|
|
|
|
|
|
func getLastRank*(color: PieceColor): Bitboard = (if color == White: getRankMask(0) else: getRankMask(7))
|
|
|
|
func getDirectionMask*(square: Square, color: PieceColor, direction: Direction): Bitboard =
|
|
## Get a bitmask for the given direction for a piece
|
|
## of the given color located at the given square
|
|
result = getDirectionMask(toBitboard(square), color, direction)
|
|
|
|
|
|
func forwardRelativeTo*(self: Bitboard, side: PieceColor): Bitboard = getDirectionMask(self, side, Forward)
|
|
func doubleForwardRelativeTo*(self: Bitboard, side: PieceColor): Bitboard = self.forwardRelativeTo(side).forwardRelativeTo(side)
|
|
|
|
func backwardRelativeTo*(self: Bitboard, side: PieceColor): Bitboard = getDirectionMask(self, side, Backward)
|
|
func doubleBackwardRelativeTo*(self: Bitboard, side: PieceColor): Bitboard = self.backwardRelativeTo(side).backwardRelativeTo(side)
|
|
|
|
|
|
# We mask off the first and last ranks for
|
|
# left and right movements respectively to
|
|
# avoid weird wraparounds
|
|
func forwardRightRelativeTo*(self: Bitboard, side: PieceColor): Bitboard =
|
|
getDirectionMask(self, side, ForwardRight) and not getFileMask(7)
|
|
|
|
|
|
func forwardLeftRelativeTo*(self: Bitboard, side: PieceColor): Bitboard =
|
|
getDirectionMask(self, side, ForwardLeft) and not getFileMask(0)
|
|
|
|
|
|
func bottomRightRelativeTo*(self: Bitboard, side: PieceColor): Bitboard =
|
|
getDirectionMask(self, side, BackwardRight) and not getFileMask(7)
|
|
|
|
|
|
func bottomLeftRelativeTo*(self: Bitboard, side: PieceColor): Bitboard =
|
|
getDirectionMask(self, side, BackwardLeft) and not getFileMask(0) |