Fix bugs with pawn movegen and add promotions

This commit is contained in:
Mattia Giambirtone 2024-04-17 11:54:45 +02:00
parent e50cfb9d64
commit 86265c68f0
4 changed files with 75 additions and 76 deletions

View File

@ -46,21 +46,21 @@ func `!=`*(a: Bitboard, b: SomeInteger): bool {.inline.} = a.uint64 != b.uint64
func getFileMask*(file: int): Bitboard = Bitboard(0x101010101010101'u64) shl file.uint64 func getFileMask*(file: int): Bitboard = Bitboard(0x101010101010101'u64) shl file.uint64
func getRankMask*(rank: int): Bitboard = Bitboard(uint64.high()) shl uint64(8 * (rank + 1)) func getRankMask*(rank: int): Bitboard = Bitboard(0xff) shl uint64(8 * rank)
func squareToBitboard*(square: SomeInteger): Bitboard = Bitboard(1'u64) shl square.uint64 func toBitboard*(square: SomeInteger): Bitboard = Bitboard(1'u64) shl square.uint64
func squareToBitboard*(square: Square): Bitboard = squareToBitboard(square.int8) func toBitboard*(square: Square): Bitboard = toBitboard(square.int8)
proc bitboardToSquare*(b: Bitboard): Square = Square(b.uint64.countTrailingZeroBits()) proc toSquare*(b: Bitboard): Square = Square(b.uint64.countTrailingZeroBits())
func createMove*(startSquare: Bitboard, targetSquare: Square, flags: varargs[MoveFlag]): Move = func createMove*(startSquare: Bitboard, targetSquare: Square, flags: varargs[MoveFlag]): Move =
result = createMove(startSquare.bitboardToSquare(), targetSquare, flags) result = createMove(startSquare.toSquare(), targetSquare, flags)
func createMove*(startSquare: Square, targetSquare: Bitboard, flags: varargs[MoveFlag]): Move = func createMove*(startSquare: Square, targetSquare: Bitboard, flags: varargs[MoveFlag]): Move =
result = createMove(startSquare, targetSquare.bitboardToSquare(), flags) result = createMove(startSquare, targetSquare.toSquare(), flags)
func createMove*(startSquare, targetSquare: Bitboard, flags: varargs[MoveFlag]): Move = func createMove*(startSquare, targetSquare: Bitboard, flags: varargs[MoveFlag]): Move =
result = createMove(startSquare.bitboardToSquare(), targetSquare.bitboardToSquare(), flags) result = createMove(startSquare.toSquare(), targetSquare.toSquare(), flags)
func toBin*(x: Bitboard, b: Positive = 64): string = toBin(BiggestInt(x), b) func toBin*(x: Bitboard, b: Positive = 64): string = toBin(BiggestInt(x), b)
@ -73,7 +73,7 @@ iterator items*(self: Bitboard): Square =
## are set ## are set
var bits = self var bits = self
while bits != 0: while bits != 0:
yield bits.bitboardToSquare() yield bits.toSquare()
bits = bits and bits - 1 bits = bits and bits - 1
@ -165,10 +165,10 @@ func getDirectionMask*(bitboard: Bitboard, color: PieceColor, direction: Directi
return bitboard shl 8 return bitboard shl 8
of Backward: of Backward:
return bitboard shr 8 return bitboard shr 8
of ForwardLeft:
return bitboard shl 9
of ForwardRight: of ForwardRight:
return bitboard shl 7 return bitboard shl 7
of ForwardLeft:
return bitboard shr 9
of BackwardRight: of BackwardRight:
return bitboard shr 9 return bitboard shr 9
of BackwardLeft: of BackwardLeft:
@ -179,34 +179,35 @@ func getDirectionMask*(bitboard: Bitboard, color: PieceColor, direction: Directi
discard discard
func getLastRank*(color: PieceColor): Bitboard = (if color == White: getRankMask(0) else: getRankMask(7))
func getDirectionMask*(square: Square, color: PieceColor, direction: Direction): Bitboard = func getDirectionMask*(square: Square, color: PieceColor, direction: Direction): Bitboard =
## Get a bitmask for the given direction for a piece ## Get a bitmask for the given direction for a piece
## of the given color located at the given square ## of the given color located at the given square
result = getDirectionMask(squareToBitboard(square), color, direction) result = getDirectionMask(toBitboard(square), color, direction)
func forwardRelativeTo*(self: Bitboard, side: PieceColor): Bitboard = getDirectionMask(self, side, Forward) func forwardRelativeTo*(self: Bitboard, side: PieceColor): Bitboard = getDirectionMask(self, side, Forward)
func doubleForwardRelativeTo*(self: Bitboard, side: PieceColor): Bitboard = self.forwardRelativeTo(side).forwardRelativeTo(side) func doubleForwardRelativeTo*(self: Bitboard, side: PieceColor): Bitboard = self.forwardRelativeTo(side).forwardRelativeTo(side)
func backwardRelativeTo*(self: Bitboard, side: PieceColor): Bitboard = getDirectionMask(self, side, Backward) func backwardRelativeTo*(self: Bitboard, side: PieceColor): Bitboard = getDirectionMask(self, side, Backward)
func doubleBackwardRelativeTo*(self: Bitboard, side: PieceColor): Bitboard = self.backwardRelativeTo(side).backwardRelativeTo(side)
# We mask off the first and last ranks/files for # We mask off the first and last ranks for
# left and right movements respectively to # left and right movements respectively to
# avoid weird wraparounds # avoid weird wraparounds
func topRightRelativeTo*(self: Bitboard, side: PieceColor): Bitboard = func forwardRightRelativeTo*(self: Bitboard, side: PieceColor): Bitboard =
getDirectionMask(self, side, ForwardRight) and not getFileMask(7) getDirectionMask(self, side, ForwardRight) and not getFileMask(7)
func topLeftRelativeTo*(self: Bitboard, side: PieceColor): Bitboard = func forwardLeftRelativeTo*(self: Bitboard, side: PieceColor): Bitboard =
getDirectionMask(self, side, ForwardLeft) and not getFileMask(0) getDirectionMask(self, side, ForwardLeft) and not getFileMask(0)
func bottomRightRelativeTo*(self: Bitboard, side: PieceColor): Bitboard = func bottomRightRelativeTo*(self: Bitboard, side: PieceColor): Bitboard =
let lastRank = if side == White: getRankMask(0) else: getRankMask(7) getDirectionMask(self, side, BackwardRight) and not getFileMask(7)
getDirectionMask(self, side, BackwardRight) and not lastRank
func bottomLeftRelativeTo*(self: Bitboard, side: PieceColor): Bitboard = func bottomLeftRelativeTo*(self: Bitboard, side: PieceColor): Bitboard =
let lastRank = if side == White: getRankMask(0) else: getRankMask(7) getDirectionMask(self, side, BackwardLeft) and not getFileMask(0)
getDirectionMask(self, side, BackwardLeft) and not getRankMask(7)

View File

@ -85,9 +85,8 @@ proc updateBoard*(self: ChessBoard)
# A bunch of getters # A bunch of getters
func getActiveColor*(self: ChessBoard): PieceColor {.inline.} = func getSideToMove*(self: ChessBoard): PieceColor {.inline.} =
## Returns the currently active color ## Returns the currently side to move
## (turn of who has to move)
return self.position.turn return self.position.turn
@ -141,17 +140,6 @@ func getKingStartingSquare(color: PieceColor): Square {.inline.} =
discard discard
func getLastRank(color: PieceColor): int {.inline.} =
## Retrieves the square of the last
## rank relative to the given color
case color:
of White:
return 0
of Black:
return 7
else:
return -1
func kingSideRook(color: PieceColor): Square {.inline.} = (if color == White: makeSquare(7, 7) else: makeSquare(0, 7)) func kingSideRook(color: PieceColor): Square {.inline.} = (if color == White: makeSquare(7, 7) else: makeSquare(0, 7))
func queenSideRook(color: PieceColor): Square {.inline.} = (if color == White: makeSquare(7, 0) else: makeSquare(0, 0)) func queenSideRook(color: PieceColor): Square {.inline.} = (if color == White: makeSquare(7, 0) else: makeSquare(0, 0))
func longCastleKing(color: PieceColor): Square {.inline.} = (if color == White: makeSquare(7, 2) else: makeSquare(0, 5)) func longCastleKing(color: PieceColor): Square {.inline.} = (if color == White: makeSquare(7, 2) else: makeSquare(0, 5))
@ -369,7 +357,7 @@ proc newChessboardFromFEN*(fen: string): ChessBoard =
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)
# result.updateAttackedSquares() # result.updateAttackedSquares()
#[if result.inCheck(result.getActiveColor().opposite): #[if result.inCheck(result.getSideToMove().opposite):
# Opponent king cannot be captured on the next move # 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 == Bitboard(0) or result.position.pieces.black.king == Bitboard(0): if result.position.pieces.white.king == Bitboard(0) or result.position.pieces.black.king == Bitboard(0):
@ -509,7 +497,7 @@ func getKing(self: ChessBoard, color: PieceColor = None): Square {.inline.} =
## color (if it is None, the active color is used) ## color (if it is None, the active color is used)
var color = color var color = color
if color == None: if color == None:
color = self.getActiveColor() color = self.getSideToMove()
case color: case color:
of White: of White:
return Square(self.position.pieces.white.king.uint64.countTrailingZeroBits()) return Square(self.position.pieces.white.king.uint64.countTrailingZeroBits())
@ -541,54 +529,61 @@ proc getOccupancy(self: ChessBoard): Bitboard =
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
## pawn movems ## and non-promotion pawn moves
let let
sideToMove = self.getActiveColor() sideToMove = self.getSideToMove()
pawns = self.getBitboard(Pawn, sideToMove) pawns = self.getBitboard(Pawn, sideToMove)
# Can't move to these squares # We can only move to squares that are *not* occupied by another piece.
occupancy = not self.getOccupancy() # We also cannot move to the last rank, as that will result in a promotion
offsets = if sideToMove == White: (-8, -16) else: (8, 16) # and is handled elsewhere
# Single pushes occupancy = not (self.getOccupancy() or sideToMove.getLastRank())
# Single push
for square in pawns.forwardRelativeTo(sideToMove) and occupancy: for square in pawns.forwardRelativeTo(sideToMove) and occupancy:
moves.add(createMove((square - offsets[0]), square)) moves.add(createMove(square.toBitboard().backwardRelativeTo(sideToMove), square))
# Double pushes # Double push
for square in pawns.doubleForwardRelativeTo(sideToMove) and occupancy: let rank = if sideToMove == White: getRankMask(6) else: getRankMask(1) # Only pawns on their starting rank can double push
moves.add(createMove((square - offsets[1]), square, DoublePush)) for square in (pawns and rank).doubleForwardRelativeTo(sideToMove) and occupancy:
moves.add(createMove(square.toBitboard().doubleBackwardRelativeTo(sideToMove), square, DoublePush))
proc generatePawnCaptures(self: ChessBoard, moves: var MoveList) = proc generatePawnCaptures(self: ChessBoard, moves: var MoveList) =
## Helper of generatePawnMoves for generating all capture ## Helper of generatePawnMoves for generating all capture
## pawn moves ## pawn moves
let let
sideToMove = self.getActiveColor() sideToMove = self.getSideToMove()
nonSideToMove = sideToMove.opposite() nonSideToMove = sideToMove.opposite()
pawns = self.getBitboard(Pawn, sideToMove) pawns = self.getBitboard(Pawn, sideToMove)
# We can only capture enemy pieces # We can only capture enemy pieces
enemyPieces = self.getOccupancyFor(nonSideToMove) enemyPieces = self.getOccupancyFor(nonSideToMove)
offsets = if sideToMove == White: (9, 7) else: (-7, -9) rightMovement = pawns.forwardRightRelativeTo(sideToMove)
rightMovement = pawns.topRightRelativeTo(sideToMove) leftMovement = pawns.forwardLeftRelativeTo(sideToMove)
leftMovement = pawns.topLeftRelativeTo(sideToMove)
epTarget = self.getEnPassantTarget() epTarget = self.getEnPassantTarget()
let epBitboard = if (epTarget != nullSquare()): epTarget.squareToBitboard() else: Bitboard(0) let epBitboard = if (epTarget != nullSquare()): epTarget.toBitboard() else: Bitboard(0)
# Top right attacks # Top right attacks
for square in rightMovement and enemyPieces: for square in rightMovement and enemyPieces:
moves.add(createMove((square - offsets[0]), square, Capture)) moves.add(createMove(square.toBitboard().bottomLeftRelativeTo(sideToMove), square, Capture))
# Top left attacks
for square in leftMovement and enemyPieces: for square in leftMovement and enemyPieces:
moves.add(createMove((square - offsets[1]), square, Capture)) moves.add(createMove(square.toBitboard().bottomRightRelativeTo(sideToMove), square, Capture))
# Special case for en passant # Special case for en passant
let let
epLeft = epBitboard and leftMovement epLeft = epBitboard and leftMovement
epRight = epBitboard and rightMovement epRight = epBitboard and rightMovement
if epLeft != 0: if epLeft != 0:
moves.add(createMove(epBitboard.topLeftRelativeTo(nonSideToMove), epBitboard, EnPassant)) moves.add(createMove(epBitboard.forwardLeftRelativeTo(nonSideToMove), epBitboard, EnPassant))
elif epRight != 0: elif epRight != 0:
moves.add(createMove(epBitboard.topRightRelativeTo(nonSideToMove), epBitboard, EnPassant)) moves.add(createMove(epBitboard.forwardRightRelativeTo(nonSideToMove), epBitboard, EnPassant))
proc generatePawnPromotions(self: ChessBoard, moves: var MoveList) = proc generatePawnPromotions(self: ChessBoard, moves: var MoveList) =
## Helper of generatePawnMoves for generating all pawn promotion ## Helper of generatePawnMoves for generating all pawn promotion
## moves ## moves
let
sideToMove = self.getSideToMove()
pawns = self.getBitboard(Pawn, sideToMove)
occupancy = not self.getOccupancy()
for square in pawns.forwardRelativeTo(sideToMove) and occupancy and sideToMove.getLastRank():
for promotion in [PromoteToBishop, PromoteToKnight, PromoteToQueen, PromoteToRook]:
moves.add(createMove(square.toBitboard().backwardRelativeTo(sideToMove), square, promotion))
proc generatePawnMoves(self: ChessBoard, moves: var MoveList) = proc generatePawnMoves(self: ChessBoard, moves: var MoveList) =
@ -597,6 +592,7 @@ proc generatePawnMoves(self: ChessBoard, moves: var MoveList) =
self.generatePawnMovements(moves) self.generatePawnMovements(moves)
self.generatePawnCaptures(moves) self.generatePawnCaptures(moves)
self.generatePawnPromotions(moves)
proc generateSlidingMoves(self: ChessBoard, square: Square): seq[Move] = proc generateSlidingMoves(self: ChessBoard, square: Square): seq[Move] =
@ -882,7 +878,7 @@ proc doMove(self: ChessBoard, move: Move) =
inc(fullMoveCount) inc(fullMoveCount)
if move.isDoublePush(): if move.isDoublePush():
enPassantTarget = move.targetSquare.squareToBitboard().backwardRelativeTo(piece.color).bitboardToSquare() enPassantTarget = move.targetSquare.toBitboard().backwardRelativeTo(piece.color).toSquare()
# Castling check: have the rooks moved? # Castling check: have the rooks moved?
if piece.kind == Rook: if piece.kind == Rook:
@ -944,7 +940,7 @@ 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,
turn: self.getActiveColor().opposite, turn: self.getSideToMove().opposite,
castlingAvailable: castlingAvailable, castlingAvailable: castlingAvailable,
enPassantSquare: enPassantTarget, enPassantSquare: enPassantTarget,
pieces: self.position.pieces pieces: self.position.pieces
@ -971,7 +967,7 @@ proc doMove(self: ChessBoard, move: Move) =
if move.isEnPassant(): if move.isEnPassant():
# Make the en passant pawn disappear # Make the en passant pawn disappear
self.removePiece(move.targetSquare.squareToBitboard().backwardRelativeTo(piece.color).bitboardToSquare(), attack=false) self.removePiece(move.targetSquare.toBitboard().backwardRelativeTo(piece.color).toSquare(), attack=false)
if move.isCapture(): if move.isCapture():
# Get rid of captured pieces # Get rid of captured pieces
@ -1032,8 +1028,8 @@ proc updateBoard*(self: ChessBoard) =
self.grid[sq] = Piece(color: White, kind: Queen) self.grid[sq] = Piece(color: White, kind: Queen)
for sq in self.position.pieces.black.queens: for sq in self.position.pieces.black.queens:
self.grid[sq] = Piece(color: Black, kind: Queen) self.grid[sq] = Piece(color: Black, kind: Queen)
self.grid[self.position.pieces.white.king.bitboardToSquare()] = Piece(color: White, kind: King) self.grid[self.position.pieces.white.king.toSquare()] = Piece(color: White, kind: King)
self.grid[self.position.pieces.black.king.bitboardToSquare()] = Piece(color: Black, kind: King) self.grid[self.position.pieces.black.king.toSquare()] = Piece(color: Black, kind: King)
proc unmakeMove*(self: ChessBoard) = proc unmakeMove*(self: ChessBoard) =
@ -1180,7 +1176,7 @@ proc toFEN*(self: ChessBoard): string =
result &= "/" result &= "/"
result &= " " result &= " "
# Active color # Active color
result &= (if self.getActiveColor() == White: "w" else: "b") result &= (if self.getSideToMove() == White: "w" else: "b")
result &= " " result &= " "
# Castling availability # Castling availability
let castleWhite = self.position.castlingAvailable.white let castleWhite = self.position.castlingAvailable.white
@ -1245,10 +1241,10 @@ proc perft*(self: ChessBoard, ply: int, verbose: bool = false, divide: bool = fa
for move in moves: for move in moves:
if verbose: if verbose:
let canCastle = self.canCastle(self.getActiveColor()) let canCastle = self.canCastle(self.getSideToMove())
echo &"Ply (from root): {self.position.plyFromRoot}" echo &"Ply (from root): {self.position.plyFromRoot}"
echo &"Move: {move.startSquare.toAlgebraic()}{move.targetSquare.toAlgebraic()}, from ({move.startSquare.rank}, {move.startSquare.file}) to ({move.targetSquare.rank}, {move.targetSquare.file})" echo &"Move: {move.startSquare.toAlgebraic()}{move.targetSquare.toAlgebraic()}, from ({move.startSquare.rank}, {move.startSquare.file}) to ({move.targetSquare.rank}, {move.targetSquare.file})"
echo &"Turn: {self.getActiveColor()}" echo &"Turn: {self.getSideToMove()}"
echo &"Piece: {self.grid[move.startSquare].kind}" echo &"Piece: {self.grid[move.startSquare].kind}"
echo &"Flags: {move.getFlags()}" echo &"Flags: {move.getFlags()}"
echo &"In check: {(if self.inCheck(): \"yes\" else: \"no\")}" echo &"In check: {(if self.inCheck(): \"yes\" else: \"no\")}"
@ -1274,7 +1270,7 @@ proc perft*(self: ChessBoard, ply: int, verbose: bool = false, divide: bool = fa
# 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.getActiveColor()) let canCastle = self.canCastle(self.getSideToMove())
echo "\n" echo "\n"
echo &"Opponent in check: {(if self.inCheck(): \"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\")}"
@ -1420,7 +1416,7 @@ proc handleMoveCommand(board: ChessBoard, command: seq[string]): Move {.discarda
var move = createMove(startSquare, targetSquare, flags) var move = createMove(startSquare, targetSquare, flags)
let piece = board.getPiece(move.startSquare) let piece = board.getPiece(move.startSquare)
if piece.kind == King and move.startSquare == board.getActiveColor().getKingStartingSquare(): if piece.kind == King and move.startSquare == board.getSideToMove().getKingStartingSquare():
if move.targetSquare == longCastleKing(piece.color): if move.targetSquare == longCastleKing(piece.color):
move.flags = move.flags or CastleLong.uint16 move.flags = move.flags or CastleLong.uint16
elif move.targetSquare == shortCastleKing(piece.color): elif move.targetSquare == shortCastleKing(piece.color):
@ -1582,7 +1578,7 @@ proc main: int =
of "undo", "u": of "undo", "u":
board.unmakeMove() board.unmakeMove()
of "turn": of "turn":
echo &"Active color: {board.getActiveColor()}" echo &"Active color: {board.getSideToMove()}"
of "ep": of "ep":
let target = board.getEnPassantTarget() let target = board.getEnPassantTarget()
if target != nullSquare(): if target != nullSquare():
@ -1600,9 +1596,9 @@ proc main: int =
continue continue
#[of "castle": #[of "castle":
let canCastle = board.canCastle() let canCastle = board.canCastle()
echo &"Castling rights for {($board.getActiveColor()).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.getActiveColor()} king in check: {(if board.inCheck(): \"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."
@ -1719,18 +1715,18 @@ when isMainModule:
testPieceBitboard(blackQueens, blackQueenSquares) testPieceBitboard(blackQueens, blackQueenSquares)
testPieceBitboard(blackKing, blackKingSquares) testPieceBitboard(blackKing, blackKingSquares)
b = newChessboardFromFEN("B7/P1P1P1P1/1P1P1P1P/7k/8/8/8/3K4 w - - 0 1")
var m = MoveList() var m = MoveList()
b.generatePawnMoves(m) b.generatePawnMoves(m)
echo &"Pawn moves for {b.getActiveColor()} at {b.toFEN()}: " echo &"Pawn moves for {b.getSideToMove()} at {b.toFEN()}: "
for move in m: for move in m:
echo " - ", move.startSquare, move.targetSquare echo " - ", move.startSquare, move.targetSquare, " ", move.getFlags()
b.doMove(createMove("a2", "a3")) #[b.doMove(createMove("d1", "c1"))
m.clear() m.clear()
b.generatePawnMoves(m) b.generatePawnMoves(m)
echo &"Pawn moves for {b.getActiveColor()} at {b.toFEN()}: " echo &"Pawn moves for {b.getSideToMove()} at {b.toFEN()}: "
for move in m: for move in m:
echo " - ", move.startSquare, move.targetSquare echo " - ", move.startSquare, move.targetSquare, " ", move.getFlags()
b.doMove(createMove("a7", "a5")) echo b.pretty()]#
echo b.pretty()
# setControlCHook(proc () {.noconv.} = quit(0)) # setControlCHook(proc () {.noconv.} = quit(0))
# quit(main()) # quit(main())

View File

@ -39,7 +39,9 @@ func `!=`*(a, b: Square): bool {.inline.} = a.int8 != b.int8
func `-`*(a, b: Square): Square {.inline.} = Square(a.int8 - b.int8) func `-`*(a, b: Square): Square {.inline.} = Square(a.int8 - b.int8)
func `-`*(a: Square, b: SomeInteger): Square {.inline.} = Square(a.int8 - b.int8) func `-`*(a: Square, b: SomeInteger): Square {.inline.} = Square(a.int8 - b.int8)
func `-`*(a: SomeInteger, b: Square): Square {.inline.} = Square(a.int8 - b.int8) func `-`*(a: SomeInteger, b: Square): Square {.inline.} = Square(a.int8 - b.int8)
func `+`*(a, b: Square): Square {.inline.} = Square(a.int8 + b.int8)
func `+`*(a: Square, b: SomeInteger): Square {.inline.} = Square(a.int8 + b.int8)
func `+`*(a: SomeInteger, b: Square): Square {.inline.} = Square(a.int8 + b.int8)
func colFromSquare*(square: Square): int8 = square.int8 mod 8 + 1 func colFromSquare*(square: Square): int8 = square.int8 mod 8 + 1
func rowFromSquare*(square: Square): int8 = square.int8 div 8 + 1 func rowFromSquare*(square: Square): int8 = square.int8 div 8 + 1

View File

@ -19,7 +19,7 @@ when isMainModule:
while true: while true:
canCastle = board.canCastle() canCastle = board.canCastle()
echo &"{board.pretty()}" echo &"{board.pretty()}"
echo &"Turn: {board.getActiveColor()}" echo &"Turn: {board.getSideToMove()}"
echo &"Moves: {board.getMoveCount()} full, {board.getHalfMoveCount()} half" echo &"Moves: {board.getMoveCount()} full, {board.getHalfMoveCount()} half"
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\")}"
stdout.write(&"En passant target: ") stdout.write(&"En passant target: ")