2024-04-16 15:24:31 +02:00
|
|
|
## Handling of moves
|
|
|
|
import pieces
|
|
|
|
|
|
|
|
|
|
|
|
type
|
|
|
|
MoveFlag* = enum
|
|
|
|
## An enumeration of move flags
|
|
|
|
Default = 0'u16, # No flag
|
|
|
|
EnPassant = 1, # Move is a capture with en passant
|
|
|
|
Capture = 2, # Move is a capture
|
|
|
|
DoublePush = 4, # Move is a double pawn push
|
|
|
|
# Castling metadata
|
|
|
|
CastleLong = 8,
|
|
|
|
CastleShort = 16,
|
|
|
|
# Pawn promotion metadata
|
|
|
|
PromoteToQueen = 32,
|
|
|
|
PromoteToRook = 64,
|
|
|
|
PromoteToBishop = 128,
|
|
|
|
PromoteToKnight = 256
|
|
|
|
|
|
|
|
Move* = object
|
|
|
|
## A chess move
|
|
|
|
startSquare*: Square
|
|
|
|
targetSquare*: Square
|
|
|
|
flags*: uint16
|
|
|
|
|
2024-04-16 16:29:21 +02:00
|
|
|
MoveList* = object
|
|
|
|
## A list of moves
|
|
|
|
data: array[218, Move]
|
|
|
|
len: int8
|
|
|
|
|
2024-04-16 23:45:32 +02:00
|
|
|
func `[]`*(self: MoveList, i: SomeInteger): Move =
|
|
|
|
when not defined(danger):
|
|
|
|
if i >= self.len:
|
|
|
|
raise newException(IndexDefect, &"move list access out of bounds ({i} >= {self.len})")
|
|
|
|
result = self.data[i]
|
|
|
|
|
2024-04-16 16:29:21 +02:00
|
|
|
|
|
|
|
iterator items*(self: MoveList): Move =
|
|
|
|
var i = 0
|
|
|
|
while self.len > i:
|
|
|
|
yield self.data[i]
|
|
|
|
inc(i)
|
|
|
|
|
|
|
|
|
|
|
|
iterator pairs*(self: MoveList): tuple[i: int, move: Move] =
|
|
|
|
var i = 0
|
|
|
|
for item in self:
|
|
|
|
yield (i, item)
|
|
|
|
|
|
|
|
|
|
|
|
func add*(self: var MoveList, move: Move) {.inline.} =
|
|
|
|
self.data[self.len] = move
|
|
|
|
inc(self.len)
|
|
|
|
|
|
|
|
|
2024-04-16 23:45:32 +02:00
|
|
|
func clear*(self: var MoveList) {.inline.} =
|
|
|
|
self.len = 0
|
|
|
|
|
|
|
|
|
2024-04-16 16:29:21 +02:00
|
|
|
func contains*(self: MoveList, move: Move): bool {.inline.} =
|
|
|
|
for item in self:
|
|
|
|
if move == item:
|
|
|
|
return true
|
|
|
|
return false
|
|
|
|
|
|
|
|
|
|
|
|
func len*(self: MoveList): int {.inline.} = self.len
|
|
|
|
|
2024-04-16 15:24:31 +02:00
|
|
|
|
2024-04-16 23:45:32 +02:00
|
|
|
# A bunch of move creation utilities
|
2024-04-16 15:24:31 +02:00
|
|
|
|
2024-04-16 23:45:32 +02:00
|
|
|
func createMove*(startSquare, targetSquare: Square, flags: varargs[MoveFlag]): Move =
|
2024-04-16 15:24:31 +02:00
|
|
|
result = Move(startSquare: startSquare, targetSquare: targetSquare, flags: Default.uint16)
|
|
|
|
for flag in flags:
|
|
|
|
result.flags = result.flags or flag.uint16
|
|
|
|
|
2024-04-16 23:45:32 +02:00
|
|
|
proc createMove*(startSquare, targetSquare: string, flags: varargs[MoveFlag]): Move =
|
|
|
|
result = createMove(startSquare.toSquare(), targetSquare.toSquare(), flags)
|
|
|
|
|
|
|
|
func createMove*(startSquare, targetSquare: SomeInteger, flags: varargs[MoveFlag]): Move =
|
|
|
|
result = createMove(Square(startSquare.int8), Square(targetSquare.int8), flags)
|
|
|
|
|
|
|
|
|
|
|
|
func createMove*(startSquare: Square, targetSquare: SomeInteger, flags: varargs[MoveFlag]): Move =
|
|
|
|
result = createMove(startSquare, Square(targetSquare.int8), flags)
|
|
|
|
|
2024-04-16 15:24:31 +02:00
|
|
|
|
2024-04-20 14:51:50 +02:00
|
|
|
func nullMove*: Move {.inline.} = createMove(nullSquare(), nullSquare())
|
|
|
|
|
|
|
|
func isPromotion*(move: Move): bool {.inline.} =
|
|
|
|
## Returns whether the given move is a
|
|
|
|
## pawn promotion
|
|
|
|
for promotion in [PromoteToBishop, PromoteToKnight, PromoteToRook, PromoteToQueen]:
|
|
|
|
if (move.flags and promotion.uint16) != 0:
|
|
|
|
return true
|
|
|
|
|
|
|
|
|
|
|
|
func getPromotionType*(move: Move): MoveFlag {.inline.} =
|
|
|
|
## Returns the promotion type of the given move.
|
|
|
|
## The return value of this function is only valid
|
|
|
|
## if isPromotion() returns true
|
|
|
|
for promotion in [PromoteToBishop, PromoteToKnight, PromoteToRook, PromoteToQueen]:
|
|
|
|
if (move.flags and promotion.uint16) != 0:
|
|
|
|
return promotion
|
|
|
|
|
|
|
|
|
|
|
|
func isCapture*(move: Move): bool {.inline.} =
|
|
|
|
## Returns whether the given move is a
|
|
|
|
## cature
|
|
|
|
result = (move.flags and Capture.uint16) == Capture.uint16
|
|
|
|
|
|
|
|
|
|
|
|
func isCastling*(move: Move): bool {.inline.} =
|
|
|
|
## Returns whether the given move is a
|
|
|
|
## castle
|
|
|
|
for flag in [CastleLong, CastleShort]:
|
|
|
|
if (move.flags and flag.uint16) != 0:
|
|
|
|
return true
|
|
|
|
|
|
|
|
|
|
|
|
func getCastlingType*(move: Move): MoveFlag {.inline.} =
|
|
|
|
## Returns the castlingRights type of the given move.
|
|
|
|
## The return value of this function is only valid
|
|
|
|
## if isCastling() returns true
|
|
|
|
for flag in [CastleLong, CastleShort]:
|
|
|
|
if (move.flags and flag.uint16) != 0:
|
|
|
|
return flag
|
|
|
|
|
|
|
|
|
|
|
|
func isEnPassant*(move: Move): bool {.inline.} =
|
|
|
|
## Returns whether the given move is an
|
|
|
|
## en passant capture
|
|
|
|
result = (move.flags and EnPassant.uint16) != 0
|
|
|
|
|
|
|
|
|
|
|
|
func isDoublePush*(move: Move): bool {.inline.} =
|
|
|
|
## Returns whether the given move is a
|
|
|
|
## double pawn push
|
|
|
|
result = (move.flags and DoublePush.uint16) != 0
|
|
|
|
|
|
|
|
|
|
|
|
func getFlags*(move: Move): seq[MoveFlag] =
|
|
|
|
## Gets all the flags of this move
|
|
|
|
for flag in [EnPassant, Capture, DoublePush, CastleLong, CastleShort,
|
|
|
|
PromoteToBishop, PromoteToKnight, PromoteToQueen,
|
|
|
|
PromoteToRook]:
|
|
|
|
if (move.flags and flag.uint16) == flag.uint16:
|
|
|
|
result.add(flag)
|
|
|
|
if result.len() == 0:
|
|
|
|
result.add(Default)
|