More work on attack handling and some bug fixes
This commit is contained in:
parent
6fbcd4ff74
commit
2b16b5ec61
|
@ -14,8 +14,6 @@
|
||||||
|
|
||||||
import std/strutils
|
import std/strutils
|
||||||
import std/strformat
|
import std/strformat
|
||||||
import std/times
|
|
||||||
import std/math
|
|
||||||
import std/bitops
|
import std/bitops
|
||||||
|
|
||||||
|
|
||||||
|
@ -29,8 +27,6 @@ export bitboards, magics, pieces, moves
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
||||||
CountData = tuple[nodes: uint64, captures: uint64, castles: uint64, checks: uint64, promotions: uint64, enPassant: uint64, checkmates: uint64]
|
|
||||||
|
|
||||||
Position* = object
|
Position* = object
|
||||||
## A chess position
|
## A chess position
|
||||||
|
|
||||||
|
@ -56,8 +52,8 @@ type
|
||||||
pieces: tuple[white, black: tuple[king, queens, rooks, bishops, knights, pawns: Bitboard]]
|
pieces: tuple[white, black: tuple[king, queens, rooks, bishops, knights, pawns: Bitboard]]
|
||||||
# Pinned pieces for each side
|
# Pinned pieces for each side
|
||||||
pins: tuple[white, black: Bitboard]
|
pins: tuple[white, black: Bitboard]
|
||||||
# Checking pieces
|
# Pieces checking the current side to move
|
||||||
checkers: tuple[white, black: Bitboard]
|
checkers: Bitboard
|
||||||
|
|
||||||
|
|
||||||
ChessBoard* = ref object
|
ChessBoard* = ref object
|
||||||
|
@ -83,10 +79,6 @@ proc movePiece(self: ChessBoard, move: Move)
|
||||||
proc removePiece(self: ChessBoard, square: Square)
|
proc removePiece(self: ChessBoard, square: Square)
|
||||||
|
|
||||||
|
|
||||||
proc extend[T](self: var seq[T], other: openarray[T]) {.inline.} =
|
|
||||||
for x in other:
|
|
||||||
self.add(x)
|
|
||||||
|
|
||||||
proc update*(self: ChessBoard)
|
proc update*(self: ChessBoard)
|
||||||
|
|
||||||
|
|
||||||
|
@ -121,27 +113,6 @@ func getHalfMoveCount*(self: ChessBoard): int {.inline.} =
|
||||||
return self.position.halfMoveClock
|
return self.position.halfMoveClock
|
||||||
|
|
||||||
|
|
||||||
func getStartRank(piece: Piece): int {.inline.} =
|
|
||||||
## Retrieves the starting row of
|
|
||||||
## the given piece inside our 8x8
|
|
||||||
## grid
|
|
||||||
case piece.color:
|
|
||||||
of None:
|
|
||||||
return -1
|
|
||||||
of White:
|
|
||||||
case piece.kind:
|
|
||||||
of Pawn:
|
|
||||||
return 6
|
|
||||||
else:
|
|
||||||
return 7
|
|
||||||
of Black:
|
|
||||||
case piece.kind:
|
|
||||||
of Pawn:
|
|
||||||
return 1
|
|
||||||
else:
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
func getKingStartingSquare*(color: PieceColor): Square {.inline.} =
|
func getKingStartingSquare*(color: PieceColor): Square {.inline.} =
|
||||||
## Retrieves the starting square of the king
|
## Retrieves the starting square of the king
|
||||||
## for the given color
|
## for the given color
|
||||||
|
@ -163,7 +134,7 @@ func longCastleRook*(color: PieceColor): Square {.inline.} = (if color == White:
|
||||||
func shortCastleRook*(color: PieceColor): Square {.inline.} = (if color == White: "f1".toSquare() else: "f8".toSquare())
|
func shortCastleRook*(color: PieceColor): Square {.inline.} = (if color == White: "f1".toSquare() else: "f8".toSquare())
|
||||||
|
|
||||||
|
|
||||||
proc inCheck*(self: ChessBoard, side: PieceColor): bool
|
proc inCheck*(self: ChessBoard): bool
|
||||||
|
|
||||||
|
|
||||||
proc newChessboard: ChessBoard =
|
proc newChessboard: ChessBoard =
|
||||||
|
@ -178,13 +149,7 @@ func `[]`(self: array[64, Piece], square: Square): Piece {.inline.} = self[squar
|
||||||
func `[]=`(self: var array[64, Piece], square: Square, piece: Piece) {.inline.} = self[square.int8] = piece
|
func `[]=`(self: var array[64, Piece], square: Square, piece: Piece) {.inline.} = self[square.int8] = piece
|
||||||
|
|
||||||
|
|
||||||
func getDirectionMask(self: ChessBoard, square: Square, direction: Direction): Bitboard =
|
func getBitboard*(self: ChessBoard, kind: PieceKind, color: PieceColor): Bitboard =
|
||||||
## Like getDirectionMask(), but used within the board context
|
|
||||||
## with a piece square and direction only
|
|
||||||
return getDirectionMask(square, self.grid[square].color, direction)
|
|
||||||
|
|
||||||
|
|
||||||
func getBitboard(self: ChessBoard, kind: PieceKind, color: PieceColor): Bitboard =
|
|
||||||
## Returns the positional bitboard for the given piece kind and color
|
## Returns the positional bitboard for the given piece kind and color
|
||||||
case color:
|
case color:
|
||||||
of White:
|
of White:
|
||||||
|
@ -223,7 +188,7 @@ func getBitboard(self: ChessBoard, kind: PieceKind, color: PieceColor): Bitboard
|
||||||
discard
|
discard
|
||||||
|
|
||||||
|
|
||||||
func getBitboard(self: ChessBoard, piece: Piece): Bitboard =
|
func getBitboard*(self: ChessBoard, piece: Piece): Bitboard =
|
||||||
## Returns the positional bitboard for the given piece type
|
## Returns the positional bitboard for the given piece type
|
||||||
return self.getBitboard(piece.kind, piece.color)
|
return self.getBitboard(piece.kind, piece.color)
|
||||||
|
|
||||||
|
@ -242,7 +207,6 @@ proc newChessboardFromFEN*(fen: string): ChessBoard =
|
||||||
index = 0
|
index = 0
|
||||||
# Temporary variable to store a piece
|
# Temporary variable to store a piece
|
||||||
piece: Piece
|
piece: Piece
|
||||||
pieces: int
|
|
||||||
# See https://en.wikipedia.org/wiki/Forsyth%E2%80%93Edwards_Notation
|
# See https://en.wikipedia.org/wiki/Forsyth%E2%80%93Edwards_Notation
|
||||||
while index <= fen.high():
|
while index <= fen.high():
|
||||||
var c = fen[index]
|
var c = fen[index]
|
||||||
|
@ -376,9 +340,6 @@ proc newChessboardFromFEN*(fen: string): ChessBoard =
|
||||||
else:
|
else:
|
||||||
raise newException(ValueError, "invalid FEN: too many fields in FEN string")
|
raise newException(ValueError, "invalid FEN: too many fields in FEN string")
|
||||||
inc(index)
|
inc(index)
|
||||||
if result.inCheck(result.getSideToMove().opposite):
|
|
||||||
# Opponent king cannot be captured on the next move
|
|
||||||
raise newException(ValueError, "invalid position: opponent king can be captured")
|
|
||||||
if result.position.pieces.white.king == 0 or result.position.pieces.black.king == 0:
|
if result.position.pieces.white.king == 0 or result.position.pieces.black.king == 0:
|
||||||
# Both kings must be on the board
|
# Both kings must be on the board
|
||||||
raise newException(ValueError, "invalid position: exactly one king of each color must be present")
|
raise newException(ValueError, "invalid position: exactly one king of each color must be present")
|
||||||
|
@ -511,18 +472,6 @@ func getFlags*(move: Move): seq[MoveFlag] =
|
||||||
result.add(Default)
|
result.add(Default)
|
||||||
|
|
||||||
|
|
||||||
func getKingSquare*(self: ChessBoard, color: PieceColor): Square {.inline.} =
|
|
||||||
## Returns the square of the king for the given
|
|
||||||
## side
|
|
||||||
case color:
|
|
||||||
of White:
|
|
||||||
return self.position.pieces.white.king.toSquare()
|
|
||||||
of Black:
|
|
||||||
return self.position.pieces.black.king.toSquare()
|
|
||||||
else:
|
|
||||||
discard
|
|
||||||
|
|
||||||
|
|
||||||
proc getOccupancyFor(self: ChessBoard, color: PieceColor): Bitboard =
|
proc getOccupancyFor(self: ChessBoard, color: PieceColor): Bitboard =
|
||||||
## Get the occupancy bitboard for every piece of the given color
|
## Get the occupancy bitboard for every piece of the given color
|
||||||
case color:
|
case color:
|
||||||
|
@ -559,57 +508,48 @@ proc getKingAttacks(self: ChessBoard, square: Square, attacker: PieceColor): Bit
|
||||||
## the king of the given side
|
## the king of the given side
|
||||||
result = Bitboard(0)
|
result = Bitboard(0)
|
||||||
let
|
let
|
||||||
sq = square.toBitboard()
|
|
||||||
king = self.getBitboard(King, attacker)
|
king = self.getBitboard(King, attacker)
|
||||||
if (KING_BITBOARDS[square.uint] and king) != 0:
|
if (KING_BITBOARDS[square.uint] and king) != 0:
|
||||||
result = result or sq
|
result = result or king
|
||||||
|
|
||||||
|
|
||||||
proc getKnightAttacks(self: ChessBoard, square: Square, attacker: PieceColor): Bitboard =
|
proc getKnightAttacks(self: ChessBoard, square: Square, attacker: PieceColor): Bitboard =
|
||||||
## Returns the attack bitboard for the given square from
|
## Returns the attack bitboard for the given square from
|
||||||
## the knights of the given side
|
## the knights of the given side
|
||||||
let
|
let
|
||||||
sq = square.toBitboard()
|
|
||||||
knights = self.getBitboard(Knight, attacker)
|
knights = self.getBitboard(Knight, attacker)
|
||||||
result = Bitboard(0)
|
result = Bitboard(0)
|
||||||
for knight in knights:
|
for knight in knights:
|
||||||
if (KNIGHT_BITBOARDS[square.uint] and knight.toBitboard()) != 0:
|
let knightBB = knight.toBitboard()
|
||||||
result = result or knight.toBitboard()
|
if (KNIGHT_BITBOARDS[square.uint] and knightBB) != 0:
|
||||||
|
result = result or knightBB
|
||||||
|
|
||||||
|
|
||||||
proc getSlidingAttacks(self: ChessBoard, square: Square, attacker: PieceColor): Bitboard =
|
proc getSlidingAttacks(self: ChessBoard, square: Square, attacker: PieceColor): Bitboard =
|
||||||
## Returns the attack bitboard for the given square from
|
## Returns the attack bitboard for the given square from
|
||||||
## the sliding pieces of the given side
|
## the sliding pieces of the given side
|
||||||
let
|
let
|
||||||
sq = square.toBitboard()
|
|
||||||
queens = self.getBitboard(Queen, attacker)
|
queens = self.getBitboard(Queen, attacker)
|
||||||
rooks = self.getBitboard(Rook, attacker)
|
rooks = self.getBitboard(Rook, attacker) or queens
|
||||||
bishops = self.getBitboard(Bishop, attacker)
|
bishops = self.getBitboard(Bishop, attacker) or queens
|
||||||
occupancy = self.getOccupancy()
|
|
||||||
result = Bitboard(0)
|
result = Bitboard(0)
|
||||||
for rook in rooks:
|
for rook in rooks:
|
||||||
let blockers = Rook.getRelevantBlockers(square)
|
let blockers = Rook.getRelevantBlockers(square)
|
||||||
if (getRookMoves(square, blockers) and rook.toBitboard()) != 0:
|
let rookBB = rook.toBitboard()
|
||||||
result = result or rook.toBitboard()
|
if (getRookMoves(square, blockers) and rookBB) != 0:
|
||||||
|
result = result or rookBB
|
||||||
for bishop in bishops:
|
for bishop in bishops:
|
||||||
let blockers = Bishop.getRelevantBlockers(square)
|
let
|
||||||
if (getBishopMoves(square, blockers) and bishop.toBitboard()) != 0:
|
blockers = Bishop.getRelevantBlockers(square)
|
||||||
result = result or bishop.toBitboard()
|
bishopBB = bishop.toBitboard()
|
||||||
for queen in queens:
|
if (getBishopMoves(square, blockers) and bishopBB) != 0:
|
||||||
let rookBlockers = Rook.getRelevantBlockers(square)
|
result = result or bishopBB
|
||||||
if (getRookMoves(square, rookBlockers) and queen.toBitboard()) != 0:
|
|
||||||
result = result or queen.toBitboard()
|
|
||||||
let bishopBlockers = Bishop.getRelevantBlockers(square)
|
|
||||||
if (getBishopMoves(square, bishopBlockers) and queen.toBitboard()) != 0:
|
|
||||||
result = result or queen.toBitboard()
|
|
||||||
|
|
||||||
|
|
||||||
proc getAttacksTo*(self: ChessBoard, square: Square, attacker: PieceColor): Bitboard =
|
proc getAttacksTo*(self: ChessBoard, square: Square, attacker: PieceColor): Bitboard =
|
||||||
## Computes the attack bitboard for the given square from
|
## Computes the attack bitboard for the given square from
|
||||||
## the given side
|
## the given side
|
||||||
result = Bitboard(0)
|
result = Bitboard(0)
|
||||||
let
|
|
||||||
squareBitboard = square.toBitboard()
|
|
||||||
result = result or self.getPawnAttacks(square, attacker)
|
result = result or self.getPawnAttacks(square, attacker)
|
||||||
result = result or self.getKingAttacks(square, attacker)
|
result = result or self.getKingAttacks(square, attacker)
|
||||||
result = result or self.getKnightAttacks(square, attacker)
|
result = result or self.getKnightAttacks(square, attacker)
|
||||||
|
@ -617,26 +557,15 @@ proc getAttacksTo*(self: ChessBoard, square: Square, attacker: PieceColor): Bitb
|
||||||
|
|
||||||
|
|
||||||
proc updateCheckers(self: ChessBoard) =
|
proc updateCheckers(self: ChessBoard) =
|
||||||
let side = self.getSideToMove()
|
let
|
||||||
let checkers = self.getAttacksTo(self.getKingSquare(side), side.opposite())
|
side = self.getSideToMove()
|
||||||
case side:
|
king = self.getBitboard(King, side).toSquare()
|
||||||
of White:
|
self.position.checkers = self.getAttacksTo(king, side.opposite())
|
||||||
self.position.checkers.white = checkers
|
|
||||||
of Black:
|
|
||||||
self.position.checkers.black = checkers
|
|
||||||
else:
|
|
||||||
discard
|
|
||||||
|
|
||||||
|
|
||||||
proc inCheck(self: ChessBoard, side: PieceColor): bool =
|
proc inCheck(self: ChessBoard): bool =
|
||||||
## Returns if the current side to move is in check
|
## Returns if the current side to move is in check
|
||||||
case self.getSideToMove():
|
return self.position.checkers != 0
|
||||||
of White:
|
|
||||||
return self.position.checkers.white != 0
|
|
||||||
of Black:
|
|
||||||
return self.position.checkers.black != 0
|
|
||||||
else:
|
|
||||||
discard
|
|
||||||
|
|
||||||
|
|
||||||
proc canCastle*(self: ChessBoard, side: PieceColor): tuple[king, queen: bool] =
|
proc canCastle*(self: ChessBoard, side: PieceColor): tuple[king, queen: bool] =
|
||||||
|
@ -644,14 +573,6 @@ proc canCastle*(self: ChessBoard, side: PieceColor): tuple[king, queen: bool] =
|
||||||
return (false, false) # TODO
|
return (false, false) # TODO
|
||||||
|
|
||||||
|
|
||||||
proc getCapturablePieces(self: ChessBoard, side: PieceColor): Bitboard {.inline.} =
|
|
||||||
## Returns the set of pieces of the given color that can
|
|
||||||
## be captured
|
|
||||||
|
|
||||||
# Just a handy helper to filter out the king and avoid code duplication
|
|
||||||
return self.getOccupancyFor(side) and not self.getBitboard(King, side)
|
|
||||||
|
|
||||||
|
|
||||||
proc generatePawnMovements(self: ChessBoard, moves: var MoveList) =
|
proc generatePawnMovements(self: ChessBoard, moves: var MoveList) =
|
||||||
## Helper of generatePawnMoves for generating all non-capture
|
## Helper of generatePawnMoves for generating all non-capture
|
||||||
## and non-promotion pawn moves
|
## and non-promotion pawn moves
|
||||||
|
@ -679,7 +600,7 @@ proc generatePawnCaptures(self: ChessBoard, moves: var MoveList) =
|
||||||
nonSideToMove = sideToMove.opposite()
|
nonSideToMove = sideToMove.opposite()
|
||||||
pawns = self.getBitboard(Pawn, sideToMove)
|
pawns = self.getBitboard(Pawn, sideToMove)
|
||||||
# We can only capture enemy pieces (except the king)
|
# We can only capture enemy pieces (except the king)
|
||||||
enemyPieces = self.getCapturablePieces(nonSideToMove)
|
enemyPieces = self.getOccupancyFor(nonSideToMove)
|
||||||
enemyPawns = self.getBitboard(Pawn, nonSideToMove)
|
enemyPawns = self.getBitboard(Pawn, nonSideToMove)
|
||||||
rightMovement = pawns.forwardRightRelativeTo(sideToMove)
|
rightMovement = pawns.forwardRightRelativeTo(sideToMove)
|
||||||
leftMovement = pawns.forwardLeftRelativeTo(sideToMove)
|
leftMovement = pawns.forwardLeftRelativeTo(sideToMove)
|
||||||
|
@ -726,12 +647,12 @@ proc generateRookMoves(self: ChessBoard, moves: var MoveList) =
|
||||||
let
|
let
|
||||||
sideToMove = self.getSideToMove()
|
sideToMove = self.getSideToMove()
|
||||||
occupancy = self.getOccupancy()
|
occupancy = self.getOccupancy()
|
||||||
enemyPieces = self.getCapturablePieces(sideToMove.opposite())
|
enemyPieces = self.getOccupancyFor(sideToMove.opposite())
|
||||||
rooks = self.getBitboard(Rook, sideToMove)
|
rooks = self.getBitboard(Rook, sideToMove) or self.getBitboard(Queen, sideToMove)
|
||||||
for square in rooks:
|
for square in rooks:
|
||||||
let blockers = occupancy and Rook.getRelevantBlockers(square)
|
let
|
||||||
# Quiet moves
|
blockers = occupancy and Rook.getRelevantBlockers(square)
|
||||||
var moveset = getRookMoves(square, blockers)
|
moveset = getRookMoves(square, blockers)
|
||||||
for target in moveset and not occupancy:
|
for target in moveset and not occupancy:
|
||||||
moves.add(createMove(square, target))
|
moves.add(createMove(square, target))
|
||||||
# Captures
|
# Captures
|
||||||
|
@ -741,51 +662,27 @@ proc generateRookMoves(self: ChessBoard, moves: var MoveList) =
|
||||||
|
|
||||||
proc generateBishopMoves(self: ChessBoard, moves: var MoveList) =
|
proc generateBishopMoves(self: ChessBoard, moves: var MoveList) =
|
||||||
## Helper of generateSlidingMoves to generate bishop moves
|
## Helper of generateSlidingMoves to generate bishop moves
|
||||||
# self.generateBishopMovements(moves)
|
|
||||||
# self.generateBishopCaptures(moves)
|
|
||||||
let
|
let
|
||||||
sideToMove = self.getSideToMove()
|
sideToMove = self.getSideToMove()
|
||||||
enemyPieces = self.getOccupancyFor(sideToMove.opposite())
|
enemyPieces = self.getOccupancyFor(sideToMove.opposite())
|
||||||
occupancy = self.getOccupancy()
|
occupancy = self.getOccupancy()
|
||||||
bishops = self.getBitboard(Bishop, sideToMove)
|
bishops = self.getBitboard(Bishop, sideToMove) or self.getBitboard(Queen, sideToMove)
|
||||||
for square in bishops:
|
for square in bishops:
|
||||||
let blockers = occupancy and Bishop.getRelevantBlockers(square)
|
let
|
||||||
let moveset = getBishopMoves(square, blockers)
|
blockers = occupancy and Bishop.getRelevantBlockers(square)
|
||||||
# Can't move over other pieces
|
moveset = getBishopMoves(square, blockers)
|
||||||
for target in moveset and not occupancy:
|
for target in moveset and not occupancy:
|
||||||
moves.add(createMove(square, target))
|
moves.add(createMove(square, target))
|
||||||
for target in moveset and enemyPieces:
|
for target in moveset and enemyPieces:
|
||||||
moves.add(createMove(square, target, Capture))
|
moves.add(createMove(square, target, Capture))
|
||||||
|
|
||||||
|
|
||||||
proc generateQueenMoves(self: ChessBoard, moves: var MoveList) =
|
|
||||||
## Helper of generateSlidingMoves to generate queen moves
|
|
||||||
let
|
|
||||||
sideToMove = self.getSideToMove()
|
|
||||||
occupancy = self.getOccupancy()
|
|
||||||
enemyPieces = self.getCapturablePieces(sideToMove.opposite())
|
|
||||||
queens = self.getBitboard(Queen, sideToMove)
|
|
||||||
for square in queens:
|
|
||||||
# A queen is just a rook plus a bishop in terms of move
|
|
||||||
# generation
|
|
||||||
let
|
|
||||||
rookBlockers = Rook.getRelevantBlockers(square) and occupancy
|
|
||||||
bishopBlockers = Bishop.getRelevantBlockers(square) and occupancy
|
|
||||||
rookMoves = getRookMoves(square, rookBlockers)
|
|
||||||
bishopMoves = getBishopMoves(square, bishopBlockers)
|
|
||||||
let moveset = rookMoves or bishopMoves
|
|
||||||
# Can't move over other pieces
|
|
||||||
for target in moveset and not occupancy:
|
|
||||||
moves.add(createMove(square, target))
|
|
||||||
for target in moveset and enemyPieces:
|
|
||||||
moves.add(createMove(square, target))
|
|
||||||
|
|
||||||
|
|
||||||
proc generateSlidingMoves(self: ChessBoard, moves: var MoveList) =
|
proc generateSlidingMoves(self: ChessBoard, moves: var MoveList) =
|
||||||
## Generates all legal sliding moves for the side to move
|
## Generates all legal sliding moves for the side to move
|
||||||
self.generateRookMoves(moves)
|
self.generateRookMoves(moves)
|
||||||
self.generateBishopMoves(moves)
|
self.generateBishopMoves(moves)
|
||||||
self.generateQueenMoves(moves)
|
# Queens are just handled rooks + bishops
|
||||||
|
|
||||||
|
|
||||||
proc generateKingMoves(self: ChessBoard, moves: var MoveList) =
|
proc generateKingMoves(self: ChessBoard, moves: var MoveList) =
|
||||||
|
@ -794,11 +691,11 @@ proc generateKingMoves(self: ChessBoard, moves: var MoveList) =
|
||||||
sideToMove = self.getSideToMove()
|
sideToMove = self.getSideToMove()
|
||||||
king = self.getBitboard(King, sideToMove)
|
king = self.getBitboard(King, sideToMove)
|
||||||
moveIdx = king.toSquare().uint64
|
moveIdx = king.toSquare().uint64
|
||||||
allowedSquares = not self.getOccupancy()
|
occupancy = self.getOccupancy()
|
||||||
nonSideToMove = sideToMove.opposite()
|
nonSideToMove = sideToMove.opposite()
|
||||||
enemyPieces = self.getCapturablePieces(nonSideToMove)
|
enemyPieces = self.getOccupancyFor(nonSideToMove)
|
||||||
# Regular moves
|
# Regular moves
|
||||||
for square in KING_BITBOARDS[moveIdx] and allowedSquares:
|
for square in KING_BITBOARDS[moveIdx] and not occupancy:
|
||||||
moves.add(createMove(king, square))
|
moves.add(createMove(king, square))
|
||||||
# Captures
|
# Captures
|
||||||
for square in KING_BITBOARDS[moveIdx] and enemyPieces:
|
for square in KING_BITBOARDS[moveIdx] and enemyPieces:
|
||||||
|
@ -810,12 +707,12 @@ proc generateKnightMoves(self: ChessBoard, moves: var MoveList)=
|
||||||
let
|
let
|
||||||
sideToMove = self.getSideToMove()
|
sideToMove = self.getSideToMove()
|
||||||
knights = self.getBitboard(Knight, sideToMove)
|
knights = self.getBitboard(Knight, sideToMove)
|
||||||
allowedSquares = not self.getOccupancy()
|
occupancy = self.getOccupancy()
|
||||||
nonSideToMove = sideToMove.opposite()
|
nonSideToMove = sideToMove.opposite()
|
||||||
enemyPieces = self.getCapturablePieces(nonSideToMove)
|
enemyPieces = self.getOccupancyFor(nonSideToMove)
|
||||||
for square in knights:
|
for square in knights:
|
||||||
# Regular moves
|
# Regular moves
|
||||||
for target in KNIGHT_BITBOARDS[square.uint64] and allowedSquares:
|
for target in KNIGHT_BITBOARDS[square.uint64] and not occupancy:
|
||||||
moves.add(createMove(square, target))
|
moves.add(createMove(square, target))
|
||||||
# Captures
|
# Captures
|
||||||
for target in KNIGHT_BITBOARDS[square.uint64] and enemyPieces:
|
for target in KNIGHT_BITBOARDS[square.uint64] and enemyPieces:
|
||||||
|
@ -834,6 +731,7 @@ proc generateMoves*(self: ChessBoard, moves: var MoveList) =
|
||||||
self.generatePawnMoves(moves)
|
self.generatePawnMoves(moves)
|
||||||
self.generateKnightMoves(moves)
|
self.generateKnightMoves(moves)
|
||||||
self.generateSlidingMoves(moves)
|
self.generateSlidingMoves(moves)
|
||||||
|
# self.updateCheckers()
|
||||||
|
|
||||||
|
|
||||||
proc removePieceFromBitboard(self: ChessBoard, square: Square) =
|
proc removePieceFromBitboard(self: ChessBoard, square: Square) =
|
||||||
|
@ -942,11 +840,6 @@ proc movePiece(self: ChessBoard, move: Move) =
|
||||||
self.spawnPiece(move.targetSquare, piece)
|
self.spawnPiece(move.targetSquare, piece)
|
||||||
|
|
||||||
|
|
||||||
proc movePiece(self: ChessBoard, startSquare, targetSquare: Square) =
|
|
||||||
## Like the other movePiece(), but with two squares
|
|
||||||
self.movePiece(Move(startSquare: startSquare, targetSquare: targetSquare))
|
|
||||||
|
|
||||||
|
|
||||||
proc doMove*(self: ChessBoard, move: Move) =
|
proc doMove*(self: ChessBoard, move: Move) =
|
||||||
## Internal function called by makeMove after
|
## Internal function called by makeMove after
|
||||||
## performing legality checks. Can be used in
|
## performing legality checks. Can be used in
|
||||||
|
@ -1220,113 +1113,11 @@ proc toFEN*(self: ChessBoard): string =
|
||||||
|
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
|
import nimfishpkg/tui
|
||||||
|
import nimfishpkg/misc
|
||||||
|
|
||||||
|
basicTests()
|
||||||
proc testPiece(piece: Piece, kind: PieceKind, color: PieceColor) =
|
|
||||||
doAssert piece.kind == kind and piece.color == color, &"expected piece of kind {kind} and color {color}, got {piece.kind} / {piece.color} instead"
|
|
||||||
|
|
||||||
proc testPieceCount(board: ChessBoard, kind: PieceKind, color: PieceColor, count: int) =
|
|
||||||
let pieces = board.countPieces(kind, color)
|
|
||||||
doAssert pieces == count, &"expected {count} pieces of kind {kind} and color {color}, got {pieces} instead"
|
|
||||||
|
|
||||||
proc testPieceBitboard(bitboard: Bitboard, squares: seq[Square]) =
|
|
||||||
var i = 0
|
|
||||||
for square in bitboard:
|
|
||||||
doAssert squares[i] == square, &"squares[{i}] != bitboard[i]: {squares[i]} != {square}"
|
|
||||||
inc(i)
|
|
||||||
if i != squares.len():
|
|
||||||
doAssert false, &"bitboard.len() ({i}) != squares.len() ({squares.len()})"
|
|
||||||
|
|
||||||
|
|
||||||
var b = newDefaultChessboard()
|
|
||||||
# Ensure correct number of pieces
|
|
||||||
testPieceCount(b, Pawn, White, 8)
|
|
||||||
testPieceCount(b, Pawn, Black, 8)
|
|
||||||
testPieceCount(b, Knight, White, 2)
|
|
||||||
testPieceCount(b, Knight, Black, 2)
|
|
||||||
testPieceCount(b, Bishop, White, 2)
|
|
||||||
testPieceCount(b, Bishop, Black, 2)
|
|
||||||
testPieceCount(b, Rook, White, 2)
|
|
||||||
testPieceCount(b, Rook, Black, 2)
|
|
||||||
testPieceCount(b, Queen, White, 1)
|
|
||||||
testPieceCount(b, Queen, Black, 1)
|
|
||||||
testPieceCount(b, King, White, 1)
|
|
||||||
testPieceCount(b, King, Black, 1)
|
|
||||||
|
|
||||||
# Ensure pieces are in the correct squares. This is testing the FEN
|
|
||||||
# parser
|
|
||||||
|
|
||||||
# Pawns
|
|
||||||
for loc in ["a2", "b2", "c2", "d2", "e2", "f2", "g2", "h2"]:
|
|
||||||
testPiece(b.getPiece(loc), Pawn, White)
|
|
||||||
for loc in ["a7", "b7", "c7", "d7", "e7", "f7", "g7", "h7"]:
|
|
||||||
testPiece(b.getPiece(loc), Pawn, Black)
|
|
||||||
# Rooks
|
|
||||||
testPiece(b.getPiece("a1"), Rook, White)
|
|
||||||
testPiece(b.getPiece("h1"), Rook, White)
|
|
||||||
testPiece(b.getPiece("a8"), Rook, Black)
|
|
||||||
testPiece(b.getPiece("h8"), Rook, Black)
|
|
||||||
# Knights
|
|
||||||
testPiece(b.getPiece("b1"), Knight, White)
|
|
||||||
testPiece(b.getPiece("g1"), Knight, White)
|
|
||||||
testPiece(b.getPiece("b8"), Knight, Black)
|
|
||||||
testPiece(b.getPiece("g8"), Knight, Black)
|
|
||||||
# Bishops
|
|
||||||
testPiece(b.getPiece("c1"), Bishop, White)
|
|
||||||
testPiece(b.getPiece("f1"), Bishop, White)
|
|
||||||
testPiece(b.getPiece("c8"), Bishop, Black)
|
|
||||||
testPiece(b.getPiece("f8"), Bishop, Black)
|
|
||||||
# Kings
|
|
||||||
testPiece(b.getPiece("e1"), King, White)
|
|
||||||
testPiece(b.getPiece("e8"), King, Black)
|
|
||||||
# Queens
|
|
||||||
testPiece(b.getPiece("d1"), Queen, White)
|
|
||||||
testPiece(b.getPiece("d8"), Queen, Black)
|
|
||||||
|
|
||||||
# Ensure our bitboards match with the board
|
|
||||||
let
|
|
||||||
whitePawns = b.getBitboard(Pawn, White)
|
|
||||||
whiteKnights = b.getBitboard(Knight, White)
|
|
||||||
whiteBishops = b.getBitboard(Bishop, White)
|
|
||||||
whiteRooks = b.getBitboard(Rook, White)
|
|
||||||
whiteQueens = b.getBitboard(Queen, White)
|
|
||||||
whiteKing = b.getBitboard(King, White)
|
|
||||||
blackPawns = b.getBitboard(Pawn, Black)
|
|
||||||
blackKnights = b.getBitboard(Knight, Black)
|
|
||||||
blackBishops = b.getBitboard(Bishop, Black)
|
|
||||||
blackRooks = b.getBitboard(Rook, Black)
|
|
||||||
blackQueens = b.getBitboard(Queen, Black)
|
|
||||||
blackKing = b.getBitboard(King, Black)
|
|
||||||
whitePawnSquares = @[makeSquare(6'i8, 0'i8), makeSquare(6, 1), makeSquare(6, 2), makeSquare(6, 3), makeSquare(6, 4), makeSquare(6, 5), makeSquare(6, 6), makeSquare(6, 7)]
|
|
||||||
whiteKnightSquares = @[makeSquare(7'i8, 1'i8), makeSquare(7, 6)]
|
|
||||||
whiteBishopSquares = @[makeSquare(7'i8, 2'i8), makeSquare(7, 5)]
|
|
||||||
whiteRookSquares = @[makeSquare(7'i8, 0'i8), makeSquare(7, 7)]
|
|
||||||
whiteQueenSquares = @[makeSquare(7'i8, 3'i8)]
|
|
||||||
whiteKingSquares = @[makeSquare(7'i8, 4'i8)]
|
|
||||||
blackPawnSquares = @[makeSquare(1'i8, 0'i8), makeSquare(1, 1), makeSquare(1, 2), makeSquare(1, 3), makeSquare(1, 4), makeSquare(1, 5), makeSquare(1, 6), makeSquare(1, 7)]
|
|
||||||
blackKnightSquares = @[makeSquare(0'i8, 1'i8), makeSquare(0, 6)]
|
|
||||||
blackBishopSquares = @[makeSquare(0'i8, 2'i8), makeSquare(0, 5)]
|
|
||||||
blackRookSquares = @[makeSquare(0'i8, 0'i8), makeSquare(0, 7)]
|
|
||||||
blackQueenSquares = @[makeSquare(0'i8, 3'i8)]
|
|
||||||
blackKingSquares = @[makeSquare(0'i8, 4'i8)]
|
|
||||||
|
|
||||||
|
|
||||||
testPieceBitboard(whitePawns, whitePawnSquares)
|
|
||||||
testPieceBitboard(whiteKnights, whiteKnightSquares)
|
|
||||||
testPieceBitboard(whiteBishops, whiteBishopSquares)
|
|
||||||
testPieceBitboard(whiteRooks, whiteRookSquares)
|
|
||||||
testPieceBitboard(whiteQueens, whiteQueenSquares)
|
|
||||||
testPieceBitboard(whiteKing, whiteKingSquares)
|
|
||||||
testPieceBitboard(blackPawns, blackPawnSquares)
|
|
||||||
testPieceBitboard(blackKnights, blackKnightSquares)
|
|
||||||
testPieceBitboard(blackBishops, blackBishopSquares)
|
|
||||||
testPieceBitboard(blackRooks, blackRookSquares)
|
|
||||||
testPieceBitboard(blackQueens, blackQueenSquares)
|
|
||||||
testPieceBitboard(blackKing, blackKingSquares)
|
|
||||||
|
|
||||||
|
|
||||||
setControlCHook(proc () {.noconv.} = quit(0))
|
setControlCHook(proc () {.noconv.} = quit(0))
|
||||||
|
|
||||||
import tui
|
quit(commandLoop())
|
||||||
|
|
||||||
quit(tui.commandLoop())
|
|
||||||
|
|
|
@ -132,7 +132,7 @@ var
|
||||||
BISHOP_MOVES: array[64, seq[Bitboard]]
|
BISHOP_MOVES: array[64, seq[Bitboard]]
|
||||||
|
|
||||||
|
|
||||||
proc getRookMoves*(square: Square, blockers: Bitboard): Bitboard =
|
proc getRookMoves*(square: Square, blockers: Bitboard): Bitboard =
|
||||||
## Returns the move bitboard for the rook at the given
|
## Returns the move bitboard for the rook at the given
|
||||||
## square with the given blockers bitboard
|
## square with the given blockers bitboard
|
||||||
let
|
let
|
||||||
|
@ -141,7 +141,7 @@ proc getRookMoves*(square: Square, blockers: Bitboard): Bitboard =
|
||||||
return moves[getIndex(magic, blockers)]
|
return moves[getIndex(magic, blockers)]
|
||||||
|
|
||||||
|
|
||||||
proc getBishopMoves*(square: Square, blockers: Bitboard): Bitboard =
|
proc getBishopMoves*(square: Square, blockers: Bitboard): Bitboard =
|
||||||
## Returns the move bitboard for the bishop at the given
|
## Returns the move bitboard for the bishop at the given
|
||||||
## square with the given blockers bitboard
|
## square with the given blockers bitboard
|
||||||
let
|
let
|
||||||
|
@ -159,7 +159,7 @@ const
|
||||||
BISHOP_BLOCKERS = generateBishopBlockers()
|
BISHOP_BLOCKERS = generateBishopBlockers()
|
||||||
|
|
||||||
|
|
||||||
func getRelevantBlockers*(kind: PieceKind, square: Square): Bitboard =
|
func getRelevantBlockers*(kind: PieceKind, square: Square): Bitboard {.inline.} =
|
||||||
## Returns the relevant blockers mask for the given piece
|
## Returns the relevant blockers mask for the given piece
|
||||||
## type at the given square
|
## type at the given square
|
||||||
case kind:
|
case kind:
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
import ../nimfish
|
||||||
|
|
||||||
|
import std/strformat
|
||||||
|
|
||||||
|
|
||||||
|
proc testPiece(piece: Piece, kind: PieceKind, color: PieceColor) =
|
||||||
|
doAssert piece.kind == kind and piece.color == color, &"expected piece of kind {kind} and color {color}, got {piece.kind} / {piece.color} instead"
|
||||||
|
|
||||||
|
proc testPieceCount(board: ChessBoard, kind: PieceKind, color: PieceColor, count: int) =
|
||||||
|
let pieces = board.countPieces(kind, color)
|
||||||
|
doAssert pieces == count, &"expected {count} pieces of kind {kind} and color {color}, got {pieces} instead"
|
||||||
|
|
||||||
|
proc testPieceBitboard(bitboard: Bitboard, squares: seq[Square]) =
|
||||||
|
var i = 0
|
||||||
|
for square in bitboard:
|
||||||
|
doAssert squares[i] == square, &"squares[{i}] != bitboard[i]: {squares[i]} != {square}"
|
||||||
|
inc(i)
|
||||||
|
if i != squares.len():
|
||||||
|
doAssert false, &"bitboard.len() ({i}) != squares.len() ({squares.len()})"
|
||||||
|
|
||||||
|
|
||||||
|
proc basicTests* =
|
||||||
|
var b = newDefaultChessboard()
|
||||||
|
# Ensure correct number of pieces
|
||||||
|
testPieceCount(b, Pawn, White, 8)
|
||||||
|
testPieceCount(b, Pawn, Black, 8)
|
||||||
|
testPieceCount(b, Knight, White, 2)
|
||||||
|
testPieceCount(b, Knight, Black, 2)
|
||||||
|
testPieceCount(b, Bishop, White, 2)
|
||||||
|
testPieceCount(b, Bishop, Black, 2)
|
||||||
|
testPieceCount(b, Rook, White, 2)
|
||||||
|
testPieceCount(b, Rook, Black, 2)
|
||||||
|
testPieceCount(b, Queen, White, 1)
|
||||||
|
testPieceCount(b, Queen, Black, 1)
|
||||||
|
testPieceCount(b, King, White, 1)
|
||||||
|
testPieceCount(b, King, Black, 1)
|
||||||
|
|
||||||
|
# Ensure pieces are in the correct squares. This is testing the FEN
|
||||||
|
# parser
|
||||||
|
|
||||||
|
# Pawns
|
||||||
|
for loc in ["a2", "b2", "c2", "d2", "e2", "f2", "g2", "h2"]:
|
||||||
|
testPiece(b.getPiece(loc), Pawn, White)
|
||||||
|
for loc in ["a7", "b7", "c7", "d7", "e7", "f7", "g7", "h7"]:
|
||||||
|
testPiece(b.getPiece(loc), Pawn, Black)
|
||||||
|
# Rooks
|
||||||
|
testPiece(b.getPiece("a1"), Rook, White)
|
||||||
|
testPiece(b.getPiece("h1"), Rook, White)
|
||||||
|
testPiece(b.getPiece("a8"), Rook, Black)
|
||||||
|
testPiece(b.getPiece("h8"), Rook, Black)
|
||||||
|
# Knights
|
||||||
|
testPiece(b.getPiece("b1"), Knight, White)
|
||||||
|
testPiece(b.getPiece("g1"), Knight, White)
|
||||||
|
testPiece(b.getPiece("b8"), Knight, Black)
|
||||||
|
testPiece(b.getPiece("g8"), Knight, Black)
|
||||||
|
# Bishops
|
||||||
|
testPiece(b.getPiece("c1"), Bishop, White)
|
||||||
|
testPiece(b.getPiece("f1"), Bishop, White)
|
||||||
|
testPiece(b.getPiece("c8"), Bishop, Black)
|
||||||
|
testPiece(b.getPiece("f8"), Bishop, Black)
|
||||||
|
# Kings
|
||||||
|
testPiece(b.getPiece("e1"), King, White)
|
||||||
|
testPiece(b.getPiece("e8"), King, Black)
|
||||||
|
# Queens
|
||||||
|
testPiece(b.getPiece("d1"), Queen, White)
|
||||||
|
testPiece(b.getPiece("d8"), Queen, Black)
|
||||||
|
|
||||||
|
# Ensure our bitboards match with the board
|
||||||
|
let
|
||||||
|
whitePawns = b.getBitboard(Pawn, White)
|
||||||
|
whiteKnights = b.getBitboard(Knight, White)
|
||||||
|
whiteBishops = b.getBitboard(Bishop, White)
|
||||||
|
whiteRooks = b.getBitboard(Rook, White)
|
||||||
|
whiteQueens = b.getBitboard(Queen, White)
|
||||||
|
whiteKing = b.getBitboard(King, White)
|
||||||
|
blackPawns = b.getBitboard(Pawn, Black)
|
||||||
|
blackKnights = b.getBitboard(Knight, Black)
|
||||||
|
blackBishops = b.getBitboard(Bishop, Black)
|
||||||
|
blackRooks = b.getBitboard(Rook, Black)
|
||||||
|
blackQueens = b.getBitboard(Queen, Black)
|
||||||
|
blackKing = b.getBitboard(King, Black)
|
||||||
|
whitePawnSquares = @[makeSquare(6'i8, 0'i8), makeSquare(6, 1), makeSquare(6, 2), makeSquare(6, 3), makeSquare(6, 4), makeSquare(6, 5), makeSquare(6, 6), makeSquare(6, 7)]
|
||||||
|
whiteKnightSquares = @[makeSquare(7'i8, 1'i8), makeSquare(7, 6)]
|
||||||
|
whiteBishopSquares = @[makeSquare(7'i8, 2'i8), makeSquare(7, 5)]
|
||||||
|
whiteRookSquares = @[makeSquare(7'i8, 0'i8), makeSquare(7, 7)]
|
||||||
|
whiteQueenSquares = @[makeSquare(7'i8, 3'i8)]
|
||||||
|
whiteKingSquares = @[makeSquare(7'i8, 4'i8)]
|
||||||
|
blackPawnSquares = @[makeSquare(1'i8, 0'i8), makeSquare(1, 1), makeSquare(1, 2), makeSquare(1, 3), makeSquare(1, 4), makeSquare(1, 5), makeSquare(1, 6), makeSquare(1, 7)]
|
||||||
|
blackKnightSquares = @[makeSquare(0'i8, 1'i8), makeSquare(0, 6)]
|
||||||
|
blackBishopSquares = @[makeSquare(0'i8, 2'i8), makeSquare(0, 5)]
|
||||||
|
blackRookSquares = @[makeSquare(0'i8, 0'i8), makeSquare(0, 7)]
|
||||||
|
blackQueenSquares = @[makeSquare(0'i8, 3'i8)]
|
||||||
|
blackKingSquares = @[makeSquare(0'i8, 4'i8)]
|
||||||
|
|
||||||
|
|
||||||
|
testPieceBitboard(whitePawns, whitePawnSquares)
|
||||||
|
testPieceBitboard(whiteKnights, whiteKnightSquares)
|
||||||
|
testPieceBitboard(whiteBishops, whiteBishopSquares)
|
||||||
|
testPieceBitboard(whiteRooks, whiteRookSquares)
|
||||||
|
testPieceBitboard(whiteQueens, whiteQueenSquares)
|
||||||
|
testPieceBitboard(whiteKing, whiteKingSquares)
|
||||||
|
testPieceBitboard(blackPawns, blackPawnSquares)
|
||||||
|
testPieceBitboard(blackKnights, blackKnightSquares)
|
||||||
|
testPieceBitboard(blackBishops, blackBishopSquares)
|
||||||
|
testPieceBitboard(blackRooks, blackRookSquares)
|
||||||
|
testPieceBitboard(blackQueens, blackQueenSquares)
|
||||||
|
testPieceBitboard(blackKing, blackKingSquares)
|
|
@ -9,8 +9,8 @@ type
|
||||||
|
|
||||||
PieceColor* = enum
|
PieceColor* = enum
|
||||||
## A piece color enumeration
|
## A piece color enumeration
|
||||||
None = 0'i8,
|
None = 0'i8
|
||||||
White,
|
White
|
||||||
Black
|
Black
|
||||||
|
|
||||||
PieceKind* = enum
|
PieceKind* = enum
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import nimfish
|
import ../nimfish
|
||||||
|
|
||||||
|
|
||||||
import std/strformat
|
import std/strformat
|
||||||
|
@ -20,7 +20,7 @@ proc perft*(self: ChessBoard, ply: int, verbose: bool = false, divide: bool = fa
|
||||||
var moves = MoveList()
|
var moves = MoveList()
|
||||||
self.generateMoves(moves)
|
self.generateMoves(moves)
|
||||||
if not bulk:
|
if not bulk:
|
||||||
if len(moves) == 0 and self.inCheck(self.getSideToMove()):
|
if len(moves) == 0 and self.inCheck():
|
||||||
result.checkmates = 1
|
result.checkmates = 1
|
||||||
# TODO: Should we count stalemates/draws?
|
# TODO: Should we count stalemates/draws?
|
||||||
if ply == 0:
|
if ply == 0:
|
||||||
|
@ -54,7 +54,7 @@ proc perft*(self: ChessBoard, ply: int, verbose: bool = false, divide: bool = fa
|
||||||
echo &"Turn: {self.getSideToMove()}"
|
echo &"Turn: {self.getSideToMove()}"
|
||||||
echo &"Piece: {self.getPiece(move.startSquare).kind}"
|
echo &"Piece: {self.getPiece(move.startSquare).kind}"
|
||||||
echo &"Flags: {move.getFlags()}"
|
echo &"Flags: {move.getFlags()}"
|
||||||
echo &"In check: {(if self.inCheck(self.getSideToMove()): \"yes\" else: \"no\")}"
|
echo &"In check: {(if self.inCheck(): \"yes\" else: \"no\")}"
|
||||||
echo &"Can castle:\n - King side: {(if canCastle.king: \"yes\" else: \"no\")}\n - Queen side: {(if canCastle.queen: \"yes\" else: \"no\")}"
|
echo &"Can castle:\n - King side: {(if canCastle.king: \"yes\" else: \"no\")}\n - Queen side: {(if canCastle.queen: \"yes\" else: \"no\")}"
|
||||||
echo &"Position before move: {self.toFEN()}"
|
echo &"Position before move: {self.toFEN()}"
|
||||||
stdout.write("En Passant target: ")
|
stdout.write("En Passant target: ")
|
||||||
|
@ -73,13 +73,13 @@ proc perft*(self: ChessBoard, ply: int, verbose: bool = false, divide: bool = fa
|
||||||
inc(result.promotions)
|
inc(result.promotions)
|
||||||
if move.isEnPassant():
|
if move.isEnPassant():
|
||||||
inc(result.enPassant)
|
inc(result.enPassant)
|
||||||
if self.inCheck(self.getSideToMove()):
|
if self.inCheck():
|
||||||
# Opponent king is in check
|
# Opponent king is in check
|
||||||
inc(result.checks)
|
inc(result.checks)
|
||||||
if verbose:
|
if verbose:
|
||||||
let canCastle = self.canCastle(self.getSideToMove())
|
let canCastle = self.canCastle(self.getSideToMove())
|
||||||
echo "\n"
|
echo "\n"
|
||||||
echo &"Opponent in check: {(if self.inCheck(self.getSideToMove()): \"yes\" else: \"no\")}"
|
echo &"Opponent in check: {(if self.inCheck(): \"yes\" else: \"no\")}"
|
||||||
echo &"Opponent can castle:\n - King side: {(if canCastle.king: \"yes\" else: \"no\")}\n - Queen side: {(if canCastle.queen: \"yes\" else: \"no\")}"
|
echo &"Opponent can castle:\n - King side: {(if canCastle.king: \"yes\" else: \"no\")}\n - Queen side: {(if canCastle.queen: \"yes\" else: \"no\")}"
|
||||||
echo &"Position after move: {self.toFEN()}"
|
echo &"Position after move: {self.toFEN()}"
|
||||||
echo "\n", self.pretty()
|
echo "\n", self.pretty()
|
||||||
|
@ -420,7 +420,7 @@ proc commandLoop*: int =
|
||||||
let canCastle = board.canCastle(board.getSideToMove())
|
let canCastle = board.canCastle(board.getSideToMove())
|
||||||
echo &"Castling rights for {($board.getSideToMove()).toLowerAscii()}:\n - King side: {(if canCastle.king: \"yes\" else: \"no\")}\n - Queen side: {(if canCastle.queen: \"yes\" else: \"no\")}"
|
echo &"Castling rights for {($board.getSideToMove()).toLowerAscii()}:\n - King side: {(if canCastle.king: \"yes\" else: \"no\")}\n - Queen side: {(if canCastle.queen: \"yes\" else: \"no\")}"
|
||||||
of "check":
|
of "check":
|
||||||
echo &"{board.getSideToMove()} king in check: {(if board.inCheck(board.getSideToMove()): \"yes\" else: \"no\")}"
|
echo &"{board.getSideToMove()} king in check: {(if board.inCheck(): \"yes\" else: \"no\")}"
|
||||||
else:
|
else:
|
||||||
echo &"Unknown command '{cmd[0]}'. Type 'help' for more information."
|
echo &"Unknown command '{cmd[0]}'. Type 'help' for more information."
|
||||||
except IOError:
|
except IOError:
|
Loading…
Reference in New Issue