Switch to better mechanism to keep track of pins
This commit is contained in:
parent
b0ebdc02a6
commit
1a89b437fa
|
@ -19,16 +19,7 @@ import std/strutils
|
|||
import std/strformat
|
||||
import std/sequtils
|
||||
|
||||
|
||||
type
|
||||
# Useful type aliases
|
||||
Location* = tuple[row, col: int8]
|
||||
|
||||
Attacked = seq[tuple[source, dest: Location]]
|
||||
|
||||
Pieces = tuple[king: Location, queens: seq[Location], rooks: seq[Location],
|
||||
bishops: seq[Location], knights: seq[Location],
|
||||
pawns: seq[Location]]
|
||||
|
||||
PieceColor* = enum
|
||||
## A piece color enumeration
|
||||
|
@ -38,7 +29,7 @@ type
|
|||
|
||||
PieceKind* = enum
|
||||
## A chess piece enumeration
|
||||
Empty = '\0', # No piece
|
||||
Empty = 0'i8, # No piece
|
||||
Bishop = 'b',
|
||||
King = 'k'
|
||||
Knight = 'n',
|
||||
|
@ -60,13 +51,24 @@ type
|
|||
# Castling metadata
|
||||
CastleLong,
|
||||
CastleShort,
|
||||
XRay, # Move is an X-ray attack
|
||||
# Pawn promotion metadata
|
||||
PromoteToQueen,
|
||||
PromoteToRook,
|
||||
PromoteToBishop,
|
||||
PromoteToKnight
|
||||
|
||||
# Useful type aliases
|
||||
Location* = tuple[row, col: int8]
|
||||
|
||||
Attacked = seq[tuple[source, target: Location]]
|
||||
Pinned = seq[tuple[source, target, direction: Location]]
|
||||
|
||||
Pieces = tuple[king: Location, queens: seq[Location], rooks: seq[Location],
|
||||
bishops: seq[Location], knights: seq[Location],
|
||||
pawns: seq[Location]]
|
||||
|
||||
CountData = tuple[nodes: uint64, captures: uint64, castles: uint64, checks: uint64, promotions: uint64, enPassant: uint64, checkmates: uint64]
|
||||
|
||||
Move* = object
|
||||
## A chess move
|
||||
piece*: Piece
|
||||
|
@ -91,13 +93,12 @@ type
|
|||
# every 2 ply
|
||||
fullMoveCount: int16
|
||||
# En passant target square (see https://en.wikipedia.org/wiki/En_passant)
|
||||
# If en passant is not possible, both the row and
|
||||
# column of the position will be set to -1
|
||||
enPassantSquare*: Move
|
||||
# Locations of all pieces
|
||||
pieces: tuple[white: Pieces, black: Pieces]
|
||||
# Potential attacking moves for black and white
|
||||
attacked: tuple[white: Attacked, black: Attacked]
|
||||
pinned: tuple[white: Pinned, black: Pinned]
|
||||
# Has any piece been captured to reach this position?
|
||||
captured: Piece
|
||||
# Active color
|
||||
|
@ -127,6 +128,8 @@ proc makeMove*(self: ChessBoard, move: Move): Move {.discardable.}
|
|||
proc makeMove*(self: ChessBoard, startSquare, targetSquare: Location): Move {.discardable.}
|
||||
func emptyMove*: Move {.inline.} = Move(startSquare: emptyLocation(), targetSquare: emptyLocation(), piece: emptyPiece())
|
||||
func `+`*(a, b: Location): Location = (a.row + b.row, a.col + b.col)
|
||||
func `-`*(a: Location): Location = (-a.row, -a.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
|
||||
proc generateMoves(self: ChessBoard, location: Location): seq[Move]
|
||||
proc isAttacked*(self: ChessBoard, loc: Location, color: PieceColor = None): bool
|
||||
|
@ -136,6 +139,13 @@ proc doMove(self: ChessBoard, move: Move)
|
|||
proc pretty*(self: ChessBoard): string
|
||||
proc spawnPiece(self: ChessBoard, location: Location, piece: Piece)
|
||||
proc updateAttackedSquares(self: ChessBoard)
|
||||
proc getPinnedDirections(self: ChessBoard, loc: Location): seq[Location]
|
||||
|
||||
|
||||
proc extend[T](self: var seq[T], other: openarray[T]) {.inline.} =
|
||||
for x in other:
|
||||
self.add(x)
|
||||
|
||||
|
||||
# Due to our board layout, directions of movement are reversed for white/black so
|
||||
# we need these helpers to avoid going mad with integer tuples and minus signs
|
||||
|
@ -146,7 +156,7 @@ func bottomLeftDiagonal(color: PieceColor): Location {.inline.} = (if color == W
|
|||
func bottomRightDiagonal(color: PieceColor): Location {.inline.} = (if color == White: (1, 1) else: (-1, -1))
|
||||
func leftSide(color: PieceColor): Location {.inline.} = (if color == White: (0, -1) else: (0, 1))
|
||||
func rightSide(color: PieceColor): Location {.inline.} = (if color == White: (0, 1) else: (0, -1))
|
||||
func topSide(color: PieceColor): Location {.inline.} = (-1, 0)
|
||||
func topSide(color: PieceColor): Location {.inline.} = (if color == White: (-1, 0) else: (1, 0))
|
||||
func bottomSide(color: PieceColor): Location {.inline.} = (if color == White: (1, 0) else: (-1, 0))
|
||||
func forward(color: PieceColor): Location {.inline.} = (if color == White: (-1, 0) else: (1, 0))
|
||||
func doublePush(color: PieceColor): Location {.inline.} = (if color == White: (-2, 0) else: (2, 0))
|
||||
|
@ -275,7 +285,19 @@ proc newChessboard: ChessBoard =
|
|||
enPassantSquare: emptyMove(),
|
||||
move: emptyMove(),
|
||||
turn: White,
|
||||
fullMoveCount: 1)
|
||||
fullMoveCount: 1,
|
||||
pieces: (white: (king: emptyLocation(),
|
||||
queens: @[],
|
||||
rooks: @[],
|
||||
bishops: @[],
|
||||
knights: @[],
|
||||
pawns: @[]),
|
||||
black: (king: emptyLocation(),
|
||||
queens: @[],
|
||||
rooks: @[],
|
||||
bishops: @[],
|
||||
knights: @[],
|
||||
pawns: @[])))
|
||||
|
||||
|
||||
|
||||
|
@ -532,7 +554,6 @@ func getPiece*(self: ChessBoard, square: string): Piece =
|
|||
return self.getPiece(square.algebraicToLocation())
|
||||
|
||||
|
||||
|
||||
func isCapture*(move: Move): bool {.inline.} =
|
||||
## Returns whether the given move is a capture
|
||||
## or not
|
||||
|
@ -555,9 +576,9 @@ proc inCheck*(self: ChessBoard, color: PieceColor = None): bool =
|
|||
color = self.getActiveColor()
|
||||
case color:
|
||||
of White:
|
||||
return self.isAttacked(self.position.pieces.white.king, color)
|
||||
return self.isAttacked(self.position.pieces.white.king, Black)
|
||||
of Black:
|
||||
return self.isAttacked(self.position.pieces.black.king, color)
|
||||
return self.isAttacked(self.position.pieces.black.king, White)
|
||||
else:
|
||||
# Unreachable
|
||||
discard
|
||||
|
@ -608,38 +629,40 @@ proc canCastle*(self: ChessBoard, color: PieceColor = None): tuple[queen, king:
|
|||
|
||||
if result.king:
|
||||
# Short castle
|
||||
var location = loc
|
||||
var
|
||||
location = loc
|
||||
otherPiece: Piece
|
||||
while true:
|
||||
location = location + kingSide
|
||||
if not location.isValid():
|
||||
break
|
||||
if self.grid[location.row, location.col].color == color:
|
||||
# Blocked by own piece
|
||||
otherPiece = self.grid[location.row, location.col]
|
||||
|
||||
if otherPiece.color == None:
|
||||
continue
|
||||
|
||||
if otherPiece.color == color.opposite() or otherPiece.kind != Rook or self.isAttacked(location, color.opposite()):
|
||||
result.king = false
|
||||
break
|
||||
# Square is attacked or blocked by enemy piece
|
||||
if self.isAttacked(location, color) or self.grid[location.row, location.col].color != None:
|
||||
result.king = false
|
||||
break
|
||||
# Square is occupied by our rook: we're done. No need to check the color or type of it (because
|
||||
# if it weren't the right color, castling rights would've already been lost and we wouldn't
|
||||
# have got this far)
|
||||
if location == color.kingSideRook():
|
||||
break
|
||||
# Square is empty and not attacked. Keep going
|
||||
|
||||
if result.queen:
|
||||
# Long castle
|
||||
var location = loc
|
||||
var
|
||||
location = loc
|
||||
otherPiece: Piece
|
||||
while true:
|
||||
location = location + queenSide
|
||||
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
|
||||
otherPiece = self.grid[location.row, location.col]
|
||||
|
||||
if otherPiece.color == None:
|
||||
continue
|
||||
|
||||
if otherPiece.color == color.opposite() or otherPiece.kind != Rook or self.isAttacked(location, color.opposite()):
|
||||
result.king = false
|
||||
break
|
||||
if location == color.queenSideRook():
|
||||
break
|
||||
|
@ -684,6 +707,17 @@ proc generatePawnMoves(self: ChessBoard, location: Location): seq[Move] =
|
|||
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)
|
||||
flags.add(Capture)
|
||||
var
|
||||
newLocation: Location
|
||||
newLocations: seq[Location]
|
||||
let pins = self.getPinnedDirections(location)
|
||||
for pin in pins:
|
||||
newLocation = location + pin
|
||||
# Pin direction is legal
|
||||
if newLocation in locations:
|
||||
newLocations.add(newLocation)
|
||||
if pins.len() > 0:
|
||||
locations = newLocations
|
||||
|
||||
var targetPiece: Piece
|
||||
for (target, flag) in zip(locations, flags):
|
||||
|
@ -698,10 +732,10 @@ proc generatePawnMoves(self: ChessBoard, location: Location): seq[Move] =
|
|||
|
||||
proc generateSlidingMoves(self: ChessBoard, location: Location): seq[Move] =
|
||||
## Generates moves for the sliding piece in the given location
|
||||
var
|
||||
piece = self.grid[location.row, location.col]
|
||||
let piece = self.grid[location.row, location.col]
|
||||
doAssert piece.kind in [Bishop, Rook, Queen], &"generateSlidingMoves called on a {piece.kind}"
|
||||
var directions: seq[Location] = @[]
|
||||
|
||||
# Only check in the right directions for the chosen piece
|
||||
if piece.kind in [Bishop, Queen]:
|
||||
directions.add(piece.color.topLeftDiagonal())
|
||||
|
@ -713,6 +747,11 @@ proc generateSlidingMoves(self: ChessBoard, location: Location): seq[Move] =
|
|||
directions.add(piece.color.bottomSide())
|
||||
directions.add(piece.color.rightSide())
|
||||
directions.add(piece.color.leftSide())
|
||||
let pinned = self.getPinnedDirections(location)
|
||||
if pinned.len() > 0:
|
||||
# If a sliding piece is pinned then it can only
|
||||
# move along the pinning direction(s)
|
||||
directions = pinned
|
||||
for direction in directions:
|
||||
# Slide in this direction as long as it's possible
|
||||
var
|
||||
|
@ -795,6 +834,11 @@ proc generateKnightMoves(self: ChessBoard, location: Location): seq[Move] =
|
|||
piece.color.bottomRightKnightMove(long=false),
|
||||
piece.color.topLeftKnightMove(long=false),
|
||||
piece.color.topRightKnightMove(long=false)]
|
||||
let pinned = self.getPinnedDirections(location)
|
||||
if pinned.len() > 0:
|
||||
# Knight is pinned: can't move!
|
||||
return @[]
|
||||
|
||||
for direction in directions:
|
||||
# Jump to this square
|
||||
let square: Location = location + direction
|
||||
|
@ -851,11 +895,11 @@ proc getAttackers*(self: ChessBoard, square: Location, color = None): seq[Locati
|
|||
case color:
|
||||
of White:
|
||||
for attack in self.position.attacked.black:
|
||||
if attack.dest == square:
|
||||
if attack.target == square:
|
||||
result.add(attack.source)
|
||||
of Black:
|
||||
for attack in self.position.attacked.white:
|
||||
if attack.dest == square:
|
||||
if attack.target == square:
|
||||
result.add(attack.source)
|
||||
else:
|
||||
# Unreachable
|
||||
|
@ -866,18 +910,18 @@ proc getAttackers*(self: ChessBoard, square: Location, color = None): seq[Locati
|
|||
# getAttackers)
|
||||
proc isAttacked*(self: ChessBoard, loc: Location, color: PieceColor = None): bool =
|
||||
## Returns whether the given location is attacked
|
||||
## by the given opponent
|
||||
## by the given color
|
||||
var color = color
|
||||
if color == None:
|
||||
color = self.getActiveColor()
|
||||
color = self.getActiveColor().opposite()
|
||||
case color:
|
||||
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:
|
||||
if attack.target == loc:
|
||||
return true
|
||||
of White:
|
||||
for attack in self.position.attacked.white:
|
||||
if attack.target == loc:
|
||||
return true
|
||||
of None:
|
||||
discard
|
||||
|
@ -885,12 +929,12 @@ proc isAttacked*(self: ChessBoard, loc: Location, color: PieceColor = None): boo
|
|||
|
||||
proc isAttacked*(self: ChessBoard, square: string): bool =
|
||||
## Returns whether the given square is attacked
|
||||
## by its opponent
|
||||
## by the current
|
||||
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():
|
||||
func addAttack(self: ChessBoard, attack: tuple[source, target: Location], color: PieceColor) {.inline.} =
|
||||
if attack.source.isValid() and attack.target.isValid():
|
||||
case color:
|
||||
of White:
|
||||
self.position.attacked.white.add(attack)
|
||||
|
@ -900,18 +944,33 @@ func addAttack(self: ChessBoard, attack: tuple[source, dest: Location], color: P
|
|||
discard
|
||||
|
||||
|
||||
proc updatePawnAttacks(self: ChessBoard) =
|
||||
proc getPinnedDirections(self: ChessBoard, loc: Location): seq[Location] =
|
||||
let piece = self.grid[loc.row, loc.col]
|
||||
case piece.color:
|
||||
of None:
|
||||
discard
|
||||
of White:
|
||||
for pin in self.position.pinned.black:
|
||||
if pin.target == loc:
|
||||
result.add(pin.direction)
|
||||
of Black:
|
||||
for pin in self.position.pinned.white:
|
||||
if pin.target == loc:
|
||||
result.add(pin.direction)
|
||||
|
||||
|
||||
proc updatePawnAttacks(self: ChessBoard) {.thread.} =
|
||||
## Internal helper of updateAttackedSquares
|
||||
for loc in self.position.pieces.white.pawns:
|
||||
# Pawns are special in how they capture (i.e. the
|
||||
# squares they can move to do not match the squares
|
||||
# they can capture on. Sneaky fucks)
|
||||
self.addAttack((loc, loc + White.topRightDiagonal()), White)
|
||||
self.addAttack((loc, loc + White.topRightDiagonal()), White)
|
||||
self.addAttack((loc, loc + White.topLeftDiagonal()), White)
|
||||
# We do the same thing for black
|
||||
for loc in self.position.pieces.black.pawns:
|
||||
self.addAttack((loc, loc + Black.topRightDiagonal()), Black)
|
||||
self.addAttack((loc, loc + Black.topRightDiagonal()), Black)
|
||||
self.addAttack((loc, loc + Black.topLeftDiagonal()), Black)
|
||||
|
||||
|
||||
proc updateKingAttacks(self: ChessBoard) =
|
||||
|
@ -951,25 +1010,27 @@ proc updateKnightAttacks(self: ChessBoard) =
|
|||
self.addAttack((loc, loc + Black.bottomRightKnightMove(long=false)), Black)
|
||||
|
||||
|
||||
proc getSlidingAttacks(self: ChessBoard, loc: Location): Attacked =
|
||||
proc getSlidingAttacks(self: ChessBoard, loc: Location): tuple[attacks: Attacked, pins: Pinned] =
|
||||
## Internal helper of updateSlidingAttacks
|
||||
var
|
||||
directions: seq[Location] = @[]
|
||||
square: Location = loc
|
||||
otherPiece: Piece
|
||||
let piece = self.grid[loc.row, loc.col]
|
||||
if piece.kind in [Bishop, Queen]:
|
||||
directions.add(piece.color.topLeftDiagonal())
|
||||
directions.add(piece.color.topRightDiagonal())
|
||||
directions.add(piece.color.bottomLeftDiagonal())
|
||||
directions.add(piece.color.bottomRightDiagonal())
|
||||
|
||||
if piece.kind in [Queen, Rook]:
|
||||
directions.add(piece.color.topSide())
|
||||
directions.add(piece.color.bottomSide())
|
||||
directions.add(piece.color.rightSide())
|
||||
directions.add(piece.color.leftSide())
|
||||
|
||||
for direction in directions:
|
||||
var
|
||||
square = loc
|
||||
otherPiece: Piece
|
||||
# Slide in this direction as long as it's possible
|
||||
while true:
|
||||
square = square + direction
|
||||
|
@ -977,46 +1038,71 @@ proc getSlidingAttacks(self: ChessBoard, loc: Location): Attacked =
|
|||
if not square.isValid():
|
||||
break
|
||||
otherPiece = self.grid[square.row, square.col]
|
||||
# A piece is in the way: we cannot proceed
|
||||
# any further
|
||||
if otherPiece.color notin [piece.color.opposite(), None]:
|
||||
# Target square is attacked (even if a friendly piece
|
||||
# is present, because in this case we're defending
|
||||
# it)
|
||||
result.attacks.add((loc, square))
|
||||
# Empty square, keep going
|
||||
if otherPiece.color == None:
|
||||
continue
|
||||
if otherPiece.color == piece.color.opposite and otherPiece.kind != King:
|
||||
# We found an enemy piece that is not
|
||||
# the enemy king. We don't break out
|
||||
# immediately because we first want
|
||||
# to check if we've pinned a piece
|
||||
var
|
||||
otherSquare: Location = square
|
||||
behindPiece: Piece
|
||||
while true:
|
||||
otherSquare = otherSquare + direction
|
||||
if not otherSquare.isValid():
|
||||
break
|
||||
behindPiece = self.grid[otherSquare.row, otherSquare.col]
|
||||
if behindPiece.color == None:
|
||||
continue
|
||||
if behindPiece.color == piece.color.opposite and behindPiece.kind == King:
|
||||
# The enemy king is behind this enemy piece: pin it in
|
||||
# this direction relative to them (that's why we have the
|
||||
# minus sign: up for us is down for them and vice versa)
|
||||
result.pins.add((loc, square, -direction))
|
||||
else:
|
||||
break
|
||||
break
|
||||
# Target square is attacked
|
||||
result.add((loc, square))
|
||||
|
||||
|
||||
proc updateSlidingAttacks(self: ChessBoard) =
|
||||
## Internal helper of updateAttackedSquares
|
||||
|
||||
var
|
||||
directions: seq[Location]
|
||||
piece: Piece
|
||||
# Bishops
|
||||
var data: tuple[attacks: Attacked, pins: Pinned]
|
||||
for loc in self.position.pieces.white.bishops:
|
||||
for attack in self.getSlidingAttacks(loc):
|
||||
self.addAttack(attack, White)
|
||||
for loc in self.position.pieces.black.bishops:
|
||||
for attack in self.getSlidingAttacks(loc):
|
||||
self.addAttack(attack, Black)
|
||||
# Rooks
|
||||
data = self.getSlidingAttacks(loc)
|
||||
self.position.attacked.white.extend(data.attacks)
|
||||
self.position.pinned.white.extend(data.pins)
|
||||
for loc in self.position.pieces.white.rooks:
|
||||
for attack in self.getSlidingAttacks(loc):
|
||||
self.addAttack(attack, White)
|
||||
for loc in self.position.pieces.black.rooks:
|
||||
for attack in self.getSlidingAttacks(loc):
|
||||
self.addAttack(attack, Black)
|
||||
# Queens
|
||||
data = self.getSlidingAttacks(loc)
|
||||
self.position.attacked.white.extend(data.attacks)
|
||||
self.position.pinned.white.extend(data.pins)
|
||||
for loc in self.position.pieces.white.queens:
|
||||
for attack in self.getSlidingAttacks(loc):
|
||||
self.addAttack(attack, White)
|
||||
data = self.getSlidingAttacks(loc)
|
||||
self.position.attacked.white.extend(data.attacks)
|
||||
self.position.pinned.white.extend(data.pins)
|
||||
for loc in self.position.pieces.black.bishops:
|
||||
data = self.getSlidingAttacks(loc)
|
||||
self.position.attacked.black.extend(data.attacks)
|
||||
self.position.pinned.black.extend(data.pins)
|
||||
for loc in self.position.pieces.black.rooks:
|
||||
data = self.getSlidingAttacks(loc)
|
||||
self.position.attacked.black.extend(data.attacks)
|
||||
self.position.pinned.black.extend(data.pins)
|
||||
for loc in self.position.pieces.black.queens:
|
||||
for attack in self.getSlidingAttacks(loc):
|
||||
self.addAttack(attack, Black)
|
||||
data = self.getSlidingAttacks(loc)
|
||||
self.position.attacked.black.extend(data.attacks)
|
||||
self.position.pinned.black.extend(data.pins)
|
||||
|
||||
|
||||
proc updateAttackedSquares(self: ChessBoard) =
|
||||
## Updates internal metadata about which squares
|
||||
## are attacked. Called internally by doMove
|
||||
## are attacked
|
||||
|
||||
self.position.attacked.white.setLen(0)
|
||||
self.position.attacked.black.setLen(0)
|
||||
|
@ -1024,6 +1110,7 @@ proc updateAttackedSquares(self: ChessBoard) =
|
|||
self.updatePawnAttacks()
|
||||
# Sliding pieces
|
||||
self.updateSlidingAttacks()
|
||||
# Knights
|
||||
self.updateKnightAttacks()
|
||||
# Kings
|
||||
self.updateKingAttacks()
|
||||
|
@ -1147,11 +1234,7 @@ proc updateLocations(self: ChessBoard, move: Move) =
|
|||
## the pieces on the board after a move
|
||||
if move.isCapture():
|
||||
self.position.captured = self.grid[move.targetSquare.row, move.targetSquare.col]
|
||||
try:
|
||||
self.removePiece(move.targetSquare, attack=false)
|
||||
except AssertionDefect:
|
||||
echo move
|
||||
raise
|
||||
|
||||
# Update the positional metadata of the moving piece
|
||||
self.movePiece(move)
|
||||
|
@ -1246,7 +1329,6 @@ proc doMove(self: ChessBoard, move: Move) =
|
|||
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
|
||||
|
@ -1493,16 +1575,35 @@ proc pretty*(self: ChessBoard): string =
|
|||
result &= "\x1b[0m"
|
||||
|
||||
|
||||
proc countLegalMoves*(self: ChessBoard, ply: int, verbose: bool = false, current: int = 0): tuple[nodes: int, captures: int, castles: int, checks: int, promotions: int] =
|
||||
proc perft*(self: ChessBoard, ply: int, verbose: bool = false): CountData =
|
||||
## Counts (and debugs) the number of legal positions reached after
|
||||
## the given number of ply
|
||||
var verbose = verbose
|
||||
if ply == 0:
|
||||
result = (1, 0, 0, 0, 0)
|
||||
result = (1, 0, 0, 0, 0, 0, 0)
|
||||
else:
|
||||
var
|
||||
before: string
|
||||
for move in self.generateAllMoves():
|
||||
before = self.pretty()
|
||||
let moves = self.generateAllMoves()
|
||||
if len(moves) == 0:
|
||||
inc(result.checkmates)
|
||||
for move in moves:
|
||||
if verbose:
|
||||
let canCastle = self.canCastle(move.piece.color)
|
||||
#echo "\x1Bc"
|
||||
echo &"Ply: {self.position.plyFromRoot}"
|
||||
echo &"Move: {move.startSquare.locationToAlgebraic()}{move.targetSquare.locationToAlgebraic()}, from ({move.startSquare.row}, {move.startSquare.col}) to ({move.targetSquare.row}, {move.targetSquare.col})"
|
||||
echo &"Turn: {move.piece.color}"
|
||||
echo &"Piece: {move.piece.kind}"
|
||||
echo &"Flag: {move.flag}"
|
||||
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 "Before:\n"
|
||||
echo self.pretty()
|
||||
try:
|
||||
discard readLine(stdin)
|
||||
except IOError:
|
||||
discard
|
||||
except EOFError:
|
||||
discard
|
||||
self.doMove(move)
|
||||
case move.flag:
|
||||
of Capture:
|
||||
|
@ -1511,41 +1612,30 @@ proc countLegalMoves*(self: ChessBoard, ply: int, verbose: bool = false, current
|
|||
inc(result.castles)
|
||||
of PromoteToBishop, PromoteToKnight, PromoteToQueen, PromoteToRook:
|
||||
inc(result.promotions)
|
||||
of EnPassant:
|
||||
inc(result.enPassant)
|
||||
else:
|
||||
discard
|
||||
if self.inCheck():
|
||||
# Opponent king is in check
|
||||
inc(result.checks)
|
||||
if verbose:
|
||||
let canCastle = self.canCastle(move.piece.color)
|
||||
echo "\x1Bc"
|
||||
echo &"Ply: {self.position.plyFromRoot} (move {current + result.nodes + 1})"
|
||||
echo &"Move: {move.startSquare.locationToAlgebraic()}{move.targetSquare.locationToAlgebraic()} (({move.startSquare.row}, {move.startSquare.col}) -> ({move.targetSquare.row}, {move.targetSquare.col}))"
|
||||
echo &"Turn: {move.piece.color}"
|
||||
echo &"Piece: {move.piece.kind}"
|
||||
echo &"Flag: {move.flag}"
|
||||
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 "Now:\n"
|
||||
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)
|
||||
let next = self.perft(ply - 1, verbose)
|
||||
result.nodes += next.nodes
|
||||
result.captures += next.captures
|
||||
result.checks += next.checks
|
||||
result.promotions += next.promotions
|
||||
result.castles += next.castles
|
||||
result.enPassant += next.enPassant
|
||||
result.checkmates += next.checkmates
|
||||
self.undoMove(move)
|
||||
|
||||
|
||||
|
@ -1607,6 +1697,6 @@ when isMainModule:
|
|||
when compileOption("profiler"):
|
||||
import nimprof
|
||||
|
||||
#b = newChessboardFromFEN("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - ")
|
||||
echo b.countLegalMoves(3, verbose=false)
|
||||
# b = newChessboardFromFEN("fen")
|
||||
echo b.perft(4, verbose=true)
|
||||
echo "All tests were successful"
|
Loading…
Reference in New Issue