CPG/Chess/nimfish/nimfishpkg/moves.nim

165 lines
5.1 KiB
Nim

# Copyright 2024 Mattia Giambirtone & All Contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
## 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
MoveList* = object
## A list of moves
data: array[218, Move]
len: int8
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]
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)
func clear*(self: var MoveList) {.inline.} =
self.len = 0
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
# A bunch of move creation utilities
func createMove*(startSquare, targetSquare: Square, flags: varargs[MoveFlag]): Move =
result = Move(startSquare: startSquare, targetSquare: targetSquare, flags: Default.uint16)
for flag in flags:
result.flags = result.flags or flag.uint16
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)
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)