Bug fixes(?)

This commit is contained in:
Mattia Giambirtone 2023-11-01 19:07:09 +01:00
parent 3dca208123
commit 6e10cbe925
2 changed files with 102 additions and 121 deletions

View File

@ -99,30 +99,19 @@ type
attacked: tuple[white: Attacked, black: Attacked] attacked: tuple[white: Attacked, black: Attacked]
# Pieces pinned by both sides # Pieces pinned by both sides
pinned: tuple[white: Attacked, black: Attacked] pinned: tuple[white: Attacked, black: Attacked]
# The original piece captured to reach this position (may be empty)
#captured: Piece
# The piece that moved to reach this position (needed to undo moves)
#moved: Piece
# Active color # Active color
turn: PieceColor turn: PieceColor
CacheEntry[T] = ref object
valid: bool
data: T
Cache = object
canCastle: tuple[white, black: CacheEntry[tuple[queen, king: bool]]]
inCheck: tuple[white, black: CacheEntry[bool]]
ChessBoard* = ref object ChessBoard* = ref object
## A chess board object ## A chess board object
# An 8x8 matrix we use for constant
# time lookup of pieces by their location
grid: Matrix[Piece] grid: Matrix[Piece]
# The current position
position: Position position: Position
# List of reached positions # List of all reached positions
positions: seq[Position] positions: seq[Position]
# Cached results of expensive
# functions in the current position
#cache: Cache
# Initialized only once, copied every time # Initialized only once, copied every time
@ -131,6 +120,8 @@ for _ in countup(0, 63):
empty.add(Piece(kind: Empty, color: None)) empty.add(Piece(kind: Empty, color: None))
# A bunch of simple utility functions
func emptyPiece*: Piece {.inline.} = Piece(kind: Empty, color: None) 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)
@ -153,7 +144,6 @@ proc updateAttackedSquares(self: ChessBoard)
proc getPinnedDirections(self: ChessBoard, loc: Location): seq[Location] proc getPinnedDirections(self: ChessBoard, loc: Location): seq[Location]
proc getAttacks*(self: ChessBoard, loc: Location): Attacked proc getAttacks*(self: ChessBoard, loc: Location): Attacked
proc getSlidingAttacks(self: ChessBoard, loc: Location): tuple[attacks: Attacked, pins: Attacked] proc getSlidingAttacks(self: ChessBoard, loc: Location): tuple[attacks: Attacked, pins: Attacked]
#[func invalidateCache(self: ChessBoard) {.inline.}]#
proc inCheck*(self: ChessBoard, color: PieceColor = None): bool proc inCheck*(self: ChessBoard, color: PieceColor = None): bool
@ -162,9 +152,8 @@ proc extend[T](self: var seq[T], other: openarray[T]) {.inline.} =
self.add(x) self.add(x)
# Due to our board layout, directions of movement are reversed for white/black so # Due to our board layout, directions of movement are reversed for white and black, so
# we need these helpers to avoid going mad with integer tuples and minus signs # we need these helpers to avoid going mad with integer tuples and minus signs everywhere
# everywhere
func topLeftDiagonal(color: PieceColor): Location {.inline.} = (if color == White: (-1, -1) else: (1, 1)) func topLeftDiagonal(color: PieceColor): Location {.inline.} = (if color == White: (-1, -1) else: (1, 1))
func topRightDiagonal(color: PieceColor): Location {.inline.} = (if color == White: (-1, 1) else: (1, -1)) func topRightDiagonal(color: PieceColor): Location {.inline.} = (if color == White: (-1, 1) else: (1, -1))
func bottomLeftDiagonal(color: PieceColor): Location {.inline.} = (if color == White: (1, -1) else: (-1, 1)) func bottomLeftDiagonal(color: PieceColor): Location {.inline.} = (if color == White: (1, -1) else: (-1, 1))
@ -173,16 +162,11 @@ func leftSide(color: PieceColor): Location {.inline.} = (if color == White: (0,
func rightSide(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.} = (if color == White: (-1, 0) else: (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 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)) func doublePush(color: PieceColor): Location {.inline.} = (if color == White: (-2, 0) else: (2, 0))
func longCastleKing: Location {.inline.} = (0, -2) func longCastleKing: Location {.inline.} = (0, -2)
func shortCastleKing: Location {.inline.} = (0, 2) func shortCastleKing: Location {.inline.} = (0, 2)
func longCastleRook: Location {.inline.} = (0, 3) func longCastleRook: Location {.inline.} = (0, 3)
func shortCastleRook: Location {.inline.} = (0, -2) func shortCastleRook: Location {.inline.} = (0, -2)
func kingSideRook(color: PieceColor): Location {.inline.} = (if color == White: (7, 7) else: (0, 7))
func queenSideRook(color: PieceColor): Location {.inline.} = (if color == White: (7, 0) else: (0, 0))
func bottomLeftKnightMove(color: PieceColor, long: bool = true): Location {.inline.} = func bottomLeftKnightMove(color: PieceColor, long: bool = true): Location {.inline.} =
if color == White: if color == White:
if long: if long:
@ -234,7 +218,12 @@ func topRightKnightMove(color: PieceColor, long: bool = true): Location {.inline
else: else:
return (-1, 2) return (-1, 2)
# These return absolute locations rather than relative direction offsets
func kingSideRook(color: PieceColor): Location {.inline.} = (if color == White: (7, 7) else: (0, 7))
func queenSideRook(color: PieceColor): Location {.inline.} = (if color == White: (7, 0) else: (0, 0))
# A bunch of getters
func getActiveColor*(self: ChessBoard): PieceColor {.inline.} = func getActiveColor*(self: ChessBoard): PieceColor {.inline.} =
## Returns the currently active color ## Returns the currently active color
## (turn of who has to move) ## (turn of who has to move)
@ -296,8 +285,6 @@ proc newChessboard: ChessBoard =
new(result) new(result)
# Turns our flat sequence into an 8x8 grid # Turns our flat sequence into an 8x8 grid
result.grid = newMatrixFromSeq[Piece](empty, (8, 8)) result.grid = newMatrixFromSeq[Piece](empty, (8, 8))
#result.cache = Cache(canCastle: (white: CacheEntry[tuple[queen, king: bool]](), black: CacheEntry[tuple[queen, king: bool]]()),
# inCheck: (white: CacheEntry[bool](), black: CacheEntry[bool]()))
result.position = Position(attacked: (@[], @[]), result.position = Position(attacked: (@[], @[]),
enPassantSquare: emptyLocation(), enPassantSquare: emptyLocation(),
move: emptyMove(), move: emptyMove(),
@ -318,9 +305,9 @@ proc newChessboard: ChessBoard =
proc newChessboardFromFEN*(state: string): ChessBoard = proc newChessboardFromFEN*(fen: string): ChessBoard =
## Initializes a chessboard with the ## Initializes a chessboard with the
## state encoded by the given FEN string ## position encoded by the given FEN string
result = newChessboard() result = newChessboard()
var var
# Current location in the grid # Current location in the grid
@ -330,11 +317,11 @@ proc newChessboardFromFEN*(state: string): ChessBoard =
section = 0 section = 0
# Current index into the FEN string # Current index into the FEN string
index = 0 index = 0
# Temporary variable to store the piece # Temporary variable to store a piece
piece: Piece piece: Piece
# See https://en.wikipedia.org/wiki/Forsyth%E2%80%93Edwards_Notation # See https://en.wikipedia.org/wiki/Forsyth%E2%80%93Edwards_Notation
while index <= state.high(): while index <= fen.high():
var c = state[index] var c = fen[index]
if c == ' ': if c == ' ':
# Next section # Next section
inc(section) inc(section)
@ -366,6 +353,8 @@ proc newChessboardFromFEN*(state: string): ChessBoard =
of Queen: of Queen:
result.position.pieces.black.queens.add((row, column)) result.position.pieces.black.queens.add((row, column))
of King: of King:
if result.position.pieces.black.king != emptyLocation():
raise newException(ValueError, "invalid position: exactly one king of each color must be present")
result.position.pieces.black.king = (row, column) result.position.pieces.black.king = (row, column)
else: else:
discard discard
@ -382,6 +371,8 @@ proc newChessboardFromFEN*(state: string): ChessBoard =
of Queen: of Queen:
result.position.pieces.white.queens.add((row, column)) result.position.pieces.white.queens.add((row, column))
of King: of King:
if result.position.pieces.white.king != emptyLocation():
raise newException(ValueError, "invalid position: exactly one king of each color must be present")
result.position.pieces.white.king = (row, column) result.position.pieces.white.king = (row, column)
else: else:
discard discard
@ -397,10 +388,10 @@ proc newChessboardFromFEN*(state: string): ChessBoard =
# Skip x columns # Skip x columns
let x = int(uint8(c) - uint8('0')) let x = int(uint8(c) - uint8('0'))
if x > 8: if x > 8:
raise newException(ValueError, "invalid skip value (> 8) in FEN string") raise newException(ValueError, &"invalid FEN: invalid column skip size ({x} > 8)")
column += int8(x) column += int8(x)
else: else:
raise newException(ValueError, "invalid piece identifier in FEN string") raise newException(ValueError, &"invalid FEN: unknown piece identifier '{c}'")
of 1: of 1:
# Active color # Active color
case c: case c:
@ -409,7 +400,7 @@ proc newChessboardFromFEN*(state: string): ChessBoard =
of 'b': of 'b':
result.position.turn = Black result.position.turn = Black
else: else:
raise newException(ValueError, "invalid active color identifier in FEN string") raise newException(ValueError, &"invalid FEN: invalid active color identifier '{c}'")
of 2: of 2:
# Castling availability # Castling availability
case c: case c:
@ -427,7 +418,7 @@ proc newChessboardFromFEN*(state: string): ChessBoard =
of 'q': of 'q':
result.position.castlingAvailable.black.queen = true result.position.castlingAvailable.black.queen = true
else: else:
raise newException(ValueError, "invalid castling availability in FEN string") raise newException(ValueError, &"invalid FEN: unknown symbol '{c}' found in castling availability section")
of 3: of 3:
# En passant target square # En passant target square
case c: case c:
@ -435,14 +426,14 @@ proc newChessboardFromFEN*(state: string): ChessBoard =
# Field is already uninitialized to the correct state # Field is already uninitialized to the correct state
discard discard
else: else:
result.position.enPassantSquare = state[index..index+1].algebraicToLocation() result.position.enPassantSquare = fen[index..index+1].algebraicToLocation()
# Square metadata is 2 bytes long # Square metadata is 2 bytes long
inc(index) inc(index)
of 4: of 4:
# Halfmove clock # Halfmove clock
var s = "" var s = ""
while not state[index].isSpaceAscii(): while not fen[index].isSpaceAscii():
s.add(state[index]) s.add(fen[index])
inc(index) inc(index)
# Backtrack so the space is seen by the # Backtrack so the space is seen by the
# next iteration of the loop # next iteration of the loop
@ -451,17 +442,20 @@ proc newChessboardFromFEN*(state: string): ChessBoard =
of 5: of 5:
# Fullmove number # Fullmove number
var s = "" var s = ""
while index <= state.high(): while index <= fen.high():
s.add(state[index]) s.add(fen[index])
inc(index) inc(index)
result.position.fullMoveCount = parseInt(s).int8 result.position.fullMoveCount = parseInt(s).int8
else: else:
raise newException(ValueError, "too many fields in FEN string") raise newException(ValueError, "invalid FEN: too many fields in FEN string")
inc(index) inc(index)
result.updateAttackedSquares() result.updateAttackedSquares()
if result.inCheck(result.getActiveColor().opposite): if result.inCheck(result.getActiveColor().opposite):
# Opponent king cannot be captured! # Opponent king cannot be captured on the next move
raise newException(ValueError, "invalid position: opponent king can be captured") raise newException(ValueError, "invalid position: opponent king can be captured")
if result.position.pieces.white.king == emptyLocation() or result.position.pieces.black.king == emptyLocation():
# Both kings must be on the board
raise newException(ValueError, "invalid position: exactly one king of each color must be present")
proc newDefaultChessboard*: ChessBoard {.inline.} = proc newDefaultChessboard*: ChessBoard {.inline.} =
@ -588,26 +582,12 @@ proc inCheck*(self: ChessBoard, color: PieceColor = None): bool =
color = self.getActiveColor() color = self.getActiveColor()
case color: case color:
of White: of White:
#[if self.cache.inCheck.white.valid:
return self.cache.inCheck.white.data]#
result = self.isAttacked(self.position.pieces.white.king, Black) result = self.isAttacked(self.position.pieces.white.king, Black)
of Black: of Black:
#[if self.cache.inCheck.black.valid:
return self.cache.inCheck.black.data]#
result = self.isAttacked(self.position.pieces.black.king, White) result = self.isAttacked(self.position.pieces.black.king, White)
else: else:
# Unreachable # Unreachable
discard discard
#[case color:
of White:
self.cache.inCheck.white.valid = true
self.cache.inCheck.white.data = result
of Black:
self.cache.inCheck.black.valid = true
self.cache.inCheck.black.data = result
else:
# Unreachable
discard]#
proc canCastle*(self: ChessBoard, color: PieceColor = None): tuple[queen, king: bool] {.inline.} = proc canCastle*(self: ChessBoard, color: PieceColor = None): tuple[queen, king: bool] {.inline.} =
@ -617,17 +597,12 @@ proc canCastle*(self: ChessBoard, color: PieceColor = None): tuple[queen, king:
var color = color var color = color
if color == None: if color == None:
color = self.getActiveColor() color = self.getActiveColor()
# If the rooks or king have been moved, castling # Check if castling rights are still available for moving side
# rights have been lost
case color: case color:
of White: of White:
#[if self.cache.canCastle.white.valid:
return self.cache.canCastle.white.data]#
result.king = self.position.castlingAvailable.white.king result.king = self.position.castlingAvailable.white.king
result.queen = self.position.castlingAvailable.white.queen result.queen = self.position.castlingAvailable.white.queen
of Black: of Black:
#[if self.cache.canCastle.black.valid:
return self.cache.canCastle.black.data#]#
result.king = self.position.castlingAvailable.black.king result.king = self.position.castlingAvailable.black.king
result.queen = self.position.castlingAvailable.black.queen result.queen = self.position.castlingAvailable.black.queen
of None: of None:
@ -641,9 +616,9 @@ proc canCastle*(self: ChessBoard, color: PieceColor = None): tuple[queen, king:
loc: Location loc: Location
queenSide: Location queenSide: Location
kingSide: Location kingSide: Location
# If the path between the king and rook on a given side is blocked or any of the # If the path between the king and rook on a given side is blocked, or any of the
# squares where the king would travel to are attacked by the opponent, # squares where the king would move to are attacked by the opponent, then castling
# then castling is (temporarily) prohibited on that side # is temporarily prohibited on that side
case color: case color:
of White: of White:
loc = self.position.pieces.white.king loc = self.position.pieces.white.king
@ -662,42 +637,53 @@ proc canCastle*(self: ChessBoard, color: PieceColor = None): tuple[queen, king:
var var
location = loc location = loc
otherPiece: Piece otherPiece: Piece
while location != loc + shortCastleKing(): moveKing: bool = true
while true:
location = location + kingSide location = location + kingSide
if location == color.kingSideRook():
# No need to do any extra checks: if the piece
# on this square were not a rook of the same color
# as the castling king, then we wouldn't have gotten
# here in the first place (it would've had to be either
# moved or captured, and both of those actions are detected
# and accounted for way before this point)
break
if location == loc + shortCastleKing():
moveKing = false
otherPiece = self.grid[location.row, location.col] otherPiece = self.grid[location.row, location.col]
if otherPiece.color != None or self.isAttacked(location, color.opposite()): if otherPiece.color != None:
result.king = false
break
if moveKing and self.isAttacked(location, color.opposite()):
result.king = false result.king = false
break break
if result.queen: if result.queen:
# Long castle # Long castle
var var
location = loc location = loc
otherPiece: Piece otherPiece: Piece
while location != loc + longCastleKing(): moveKing: bool = true
while true:
location = location + queenSide location = location + queenSide
if location == color.queenSideRook():
break
if location == loc + longCastleKing():
moveKing = false
otherPiece = self.grid[location.row, location.col] otherPiece = self.grid[location.row, location.col]
if otherPiece.color != None or self.isAttacked(location, color.opposite()): if otherPiece.color != None:
result.queen = false
break
if moveKing and self.isAttacked(location, color.opposite()):
result.queen = false result.queen = false
break break
#[case color:
of White:
self.cache.canCastle.white.data = result
self.cache.canCastle.white.valid = true
of Black:
self.cache.canCastle.black.data = result
self.cache.canCastle.black.valid = true
else:
discard]#
proc getCheckResolutions(self: ChessBoard, color: PieceColor): seq[Location] = proc getCheckResolutions(self: ChessBoard, color: PieceColor): seq[Location] =
## Returns the squares that need to be covered to ## Returns the squares that need to be covered to
## resolve the current check (includes capturing ## resolve the current check (including capturing
## the checking piece). In case of double check, an ## the checking piece). In case of double check, an
## empty list is returned (as the king must move) ## empty list is returned (as the king must move)
var king: Location var king: Location
@ -707,7 +693,7 @@ proc getCheckResolutions(self: ChessBoard, color: PieceColor): seq[Location] =
of Black: of Black:
king = self.position.pieces.black.king king = self.position.pieces.black.king
else: else:
return @[] return
let attackers: seq[Location] = self.getAttackers(king, color.opposite()) let attackers: seq[Location] = self.getAttackers(king, color.opposite())
if attackers.len() > 1: if attackers.len() > 1:
@ -722,8 +708,8 @@ proc getCheckResolutions(self: ChessBoard, color: PieceColor): seq[Location] =
# Blocking the attack is also a viable strategy # Blocking the attack is also a viable strategy
# (unless the check is from a knight or a pawn, # (unless the check is from a knight or a pawn,
# in which case either the king has to move or # in which case either the king has to move or
# that piece has to be captured) # that piece has to be captured, but this is
if attackerPiece.kind notin [Knight, Pawn]: # already implicitly handled by the loop below)
var location = attacker var location = attacker
while location != king: while location != king:
location = location + attack.direction location = location + attack.direction
@ -754,7 +740,7 @@ 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 forward = (piece.color.forward() + location) let forward = (piece.color.topSide() + 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(forward) locations.add(forward)
@ -766,13 +752,12 @@ proc generatePawnMoves(self: ChessBoard, location: Location): seq[Move] =
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(double) locations.add(double)
flags.add(DoublePush) flags.add(DoublePush)
let enPassantPiece = self.getEnPassantTarget() + piece.color.opposite().topSide() let enPassantPawn = self.getEnPassantTarget() + piece.color.opposite().topSide()
let enPassantPawn = self.grid[enPassantPiece.row, enPassantPiece.col]
# They can also move on either diagonal one # They can also move on either diagonal one
# square, but only to capture or for en passant # square, but only to capture or for en passant
for diagonal in [location + piece.color.topRightDiagonal(), location + piece.color.topLeftDiagonal()]: for diagonal in [location + piece.color.topRightDiagonal(), location + piece.color.topLeftDiagonal()]:
if diagonal.isValid(): if diagonal.isValid():
if enPassantPawn.color == piece.color.opposite() and diagonal == self.position.enPassantSquare: if diagonal == self.position.enPassantSquare and self.grid[enPassantPawn.row, enPassantPawn.col].color == self.getActiveColor().opposite():
# Ensure en passant doesn't create a check # Ensure en passant doesn't create a check
let king = self.getKing(piece.color) let king = self.getKing(piece.color)
var ok = true var ok = true
@ -1253,9 +1238,6 @@ proc updateAttackedSquares(self: ChessBoard) =
self.updateKnightAttacks() self.updateKnightAttacks()
# Kings # Kings
self.updateKingAttacks() self.updateKingAttacks()
# Invalidate the cache whenever updates to the
# metadata are made
#self.invalidateCache()
proc removePiece(self: ChessBoard, location: Location, attack: bool = true) = proc removePiece(self: ChessBoard, location: Location, attack: bool = true) =
@ -1361,9 +1343,7 @@ proc movePiece(self: ChessBoard, move: Move, attack: bool = true) =
self.grid[move.targetSquare.row, move.targetSquare.col] = piece self.grid[move.targetSquare.row, move.targetSquare.col] = piece
if attack: if attack:
self.updateAttackedSquares() self.updateAttackedSquares()
#else:
# Just to be sure
# self.invalidateCache()
proc movePiece(self: ChessBoard, startSquare, targetSquare: Location, attack: bool = true) = proc movePiece(self: ChessBoard, startSquare, targetSquare: Location, attack: bool = true) =
@ -1371,20 +1351,13 @@ proc movePiece(self: ChessBoard, startSquare, targetSquare: Location, attack: bo
self.movePiece(Move(startSquare: startSquare, targetSquare: targetSquare), attack) self.movePiece(Move(startSquare: startSquare, targetSquare: targetSquare), attack)
#[func invalidateCache(self: ChessBoard) {.inline.} =
## Invalidates the internal caches
self.cache.canCastle.white.valid = false
self.cache.canCastle.black.valid = false
self.cache.inCheck.white.valid = false
self.cache.inCheck.black.valid = false]#
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 on the given move. Can ## performing legality checks. Can be used in
## be used in performance-critical paths where ## performance-critical paths where a move is
## a move is already known to be legal ## already known to be legal
# Record final position for future reference # Record final position for future reference
self.positions.add(self.position) self.positions.add(self.position)
@ -1392,17 +1365,28 @@ proc doMove(self: ChessBoard, move: Move) =
# Final checks # Final checks
let piece = self.grid[move.startSquare.row, move.startSquare.col] let piece = self.grid[move.startSquare.row, move.startSquare.col]
# Needed to detect draw by the 50 move rule
var var
halfMoveClock = self.position.halfMoveClock halfMoveClock = self.position.halfMoveClock
fullMoveCount = self.position.fullMoveCount fullMoveCount = self.position.fullMoveCount
castlingAvailable = self.position.castlingAvailable castlingAvailable = self.position.castlingAvailable
enPassantTarget = self.getEnPassantTarget()
# Needed to detect draw by the 50 move rule
if piece.kind == Pawn or move.flag == Capture: if piece.kind == Pawn or move.flag == Capture:
halfMoveClock = 0 halfMoveClock = 0
else: else:
inc(halfMoveClock) inc(halfMoveClock)
if piece.color == Black: if piece.color == Black:
inc(fullMoveCount) inc(fullMoveCount)
# En passant check
if enPassantTarget != emptyLocation():
let enPassantPawn = enPassantTarget + piece.color.topSide()
if self.grid[enPassantPawn.row, enPassantPawn.col].color == piece.color.opposite():
enPassantTarget = emptyLocation()
if move.flag == DoublePush:
enPassantTarget = move.targetSquare + piece.color.bottomSide()
# Castling check: have the rooks moved? # Castling check: have the rooks moved?
if piece.kind == Rook: if piece.kind == Rook:
case piece.color: case piece.color:
@ -1462,12 +1446,11 @@ proc doMove(self: ChessBoard, move: Move) =
self.position = Position(plyFromRoot: self.position.plyFromRoot + 1, self.position = Position(plyFromRoot: self.position.plyFromRoot + 1,
halfMoveClock: halfMoveClock, halfMoveClock: halfMoveClock,
fullMoveCount: fullMoveCount, fullMoveCount: fullMoveCount,
#captured: if move.flag == Capture: self.grid[move.targetSquare.row, move.targetSquare.col] else: emptyPiece(),
turn: self.getActiveColor().opposite, turn: self.getActiveColor().opposite,
castlingAvailable: castlingAvailable, castlingAvailable: castlingAvailable,
move: move, move: move,
pieces: self.position.pieces, pieces: self.position.pieces,
enPassantSquare: if move.flag == EnPassant: move.targetSquare + piece.color.bottomSide() else: emptyLocation() enPassantSquare: enPassantTarget
) )
# Update position metadata # Update position metadata
@ -1489,7 +1472,6 @@ proc doMove(self: ChessBoard, move: Move) =
if move.flag == Capture: if move.flag == Capture:
# Get rid of captured pieces # Get rid of captured pieces
#self.position.captured = self.grid[move.targetSquare.row, move.targetSquare.col]
self.removePiece(move.targetSquare, attack=false) self.removePiece(move.targetSquare, attack=false)
if move.flag == EnPassant: if move.flag == EnPassant:
@ -1979,8 +1961,4 @@ when isMainModule:
testPiece(b.getPiece("d8"), Queen, Black) testPiece(b.getPiece("d8"), Queen, Black)
setControlCHook(proc () {.noconv.} = quit(0)) setControlCHook(proc () {.noconv.} = quit(0))
#b = newChessboardFromFEN("r3k2r/Pppp1ppp/1b3nbN/nP6/BBPPP3/q4N2/Pp4PP/R2Q1RK1 b kq d3 0 1")
#let m = Move(startSquare: "c7".algebraicToLocation, targetSquare: "c5".algebraicToLocation, flag: DoublePush)
#b.makeMove()
quit(main()) quit(main())

View File

@ -62,7 +62,10 @@ def main(args: Namespace) -> int:
positions["stockfish"][move] = int(nodes) positions["stockfish"][move] = int(nodes)
for (source, target, promotion, nodes) in pattern.findall(nimfish_output): for (source, target, promotion, nodes) in pattern.findall(nimfish_output):
move = f"{source}{target}{promotion}" move = f"{source}{target}{promotion}"
if move in positions["all"]:
positions["all"][move].append(int(nodes)) positions["all"][move].append(int(nodes))
else:
positions["all"][move] = [int(nodes)]
positions["nimfish"][move] = int(nodes) positions["nimfish"][move] = int(nodes)
missing = { missing = {