Switch to a legal move generator
This commit is contained in:
parent
f9744c077b
commit
b0ebdc02a6
|
@ -122,7 +122,6 @@ func emptyPiece*: Piece {.inline.} = Piece(kind: Empty, color: None)
|
||||||
func emptyLocation*: Location {.inline.} = (-1 , -1)
|
func emptyLocation*: Location {.inline.} = (-1 , -1)
|
||||||
func opposite*(c: PieceColor): PieceColor {.inline.} = (if c == White: Black else: White)
|
func opposite*(c: PieceColor): PieceColor {.inline.} = (if c == White: Black else: White)
|
||||||
proc algebraicToLocation*(s: string): Location {.inline.}
|
proc algebraicToLocation*(s: string): Location {.inline.}
|
||||||
func getCapture*(self: ChessBoard, move: Move): Location
|
|
||||||
proc makeMove*(self: ChessBoard, startSquare, targetSquare: string): Move {.discardable.}
|
proc makeMove*(self: ChessBoard, startSquare, targetSquare: string): Move {.discardable.}
|
||||||
proc makeMove*(self: ChessBoard, move: Move): Move {.discardable.}
|
proc makeMove*(self: ChessBoard, move: Move): Move {.discardable.}
|
||||||
proc makeMove*(self: ChessBoard, startSquare, targetSquare: Location): Move {.discardable.}
|
proc makeMove*(self: ChessBoard, startSquare, targetSquare: Location): Move {.discardable.}
|
||||||
|
@ -130,11 +129,10 @@ func emptyMove*: Move {.inline.} = Move(startSquare: emptyLocation(), targetSqua
|
||||||
func `+`*(a, b: Location): Location = (a.row + b.row, a.col + b.col)
|
func `+`*(a, b: Location): Location = (a.row + b.row, a.col + b.col)
|
||||||
func isValid*(a: Location): bool {.inline.} = a.row in 0..7 and a.col in 0..7
|
func isValid*(a: Location): bool {.inline.} = a.row in 0..7 and a.col in 0..7
|
||||||
proc generateMoves(self: ChessBoard, location: Location): seq[Move]
|
proc generateMoves(self: ChessBoard, location: Location): seq[Move]
|
||||||
proc isAttacked*(self: ChessBoard, loc: Location): bool
|
proc isAttacked*(self: ChessBoard, loc: Location, color: PieceColor = None): bool
|
||||||
proc undoMove*(self: ChessBoard, move: Move)
|
proc undoMove*(self: ChessBoard, move: Move)
|
||||||
proc isLegal(self: ChessBoard, move: Move, keep: bool = false): bool
|
proc isLegal(self: ChessBoard, move: Move): bool {.inline.}
|
||||||
proc doMove(self: ChessBoard, move: Move)
|
proc doMove(self: ChessBoard, move: Move)
|
||||||
proc isLegalFast(self: ChessBoard, move: Move, keep: bool = false): bool
|
|
||||||
proc pretty*(self: ChessBoard): string
|
proc pretty*(self: ChessBoard): string
|
||||||
proc spawnPiece(self: ChessBoard, location: Location, piece: Piece)
|
proc spawnPiece(self: ChessBoard, location: Location, piece: Piece)
|
||||||
proc updateAttackedSquares(self: ChessBoard)
|
proc updateAttackedSquares(self: ChessBoard)
|
||||||
|
@ -535,18 +533,16 @@ func getPiece*(self: ChessBoard, square: string): Piece =
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
func isCapture*(self: ChessBoard, move: Move): bool {.inline.} =
|
func isCapture*(move: Move): bool {.inline.} =
|
||||||
## Returns whether the given move is a capture
|
## Returns whether the given move is a capture
|
||||||
## or not
|
## or not
|
||||||
return move.flag in [Capture, EnPassant]
|
return move.flag in [Capture, EnPassant]
|
||||||
|
|
||||||
|
|
||||||
func getCapture*(self: ChessBoard, move: Move): Location =
|
func isPromotion*(move: Move): bool {.inline.} =
|
||||||
## Returns the location that would be captured if this
|
## Returns whrther the given move is a
|
||||||
## move were played on the board
|
## pawn promotion or not
|
||||||
if not self.isCapture(move):
|
return move.flag in [PromoteToBishop, PromoteToKnight, PromoteToRook, PromoteToQueen]
|
||||||
return emptyLocation()
|
|
||||||
return move.targetSquare
|
|
||||||
|
|
||||||
|
|
||||||
proc inCheck*(self: ChessBoard, color: PieceColor = None): bool =
|
proc inCheck*(self: ChessBoard, color: PieceColor = None): bool =
|
||||||
|
@ -559,9 +555,9 @@ proc inCheck*(self: ChessBoard, color: PieceColor = None): bool =
|
||||||
color = self.getActiveColor()
|
color = self.getActiveColor()
|
||||||
case color:
|
case color:
|
||||||
of White:
|
of White:
|
||||||
return self.isAttacked(self.position.pieces.white.king)
|
return self.isAttacked(self.position.pieces.white.king, color)
|
||||||
of Black:
|
of Black:
|
||||||
return self.isAttacked(self.position.pieces.black.king)
|
return self.isAttacked(self.position.pieces.black.king, color)
|
||||||
else:
|
else:
|
||||||
# Unreachable
|
# Unreachable
|
||||||
discard
|
discard
|
||||||
|
@ -586,65 +582,67 @@ proc canCastle*(self: ChessBoard, color: PieceColor = None): tuple[queen, king:
|
||||||
of None:
|
of None:
|
||||||
# Unreachable
|
# Unreachable
|
||||||
discard
|
discard
|
||||||
var
|
if result.king or result.queen:
|
||||||
loc: Location
|
var
|
||||||
queenSide: Location
|
loc: Location
|
||||||
kingSide: Location
|
queenSide: Location
|
||||||
# If the path between the king and a rook is blocked, then castling
|
kingSide: Location
|
||||||
# is prohibited on that side
|
if self.inCheck(color):
|
||||||
case color:
|
# King can not castle out of check
|
||||||
of White:
|
return (false, false)
|
||||||
loc = self.position.pieces.white.king
|
# If the path between the king and rook on a given side is blocked or any of the
|
||||||
queenSide = color.leftSide()
|
# squares where the king would travel to are attacked by the opponent,
|
||||||
kingSide = color.rightSide()
|
# then castling is (temporarily) prohibited on that side
|
||||||
of Black:
|
case color:
|
||||||
loc = self.position.pieces.black.king
|
of White:
|
||||||
queenSide = color.rightSide()
|
loc = self.position.pieces.white.king
|
||||||
kingSide = color.leftSide()
|
queenSide = color.leftSide()
|
||||||
of None:
|
kingSide = color.rightSide()
|
||||||
# Unreachable
|
of Black:
|
||||||
discard
|
loc = self.position.pieces.black.king
|
||||||
|
queenSide = color.rightSide()
|
||||||
|
kingSide = color.leftSide()
|
||||||
|
of None:
|
||||||
|
# Unreachable
|
||||||
|
discard
|
||||||
|
|
||||||
if result.king:
|
if result.king:
|
||||||
# Short castle
|
# Short castle
|
||||||
var location = loc
|
var location = loc
|
||||||
while true:
|
while true:
|
||||||
location = location + kingSide
|
location = location + kingSide
|
||||||
if not location.isValid():
|
if not location.isValid():
|
||||||
break
|
break
|
||||||
if self.grid[location.row, location.col].kind == Empty:
|
if self.grid[location.row, location.col].color == color:
|
||||||
continue
|
# Blocked by own piece
|
||||||
if location == color.kingSideRook() and self.grid[location.row, location.col].kind == Rook:
|
result.king = false
|
||||||
break
|
break
|
||||||
# Blocked by a piece
|
# Square is attacked or blocked by enemy piece
|
||||||
result.king = false
|
if self.isAttacked(location, color) or self.grid[location.row, location.col].color != None:
|
||||||
break
|
result.king = false
|
||||||
|
break
|
||||||
if result.queen:
|
# Square is occupied by our rook: we're done. No need to check the color or type of it (because
|
||||||
# Long castle
|
# if it weren't the right color, castling rights would've already been lost and we wouldn't
|
||||||
var location = loc
|
# have got this far)
|
||||||
while true:
|
if location == color.kingSideRook():
|
||||||
location = location + queenSide
|
break
|
||||||
if not location.isValid():
|
# Square is empty and not attacked. Keep going
|
||||||
break
|
|
||||||
if self.grid[location.row, location.col].kind == Empty:
|
|
||||||
continue
|
|
||||||
if location == color.queenSideRook() and self.grid[location.row, location.col].kind == Rook:
|
|
||||||
break
|
|
||||||
# Blocked by a piece
|
|
||||||
result.queen = false
|
|
||||||
break
|
|
||||||
|
|
||||||
# If the castling king would walk into, through or out of check
|
|
||||||
# while castling on a given side, then it is not possible to castle
|
|
||||||
# on that side until the threat exists
|
|
||||||
|
|
||||||
if (result.king or result.queen) and self.inCheck(color):
|
if result.queen:
|
||||||
# Only check for checks if castling is still available
|
# Long castle
|
||||||
# by this point (if we can avoid calls to generateMoves,
|
var location = loc
|
||||||
# we should)
|
while true:
|
||||||
return
|
location = location + queenSide
|
||||||
# TODO: Check for attacks in the various other squares
|
if not location.isValid():
|
||||||
|
break
|
||||||
|
if self.grid[location.row, location.col].color == color:
|
||||||
|
result.queen = false
|
||||||
|
break
|
||||||
|
if self.isAttacked(location, color) or self.grid[location.row, location.col].color != None:
|
||||||
|
result.queen = false
|
||||||
|
break
|
||||||
|
if location == color.queenSideRook():
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
proc generatePawnMoves(self: ChessBoard, location: Location): seq[Move] =
|
proc generatePawnMoves(self: ChessBoard, location: Location): seq[Move] =
|
||||||
|
@ -656,48 +654,46 @@ proc generatePawnMoves(self: ChessBoard, location: Location): seq[Move] =
|
||||||
flags: seq[MoveFlag] = @[]
|
flags: seq[MoveFlag] = @[]
|
||||||
doAssert piece.kind == Pawn, &"generatePawnMoves called on a {piece.kind}"
|
doAssert piece.kind == Pawn, &"generatePawnMoves called on a {piece.kind}"
|
||||||
# Pawns can move forward one square
|
# Pawns can move forward one square
|
||||||
let forwardOffset = piece.color.forward()
|
let forward = (piece.color.forward() + location)
|
||||||
let forward = (forwardOffset + location)
|
|
||||||
# Only if the square is empty though
|
# Only if the square is empty though
|
||||||
if forward.isValid() and self.grid[forward.row, forward.col].color == None:
|
if forward.isValid() and self.grid[forward.row, forward.col].color == None:
|
||||||
locations.add(forwardOffset)
|
locations.add(forward)
|
||||||
flags.add(Default)
|
flags.add(Default)
|
||||||
# If the pawn is on its first rank, it can push two squares
|
# If the pawn is on its first rank, it can push two squares
|
||||||
if location.row == piece.getStartRow():
|
if location.row == piece.getStartRow():
|
||||||
let doubleOffset = piece.color.doublePush()
|
let double = location + piece.color.doublePush()
|
||||||
let double = location + doubleOffset
|
# Check that both squares are empty
|
||||||
# Check if both squares are available
|
|
||||||
if double.isValid() and self.grid[forward.row, forward.col].color == None and self.grid[double.row, double.col].color == None:
|
if double.isValid() and self.grid[forward.row, forward.col].color == None and self.grid[double.row, double.col].color == None:
|
||||||
locations.add(piece.color.doublePush())
|
locations.add(double)
|
||||||
flags.add(DoublePush)
|
flags.add(DoublePush)
|
||||||
|
# If the target is the en passant square and the piece under en passant
|
||||||
|
# is of the opposite color of the pawn being moved, then we check if we
|
||||||
|
# can do an en passant capture
|
||||||
if self.position.enPassantSquare.piece.color == piece.color.opposite:
|
if self.position.enPassantSquare.piece.color == piece.color.opposite:
|
||||||
if abs(self.position.enPassantSquare.targetSquare.col - location.col) == 1 and abs(self.position.enPassantSquare.targetSquare.row - location.row) == 1:
|
if abs(self.position.enPassantSquare.targetSquare.col - location.col) == 1 and abs(self.position.enPassantSquare.targetSquare.row - location.row) == 1:
|
||||||
# Only viable if the piece is on the diagonal of the target
|
# Only viable if the piece is on the front diagonal of the target
|
||||||
locations.add(self.position.enPassantSquare.targetSquare)
|
locations.add(self.position.enPassantSquare.targetSquare)
|
||||||
flags.add(EnPassant)
|
flags.add(EnPassant)
|
||||||
# They can also move on either diagonal one
|
# They can also move on either diagonal one
|
||||||
# square, but only to capture
|
# square, but only to capture
|
||||||
var diagonal = piece.color.topRightDiagonal()
|
var diagonal = location + piece.color.topRightDiagonal()
|
||||||
if (diagonal + location).isValid() and self.isCapture(Move(piece: piece, startSquare: location, targetSquare: location + diagonal)):
|
if diagonal.isValid() and self.grid[diagonal.row, diagonal.col].color == piece.color.opposite() and self.grid[diagonal.row, diagonal.col].kind != King:
|
||||||
locations.add(diagonal)
|
locations.add(diagonal)
|
||||||
flags.add(Capture)
|
flags.add(Capture)
|
||||||
diagonal = piece.color.topLeftDiagonal()
|
diagonal = location + piece.color.topLeftDiagonal()
|
||||||
if (diagonal + location).isValid() and self.isCapture(Move(piece: piece, startSquare: location, targetSquare: location + diagonal)):
|
if diagonal.isValid() and self.grid[diagonal.row, diagonal.col].color == piece.color.opposite() and self.grid[diagonal.row, diagonal.col].kind != King:
|
||||||
locations.add(diagonal)
|
locations.add(diagonal)
|
||||||
flags.add(Capture)
|
flags.add(Capture)
|
||||||
|
|
||||||
var
|
var targetPiece: Piece
|
||||||
newLocation: Location
|
|
||||||
targetPiece: Piece
|
|
||||||
for (target, flag) in zip(locations, flags):
|
for (target, flag) in zip(locations, flags):
|
||||||
newLocation = location + target
|
targetPiece = self.grid[target.row, target.col]
|
||||||
targetPiece = self.grid[newLocation.row, newLocation.col]
|
if target.row == piece.color.getLastRow():
|
||||||
if newLocation.row == piece.color.getLastRow():
|
|
||||||
# Pawn reached the other side of the board: generate all potential piece promotions
|
# Pawn reached the other side of the board: generate all potential piece promotions
|
||||||
for promotionType in [PromoteToKnight, PromoteToBishop, PromoteToRook, PromoteToQueen]:
|
for promotionType in [PromoteToKnight, PromoteToBishop, PromoteToRook, PromoteToQueen]:
|
||||||
result.add(Move(startSquare: location, targetSquare: newLocation, piece: piece, flag: promotionType))
|
result.add(Move(startSquare: location, targetSquare: target, piece: piece, flag: promotionType))
|
||||||
continue
|
continue
|
||||||
result.add(Move(startSquare: location, targetSquare: newLocation, piece: piece, flag: flag))
|
result.add(Move(startSquare: location, targetSquare: target, piece: piece, flag: flag))
|
||||||
|
|
||||||
|
|
||||||
proc generateSlidingMoves(self: ChessBoard, location: Location): seq[Move] =
|
proc generateSlidingMoves(self: ChessBoard, location: Location): seq[Move] =
|
||||||
|
@ -734,7 +730,9 @@ proc generateSlidingMoves(self: ChessBoard, location: Location): seq[Move] =
|
||||||
if otherPiece.color == piece.color.opposite:
|
if otherPiece.color == piece.color.opposite:
|
||||||
# Target square contains an enemy piece: capture
|
# Target square contains an enemy piece: capture
|
||||||
# it and stop going any further
|
# it and stop going any further
|
||||||
result.add(Move(startSquare: location, targetSquare: square, piece: piece, flag: Capture))
|
if otherPiece.kind != King:
|
||||||
|
# Can't capture the king
|
||||||
|
result.add(Move(startSquare: location, targetSquare: square, piece: piece, flag: Capture))
|
||||||
break
|
break
|
||||||
# Target square is empty
|
# Target square is empty
|
||||||
result.add(Move(startSquare: location, targetSquare: square, piece: piece))
|
result.add(Move(startSquare: location, targetSquare: square, piece: piece))
|
||||||
|
@ -754,30 +752,30 @@ proc generateKingMoves(self: ChessBoard, location: Location): seq[Move] =
|
||||||
piece.color.leftSide(),
|
piece.color.leftSide(),
|
||||||
piece.color.rightSide()]
|
piece.color.rightSide()]
|
||||||
# Castling
|
# Castling
|
||||||
let canCastle = self.canCastle()
|
let canCastle = self.canCastle(piece.color)
|
||||||
if canCastle.queen:
|
if canCastle.queen:
|
||||||
directions.add(longCastleKing())
|
directions.add(longCastleKing())
|
||||||
if canCastle.king:
|
if canCastle.king:
|
||||||
directions.add(shortCastleKing())
|
directions.add(shortCastleKing())
|
||||||
var flag = Default
|
var flag = Default
|
||||||
for direction in directions:
|
for direction in directions:
|
||||||
|
# Step in this direction once
|
||||||
|
let square: Location = location + direction
|
||||||
|
# End of board reached
|
||||||
|
if not square.isValid():
|
||||||
|
continue
|
||||||
if direction == longCastleKing():
|
if direction == longCastleKing():
|
||||||
flag = CastleLong
|
flag = CastleLong
|
||||||
elif direction == shortCastleKing():
|
elif direction == shortCastleKing():
|
||||||
flag = CastleShort
|
flag = CastleShort
|
||||||
else:
|
else:
|
||||||
flag = Default
|
flag = Default
|
||||||
# Step in this direction once
|
|
||||||
let square: Location = location + direction
|
|
||||||
# End of board reached
|
|
||||||
if not square.isValid():
|
|
||||||
continue
|
|
||||||
let otherPiece = self.grid[square.row, square.col]
|
let otherPiece = self.grid[square.row, square.col]
|
||||||
if otherPiece.color == self.getActiveColor.opposite():
|
if otherPiece.color == self.getActiveColor.opposite():
|
||||||
flag = Capture
|
flag = Capture
|
||||||
# A friendly piece is in the way, move onto the next
|
# A friendly piece or the opponent king is in the way,
|
||||||
# direction
|
# move onto the next direction
|
||||||
if otherPiece.color == piece.color:
|
if otherPiece.color == piece.color or otherPiece.kind == King:
|
||||||
continue
|
continue
|
||||||
# Target square is empty or contains an enemy piece:
|
# Target square is empty or contains an enemy piece:
|
||||||
# All good for us!
|
# All good for us!
|
||||||
|
@ -804,20 +802,20 @@ proc generateKnightMoves(self: ChessBoard, location: Location): seq[Move] =
|
||||||
if not square.isValid():
|
if not square.isValid():
|
||||||
continue
|
continue
|
||||||
let otherPiece = self.grid[square.row, square.col]
|
let otherPiece = self.grid[square.row, square.col]
|
||||||
# A friendly piece is in the way
|
# A friendly piece or the opponent king is is in the way
|
||||||
if otherPiece.color == piece.color:
|
if otherPiece.color == piece.color or otherPiece.kind == King:
|
||||||
continue
|
continue
|
||||||
if otherPiece.color == piece.color.opposite:
|
if otherPiece.color != None:
|
||||||
# Target square contains an enemy piece: capture
|
# Target square contains an enemy piece: capture
|
||||||
# it
|
# it
|
||||||
result.add(Move(startSquare: location, targetSquare: square, piece: piece, flag: Capture))
|
result.add(Move(startSquare: location, targetSquare: square, piece: piece, flag: Capture))
|
||||||
continue
|
else:
|
||||||
# Target square is empty
|
# Target square is empty
|
||||||
result.add(Move(startSquare: location, targetSquare: square, piece: piece))
|
result.add(Move(startSquare: location, targetSquare: square, piece: piece))
|
||||||
|
|
||||||
|
|
||||||
proc generateMoves(self: ChessBoard, location: Location): seq[Move] =
|
proc generateMoves(self: ChessBoard, location: Location): seq[Move] =
|
||||||
## Returns the list of possible pseudo-legal chess moves for the
|
## Returns the list of possible legal chess moves for the
|
||||||
## piece in the given location
|
## piece in the given location
|
||||||
let piece = self.grid[location.row, location.col]
|
let piece = self.grid[location.row, location.col]
|
||||||
case piece.kind:
|
case piece.kind:
|
||||||
|
@ -834,7 +832,7 @@ proc generateMoves(self: ChessBoard, location: Location): seq[Move] =
|
||||||
|
|
||||||
|
|
||||||
proc generateAllMoves*(self: ChessBoard): seq[Move] =
|
proc generateAllMoves*(self: ChessBoard): seq[Move] =
|
||||||
## Returns the list of all possible pseudo-legal moves
|
## Returns the list of all possible legal moves
|
||||||
## in the current position
|
## in the current position
|
||||||
for i, row in self.grid:
|
for i, row in self.grid:
|
||||||
for j, piece in row:
|
for j, piece in row:
|
||||||
|
@ -843,23 +841,36 @@ proc generateAllMoves*(self: ChessBoard): seq[Move] =
|
||||||
result.add(move)
|
result.add(move)
|
||||||
|
|
||||||
|
|
||||||
proc getAttackers*(self: ChessBoard, square: Location): seq[Location] =
|
proc getAttackers*(self: ChessBoard, square: Location, color = None): seq[Location] =
|
||||||
## Returns all the attackers of the given square
|
## Returns all the attackers of the given square
|
||||||
for attack in self.position.attacked.black:
|
## for the given color (a color of None means use
|
||||||
if attack.dest == square:
|
## the currently active color)
|
||||||
result.add(attack.source)
|
var color = color
|
||||||
for attack in self.position.attacked.white:
|
if color == None:
|
||||||
if attack.dest == square:
|
color = self.getActiveColor()
|
||||||
result.add(attack.source)
|
case color:
|
||||||
|
of White:
|
||||||
|
for attack in self.position.attacked.black:
|
||||||
|
if attack.dest == square:
|
||||||
|
result.add(attack.source)
|
||||||
|
of Black:
|
||||||
|
for attack in self.position.attacked.white:
|
||||||
|
if attack.dest == square:
|
||||||
|
result.add(attack.source)
|
||||||
|
else:
|
||||||
|
# Unreachable
|
||||||
|
discard
|
||||||
|
|
||||||
# We don't use getAttackers because this one only cares about whether
|
# We don't use getAttackers because this one only cares about whether
|
||||||
# the square is attacked or not (and can therefore exit earlier than
|
# the square is attacked or not (and can therefore exit earlier than
|
||||||
# getAttackers)
|
# getAttackers)
|
||||||
proc isAttacked*(self: ChessBoard, loc: Location): bool =
|
proc isAttacked*(self: ChessBoard, loc: Location, color: PieceColor = None): bool =
|
||||||
## Returns whether the given location is attacked
|
## Returns whether the given location is attacked
|
||||||
## by the current opponent
|
## by the given opponent
|
||||||
let piece = self.grid[loc.row, loc.col]
|
var color = color
|
||||||
case piece.color:
|
if color == None:
|
||||||
|
color = self.getActiveColor()
|
||||||
|
case color:
|
||||||
of White:
|
of White:
|
||||||
for attack in self.position.attacked.black:
|
for attack in self.position.attacked.black:
|
||||||
if attack.dest == loc:
|
if attack.dest == loc:
|
||||||
|
@ -869,17 +880,7 @@ proc isAttacked*(self: ChessBoard, loc: Location): bool =
|
||||||
if attack.dest == loc:
|
if attack.dest == loc:
|
||||||
return true
|
return true
|
||||||
of None:
|
of None:
|
||||||
case self.getActiveColor():
|
discard
|
||||||
of White:
|
|
||||||
for attack in self.position.attacked.black:
|
|
||||||
if attack.dest == loc:
|
|
||||||
return true
|
|
||||||
of Black:
|
|
||||||
for attack in self.position.attacked.black:
|
|
||||||
if attack.dest == loc:
|
|
||||||
return true
|
|
||||||
else:
|
|
||||||
discard
|
|
||||||
|
|
||||||
|
|
||||||
proc isAttacked*(self: ChessBoard, square: string): bool =
|
proc isAttacked*(self: ChessBoard, square: string): bool =
|
||||||
|
@ -888,20 +889,66 @@ proc isAttacked*(self: ChessBoard, square: string): bool =
|
||||||
return self.isAttacked(square.algebraicToLocation())
|
return self.isAttacked(square.algebraicToLocation())
|
||||||
|
|
||||||
|
|
||||||
|
func addAttack(self: ChessBoard, attack: tuple[source, dest: Location], color: PieceColor) {.inline.} =
|
||||||
|
if attack.source.isValid() and attack.dest.isValid():
|
||||||
|
case color:
|
||||||
|
of White:
|
||||||
|
self.position.attacked.white.add(attack)
|
||||||
|
of Black:
|
||||||
|
self.position.attacked.black.add(attack)
|
||||||
|
else:
|
||||||
|
discard
|
||||||
|
|
||||||
|
|
||||||
proc updatePawnAttacks(self: ChessBoard) =
|
proc updatePawnAttacks(self: ChessBoard) =
|
||||||
## Internal helper of updateAttackedSquares
|
## Internal helper of updateAttackedSquares
|
||||||
for loc in self.position.pieces.white.pawns:
|
for loc in self.position.pieces.white.pawns:
|
||||||
# Pawns are special in how they capture (i.e. the
|
# Pawns are special in how they capture (i.e. the
|
||||||
# squares they can move to do not match the squares
|
# squares they can move to do not match the squares
|
||||||
# they can capture on. Sneaky fucks)
|
# they can capture on. Sneaky fucks)
|
||||||
let piece = self.grid[loc.row, loc.col]
|
self.addAttack((loc, loc + White.topRightDiagonal()), White)
|
||||||
self.position.attacked.white.add((loc, loc + piece.color.topRightDiagonal()))
|
self.addAttack((loc, loc + White.topRightDiagonal()), White)
|
||||||
self.position.attacked.white.add((loc, loc + piece.color.topLeftDiagonal()))
|
|
||||||
# We do the same thing for black
|
# We do the same thing for black
|
||||||
for loc in self.position.pieces.black.pawns:
|
for loc in self.position.pieces.black.pawns:
|
||||||
let piece = self.grid[loc.row, loc.col]
|
self.addAttack((loc, loc + Black.topRightDiagonal()), Black)
|
||||||
self.position.attacked.black.add((loc, loc + piece.color.topRightDiagonal()))
|
self.addAttack((loc, loc + Black.topRightDiagonal()), Black)
|
||||||
self.position.attacked.black.add((loc, loc + piece.color.topLeftDiagonal()))
|
|
||||||
|
|
||||||
|
proc updateKingAttacks(self: ChessBoard) =
|
||||||
|
## Internal helper of updateAttackedSquares
|
||||||
|
var king = self.position.pieces.white.king
|
||||||
|
self.addAttack((king, king + White.topRightDiagonal()), White)
|
||||||
|
self.addAttack((king, king + White.topLeftDiagonal()), White)
|
||||||
|
self.addAttack((king, king + White.bottomLeftDiagonal()), White)
|
||||||
|
self.addAttack((king, king + White.bottomRightDiagonal()), White)
|
||||||
|
king = self.position.pieces.black.king
|
||||||
|
self.addAttack((king, king + Black.topRightDiagonal()), Black)
|
||||||
|
self.addAttack((king, king + Black.topLeftDiagonal()), Black)
|
||||||
|
self.addAttack((king, king + Black.bottomLeftDiagonal()), Black)
|
||||||
|
self.addAttack((king, king + Black.bottomRightDiagonal()), Black)
|
||||||
|
|
||||||
|
|
||||||
|
proc updateKnightAttacks(self: ChessBoard) =
|
||||||
|
## Internal helper of updateAttackedSquares
|
||||||
|
for loc in self.position.pieces.white.knights:
|
||||||
|
self.addAttack((loc, loc + White.topLeftKnightMove()), White)
|
||||||
|
self.addAttack((loc, loc + White.topRightKnightMove()), White)
|
||||||
|
self.addAttack((loc, loc + White.bottomLeftKnightMove()), White)
|
||||||
|
self.addAttack((loc, loc + White.bottomRightKnightMove()), White)
|
||||||
|
self.addAttack((loc, loc + White.topLeftKnightMove(long=false)), White)
|
||||||
|
self.addAttack((loc, loc + White.topRightKnightMove(long=false)), White)
|
||||||
|
self.addAttack((loc, loc + White.bottomLeftKnightMove(long=false)), White)
|
||||||
|
self.addAttack((loc, loc + White.bottomRightKnightMove(long=false)), White)
|
||||||
|
|
||||||
|
for loc in self.position.pieces.black.knights:
|
||||||
|
self.addAttack((loc, loc + Black.topLeftKnightMove()), Black)
|
||||||
|
self.addAttack((loc, loc + Black.topRightKnightMove()), Black)
|
||||||
|
self.addAttack((loc, loc + Black.bottomLeftKnightMove()), Black)
|
||||||
|
self.addAttack((loc, loc + Black.bottomRightKnightMove()), Black)
|
||||||
|
self.addAttack((loc, loc + Black.topLeftKnightMove(long=false)), Black)
|
||||||
|
self.addAttack((loc, loc + Black.topRightKnightMove(long=false)), Black)
|
||||||
|
self.addAttack((loc, loc + Black.bottomLeftKnightMove(long=false)), Black)
|
||||||
|
self.addAttack((loc, loc + Black.bottomRightKnightMove(long=false)), Black)
|
||||||
|
|
||||||
|
|
||||||
proc getSlidingAttacks(self: ChessBoard, loc: Location): Attacked =
|
proc getSlidingAttacks(self: ChessBoard, loc: Location): Attacked =
|
||||||
|
@ -947,24 +994,24 @@ proc updateSlidingAttacks(self: ChessBoard) =
|
||||||
# Bishops
|
# Bishops
|
||||||
for loc in self.position.pieces.white.bishops:
|
for loc in self.position.pieces.white.bishops:
|
||||||
for attack in self.getSlidingAttacks(loc):
|
for attack in self.getSlidingAttacks(loc):
|
||||||
self.position.attacked.white.add(attack)
|
self.addAttack(attack, White)
|
||||||
for loc in self.position.pieces.black.bishops:
|
for loc in self.position.pieces.black.bishops:
|
||||||
for attack in self.getSlidingAttacks(loc):
|
for attack in self.getSlidingAttacks(loc):
|
||||||
self.position.attacked.black.add(attack)
|
self.addAttack(attack, Black)
|
||||||
# Rooks
|
# Rooks
|
||||||
for loc in self.position.pieces.white.rooks:
|
for loc in self.position.pieces.white.rooks:
|
||||||
for attack in self.getSlidingAttacks(loc):
|
for attack in self.getSlidingAttacks(loc):
|
||||||
self.position.attacked.white.add(attack)
|
self.addAttack(attack, White)
|
||||||
for loc in self.position.pieces.black.rooks:
|
for loc in self.position.pieces.black.rooks:
|
||||||
for attack in self.getSlidingAttacks(loc):
|
for attack in self.getSlidingAttacks(loc):
|
||||||
self.position.attacked.black.add(attack)
|
self.addAttack(attack, Black)
|
||||||
# Queens
|
# Queens
|
||||||
for loc in self.position.pieces.white.queens:
|
for loc in self.position.pieces.white.queens:
|
||||||
for attack in self.getSlidingAttacks(loc):
|
for attack in self.getSlidingAttacks(loc):
|
||||||
self.position.attacked.white.add(attack)
|
self.addAttack(attack, White)
|
||||||
for loc in self.position.pieces.black.queens:
|
for loc in self.position.pieces.black.queens:
|
||||||
for attack in self.getSlidingAttacks(loc):
|
for attack in self.getSlidingAttacks(loc):
|
||||||
self.position.attacked.black.add(attack)
|
self.addAttack(attack, Black)
|
||||||
|
|
||||||
|
|
||||||
proc updateAttackedSquares(self: ChessBoard) =
|
proc updateAttackedSquares(self: ChessBoard) =
|
||||||
|
@ -977,28 +1024,16 @@ proc updateAttackedSquares(self: ChessBoard) =
|
||||||
self.updatePawnAttacks()
|
self.updatePawnAttacks()
|
||||||
# Sliding pieces
|
# Sliding pieces
|
||||||
self.updateSlidingAttacks()
|
self.updateSlidingAttacks()
|
||||||
# Knights
|
self.updateKnightAttacks()
|
||||||
for loc in self.position.pieces.white.knights:
|
# Kings
|
||||||
for move in self.generateMoves(loc):
|
self.updateKingAttacks()
|
||||||
self.position.attacked.white.add((move.startSquare, move.targetSquare))
|
|
||||||
# King
|
|
||||||
for move in self.generateMoves(self.position.pieces.white.king):
|
|
||||||
self.position.attacked.white.add((move.startSquare, move.targetSquare))
|
|
||||||
# Knights
|
|
||||||
for loc in self.position.pieces.black.knights:
|
|
||||||
for move in self.generateMoves(loc):
|
|
||||||
self.position.attacked.black.add((move.startSquare, move.targetSquare))
|
|
||||||
# King
|
|
||||||
for move in self.generateMoves(self.position.pieces.black.king):
|
|
||||||
self.position.attacked.black.add((move.startSquare, move.targetSquare))
|
|
||||||
|
|
||||||
|
|
||||||
proc removePiece(self: ChessBoard, location: Location, emptyGrid: bool = true) =
|
proc removePiece(self: ChessBoard, location: Location, attack: bool = true) =
|
||||||
## Removes a piece from the board, updating necessary
|
## Removes a piece from the board, updating necessary
|
||||||
## metadata
|
## metadata
|
||||||
var piece = self.grid[location.row, location.col]
|
var piece = self.grid[location.row, location.col]
|
||||||
if emptyGrid:
|
self.grid[location.row, location.col] = emptyPiece()
|
||||||
self.grid[location.row, location.col] = emptyPiece()
|
|
||||||
case piece.color:
|
case piece.color:
|
||||||
of White:
|
of White:
|
||||||
case piece.kind:
|
case piece.kind:
|
||||||
|
@ -1034,6 +1069,8 @@ proc removePiece(self: ChessBoard, location: Location, emptyGrid: bool = true) =
|
||||||
discard
|
discard
|
||||||
else:
|
else:
|
||||||
discard
|
discard
|
||||||
|
if attack:
|
||||||
|
self.updateAttackedSquares()
|
||||||
|
|
||||||
|
|
||||||
proc movePiece(self: ChessBoard, move: Move, attack: bool = true) =
|
proc movePiece(self: ChessBoard, move: Move, attack: bool = true) =
|
||||||
|
@ -1108,10 +1145,14 @@ proc movePiece(self: ChessBoard, startSquare, targetSquare: Location, attack: bo
|
||||||
proc updateLocations(self: ChessBoard, move: Move) =
|
proc updateLocations(self: ChessBoard, move: Move) =
|
||||||
## Internal helper to update the position of
|
## Internal helper to update the position of
|
||||||
## the pieces on the board after a move
|
## the pieces on the board after a move
|
||||||
let capture = self.getCapture(move)
|
if move.isCapture():
|
||||||
if capture != emptyLocation():
|
self.position.captured = self.grid[move.targetSquare.row, move.targetSquare.col]
|
||||||
self.position.captured = self.grid[capture.row, capture.col]
|
try:
|
||||||
self.removePiece(capture)
|
self.removePiece(move.targetSquare, attack=false)
|
||||||
|
except AssertionDefect:
|
||||||
|
echo move
|
||||||
|
raise
|
||||||
|
|
||||||
# Update the positional metadata of the moving piece
|
# Update the positional metadata of the moving piece
|
||||||
self.movePiece(move)
|
self.movePiece(move)
|
||||||
|
|
||||||
|
@ -1132,8 +1173,7 @@ proc doMove(self: ChessBoard, move: Move) =
|
||||||
halfMoveClock = self.position.halfMoveClock
|
halfMoveClock = self.position.halfMoveClock
|
||||||
fullMoveCount = self.position.fullMoveCount
|
fullMoveCount = self.position.fullMoveCount
|
||||||
castlingAvailable = self.position.castlingAvailable
|
castlingAvailable = self.position.castlingAvailable
|
||||||
let capture = self.getCapture(move)
|
if move.piece.kind == Pawn or move.isCapture():
|
||||||
if move.piece.kind == Pawn or self.isCapture(move):
|
|
||||||
halfMoveClock = 0
|
halfMoveClock = 0
|
||||||
else:
|
else:
|
||||||
inc(halfMoveClock)
|
inc(halfMoveClock)
|
||||||
|
@ -1161,22 +1201,22 @@ proc doMove(self: ChessBoard, move: Move) =
|
||||||
else:
|
else:
|
||||||
discard
|
discard
|
||||||
# Has a rook been captured?
|
# Has a rook been captured?
|
||||||
if capture != emptyLocation():
|
if move.isCapture():
|
||||||
let piece = self.grid[capture.row, capture.col]
|
let piece = self.grid[move.targetSquare.row, move.targetSquare.col]
|
||||||
if piece.kind == Rook:
|
if piece.kind == Rook:
|
||||||
case piece.color:
|
case piece.color:
|
||||||
of White:
|
of White:
|
||||||
if capture == piece.color.queenSideRook():
|
if move.targetSquare == piece.color.queenSideRook():
|
||||||
# Queen side
|
# Queen side
|
||||||
castlingAvailable.white.queen = false
|
castlingAvailable.white.queen = false
|
||||||
elif capture == piece.color.kingSideRook():
|
elif move.targetSquare == piece.color.kingSideRook():
|
||||||
# King side
|
# King side
|
||||||
castlingAvailable.white.king = false
|
castlingAvailable.white.king = false
|
||||||
of Black:
|
of Black:
|
||||||
if capture == piece.color.queenSideRook():
|
if move.targetSquare == piece.color.queenSideRook():
|
||||||
# Queen side
|
# Queen side
|
||||||
castlingAvailable.black.queen = false
|
castlingAvailable.black.queen = false
|
||||||
elif capture == piece.color.kingSideRook():
|
elif move.targetSquare == piece.color.kingSideRook():
|
||||||
# King side
|
# King side
|
||||||
castlingAvailable.black.king = false
|
castlingAvailable.black.king = false
|
||||||
else:
|
else:
|
||||||
|
@ -1193,7 +1233,22 @@ proc doMove(self: ChessBoard, move: Move) =
|
||||||
castlingAvailable.black.queen = false
|
castlingAvailable.black.queen = false
|
||||||
else:
|
else:
|
||||||
discard
|
discard
|
||||||
|
if move.isPromotion():
|
||||||
|
# Move is a pawn promotion: get rid of then pawn
|
||||||
|
# and spawn a new piece
|
||||||
|
self.removePiece(move.startSquare)
|
||||||
|
case move.flag:
|
||||||
|
of PromoteToBishop:
|
||||||
|
self.spawnPiece(move.targetSquare, Piece(kind: Bishop, color: move.piece.color))
|
||||||
|
of PromoteToKnight:
|
||||||
|
self.spawnPiece(move.targetSquare, Piece(kind: Knight, color: move.piece.color))
|
||||||
|
of PromoteToRook:
|
||||||
|
self.spawnPiece(move.targetSquare, Piece(kind: Rook, color: move.piece.color))
|
||||||
|
of PromoteToQueen:
|
||||||
|
self.spawnPiece(move.targetSquare, Piece(kind: Queen, color: move.piece.color))
|
||||||
|
|
||||||
|
else:
|
||||||
|
discard
|
||||||
let previous = self.position
|
let previous = self.position
|
||||||
# Record final position for future reference
|
# Record final position for future reference
|
||||||
self.positions.add(previous)
|
self.positions.add(previous)
|
||||||
|
@ -1211,6 +1266,16 @@ proc doMove(self: ChessBoard, move: Move) =
|
||||||
|
|
||||||
var location: Location
|
var location: Location
|
||||||
if move.flag in [CastleShort, CastleLong]:
|
if move.flag in [CastleShort, CastleLong]:
|
||||||
|
# Revoke all castling rights for the moving king
|
||||||
|
case move.piece.color:
|
||||||
|
of White:
|
||||||
|
self.position.castlingAvailable.white.king = false
|
||||||
|
self.position.castlingAvailable.white.queen = false
|
||||||
|
of Black:
|
||||||
|
self.position.castlingAvailable.black.king = false
|
||||||
|
self.position.castlingAvailable.black.queen = false
|
||||||
|
else:
|
||||||
|
discard
|
||||||
# Move the rook onto the
|
# Move the rook onto the
|
||||||
# correct file
|
# correct file
|
||||||
var
|
var
|
||||||
|
@ -1283,63 +1348,82 @@ proc spawnPiece(self: ChessBoard, location: Location, piece: Piece) =
|
||||||
self.grid[location.row, location.col] = piece
|
self.grid[location.row, location.col] = piece
|
||||||
|
|
||||||
|
|
||||||
|
proc resetBoard*(self: ChessBoard) =
|
||||||
|
## Resets the internal grid representation
|
||||||
|
## according to the positional data stored
|
||||||
|
## in the chessboard. Warning: this can be
|
||||||
|
## expensive, especially in critical paths
|
||||||
|
## or tight loops
|
||||||
|
self.grid = newMatrixFromSeq[Piece](empty, (8, 8))
|
||||||
|
for loc in self.position.pieces.white.pawns:
|
||||||
|
self.grid[loc.row, loc.col] = Piece(color: White, kind: Pawn)
|
||||||
|
for loc in self.position.pieces.black.pawns:
|
||||||
|
self.grid[loc.row, loc.col] = Piece(color: Black, kind: Pawn)
|
||||||
|
for loc in self.position.pieces.white.bishops:
|
||||||
|
self.grid[loc.row, loc.col] = Piece(color: White, kind: Bishop)
|
||||||
|
for loc in self.position.pieces.black.bishops:
|
||||||
|
self.grid[loc.row, loc.col] = Piece(color: Black, kind: Bishop)
|
||||||
|
for loc in self.position.pieces.white.knights:
|
||||||
|
self.grid[loc.row, loc.col] = Piece(color: White, kind: Knight)
|
||||||
|
for loc in self.position.pieces.black.knights:
|
||||||
|
self.grid[loc.row, loc.col] = Piece(color: Black, kind: Knight)
|
||||||
|
for loc in self.position.pieces.white.rooks:
|
||||||
|
self.grid[loc.row, loc.col] = Piece(color: White, kind: Rook)
|
||||||
|
for loc in self.position.pieces.black.rooks:
|
||||||
|
self.grid[loc.row, loc.col] = Piece(color: Black, kind: Rook)
|
||||||
|
for loc in self.position.pieces.white.queens:
|
||||||
|
self.grid[loc.row, loc.col] = Piece(color: White, kind: Queen)
|
||||||
|
for loc in self.position.pieces.black.queens:
|
||||||
|
self.grid[loc.row, loc.col] = Piece(color: Black, kind: Queen)
|
||||||
|
self.grid[self.position.pieces.white.king.row, self.position.pieces.white.king.col] = Piece(color: White, kind: King)
|
||||||
|
self.grid[self.position.pieces.black.king.row, self.position.pieces.black.king.col] = Piece(color: Black, kind: King)
|
||||||
|
|
||||||
|
|
||||||
proc undoMove*(self: ChessBoard, move: Move) =
|
proc undoMove*(self: ChessBoard, move: Move) =
|
||||||
## Undoes the given move
|
## Undoes the given move
|
||||||
if self.positions.len() == 0:
|
if self.positions.len() == 0:
|
||||||
return
|
return
|
||||||
|
let previous = self.position
|
||||||
var position = self.positions[^1]
|
var position = self.positions[^1]
|
||||||
while true:
|
while position.move != move:
|
||||||
if position.move == move:
|
|
||||||
break
|
|
||||||
discard self.positions.pop()
|
discard self.positions.pop()
|
||||||
position = self.positions[^1]
|
position = self.positions[^1]
|
||||||
|
|
||||||
|
self.position = position
|
||||||
self.grid[move.startSquare.row, move.startSquare.col] = move.piece
|
self.grid[move.startSquare.row, move.startSquare.col] = move.piece
|
||||||
if self.isCapture(move):
|
if move.isCapture():
|
||||||
self.grid[move.targetSquare.row, move.targetSquare.col] = self.position.captured
|
self.grid[move.targetSquare.row, move.targetSquare.col] = previous.captured
|
||||||
else:
|
else:
|
||||||
self.grid[move.targetSquare.row, move.targetSquare.col] = emptyPiece()
|
self.grid[move.targetSquare.row, move.targetSquare.col] = emptyPiece()
|
||||||
self.position = position
|
# Reset the location of the rook in the
|
||||||
|
# grid (it's already correct in the piece
|
||||||
|
# list)
|
||||||
|
if move.flag == CastleLong:
|
||||||
|
let
|
||||||
|
rookOld = move.targetSquare + move.piece.color.rightSide()
|
||||||
|
rookNew = move.piece.color.queenSideRook()
|
||||||
|
self.grid[rookNew.row, rookNew.col] = self.grid[rookOld.row, rookOld.col]
|
||||||
|
self.grid[rookOld.row, rookOld.col] = emptyPiece()
|
||||||
|
if move.flag == CastleShort:
|
||||||
|
let
|
||||||
|
rookOld = move.targetSquare + move.piece.color.leftSide()
|
||||||
|
rookNew = move.piece.color.kingSideRook()
|
||||||
|
self.grid[rookNew.row, rookNew.col] = self.grid[rookOld.row, rookOld.col]
|
||||||
|
self.grid[rookOld.row, rookOld.col] = emptyPiece()
|
||||||
|
|
||||||
|
|
||||||
|
proc isLegal(self: ChessBoard, move: Move): bool {.inline.} =
|
||||||
proc isLegal(self: ChessBoard, move: Move, keep: bool = false): bool =
|
|
||||||
## Returns whether the given move is legal
|
## Returns whether the given move is legal
|
||||||
self.doMove(move)
|
return move in self.generateMoves(move.startSquare)
|
||||||
if move notin self.generateMoves(move.startSquare):
|
|
||||||
# Piece cannot arrive to destination (blocked
|
|
||||||
# or otherwise invalid move)
|
|
||||||
return false
|
|
||||||
if not keep:
|
|
||||||
defer: self.undoMove(move)
|
|
||||||
# Move would reveal an attack
|
|
||||||
# on our king: not allowed
|
|
||||||
return self.inCheck(move.piece.color)
|
|
||||||
|
|
||||||
|
|
||||||
proc isLegalFast(self: ChessBoard, move: Move, keep: bool = false): bool =
|
|
||||||
## Returns whether the given move is legal
|
|
||||||
## assuming that the input move is pseudo legal
|
|
||||||
self.position.move = move
|
|
||||||
self.doMove(move)
|
|
||||||
if not keep:
|
|
||||||
defer: self.undoMove(move)
|
|
||||||
|
|
||||||
# Move would reveal an attack
|
|
||||||
# on our king: not allowed
|
|
||||||
if self.inCheck(move.piece.color):
|
|
||||||
return false
|
|
||||||
# All checks have passed: move is legal
|
|
||||||
result = true
|
|
||||||
|
|
||||||
|
|
||||||
proc makeMove*(self: ChessBoard, move: Move): Move {.discardable.} =
|
proc makeMove*(self: ChessBoard, move: Move): Move {.discardable.} =
|
||||||
## Like the other makeMove(), but with a Move object
|
## Like the other makeMove(), but with a Move object
|
||||||
result = move
|
result = move
|
||||||
self.position.move = move
|
self.position.move = move
|
||||||
let legal = self.isLegal(move, keep=true)
|
if not self.isLegal(move):
|
||||||
if not legal:
|
|
||||||
return emptyMove()
|
return emptyMove()
|
||||||
|
self.doMove(move)
|
||||||
result = self.position.move
|
result = self.position.move
|
||||||
|
|
||||||
|
|
||||||
|
@ -1409,37 +1493,59 @@ proc pretty*(self: ChessBoard): string =
|
||||||
result &= "\x1b[0m"
|
result &= "\x1b[0m"
|
||||||
|
|
||||||
|
|
||||||
proc countLegalMoves*(self: ChessBoard, ply: int, verbose: bool = false, current: int = 0): int =
|
proc countLegalMoves*(self: ChessBoard, ply: int, verbose: bool = false, current: int = 0): tuple[nodes: int, captures: int, castles: int, checks: int, promotions: int] =
|
||||||
## Counts (and debugs) the number of legal positions reached after
|
## Counts (and debugs) the number of legal positions reached after
|
||||||
## the given number of half moves
|
## the given number of ply
|
||||||
if ply == 0:
|
if ply == 0:
|
||||||
result = 1
|
result = (1, 0, 0, 0, 0)
|
||||||
else:
|
else:
|
||||||
var before: string
|
var
|
||||||
|
before: string
|
||||||
for move in self.generateAllMoves():
|
for move in self.generateAllMoves():
|
||||||
before = self.pretty()
|
before = self.pretty()
|
||||||
self.doMove(move)
|
self.doMove(move)
|
||||||
if not self.inCheck(move.piece.color):
|
case move.flag:
|
||||||
if verbose:
|
of Capture:
|
||||||
let canCastle = self.canCastle()
|
inc(result.captures)
|
||||||
echo "\x1Bc"
|
of CastleShort, CastleLong:
|
||||||
echo &"Ply: {self.position.plyFromRoot} (move {current + result + 1})"
|
inc(result.castles)
|
||||||
echo &"Move: {move.startSquare.locationToAlgebraic()}{move.targetSquare.locationToAlgebraic()} (({move.startSquare.row}, {move.startSquare.col}) -> ({move.targetSquare.row}, {move.targetSquare.col}))"
|
of PromoteToBishop, PromoteToKnight, PromoteToQueen, PromoteToRook:
|
||||||
echo &"Turn: {move.piece.color}"
|
inc(result.promotions)
|
||||||
echo &"Piece: {move.piece.kind}"
|
else:
|
||||||
echo &"Flag: {move.flag}"
|
discard
|
||||||
echo &"Can castle:\n - King side: {(if canCastle.king: \"yes\" else: \"no\")}\n - Queen side: {(if canCastle.queen: \"yes\" else: \"no\")}"
|
if self.inCheck():
|
||||||
echo "\nBefore:"
|
# Opponent king is in check
|
||||||
echo before
|
inc(result.checks)
|
||||||
echo "\nNow: "
|
if verbose:
|
||||||
echo self.pretty()
|
let canCastle = self.canCastle(move.piece.color)
|
||||||
try:
|
echo "\x1Bc"
|
||||||
discard readLine(stdin)
|
echo &"Ply: {self.position.plyFromRoot} (move {current + result.nodes + 1})"
|
||||||
except IOError:
|
echo &"Move: {move.startSquare.locationToAlgebraic()}{move.targetSquare.locationToAlgebraic()} (({move.startSquare.row}, {move.startSquare.col}) -> ({move.targetSquare.row}, {move.targetSquare.col}))"
|
||||||
discard
|
echo &"Turn: {move.piece.color}"
|
||||||
except EOFError:
|
echo &"Piece: {move.piece.kind}"
|
||||||
discard
|
echo &"Flag: {move.flag}"
|
||||||
result += self.countLegalMoves(ply - 1, verbose, result + 1)
|
echo &"Can castle:\n - King side: {(if canCastle.king: \"yes\" else: \"no\")}\n - Queen side: {(if canCastle.queen: \"yes\" else: \"no\")}"
|
||||||
|
echo "\nBefore:"
|
||||||
|
echo before
|
||||||
|
echo "\nNow: "
|
||||||
|
echo self.pretty()
|
||||||
|
echo &"\n\nTotal captures: {result.captures}"
|
||||||
|
echo &"Total castles: {result.castles}"
|
||||||
|
echo &"Total checks: {result.checks}"
|
||||||
|
echo &"Total promotions: {result.promotions}"
|
||||||
|
|
||||||
|
try:
|
||||||
|
discard readLine(stdin)
|
||||||
|
except IOError:
|
||||||
|
discard
|
||||||
|
except EOFError:
|
||||||
|
discard
|
||||||
|
let next = self.countLegalMoves(ply - 1, verbose, result.nodes + 1)
|
||||||
|
result.nodes += next.nodes
|
||||||
|
result.captures += next.captures
|
||||||
|
result.checks += next.checks
|
||||||
|
result.promotions += next.promotions
|
||||||
|
result.castles += next.castles
|
||||||
self.undoMove(move)
|
self.undoMove(move)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1501,6 +1607,6 @@ when isMainModule:
|
||||||
when compileOption("profiler"):
|
when compileOption("profiler"):
|
||||||
import nimprof
|
import nimprof
|
||||||
|
|
||||||
|
#b = newChessboardFromFEN("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - ")
|
||||||
echo b.countLegalMoves(4, verbose=false)
|
echo b.countLegalMoves(3, verbose=false)
|
||||||
echo "All tests were successful"
|
echo "All tests were successful"
|
Loading…
Reference in New Issue