Profiling work
This commit is contained in:
parent
1bde8b623e
commit
8f56d9f89e
|
@ -21,7 +21,7 @@ import std/strformat
|
|||
|
||||
type
|
||||
# Useful type aliases
|
||||
Location* = tuple[row, col: int]
|
||||
Location* = tuple[row, col: int8]
|
||||
|
||||
Pieces = tuple[king: Location, queens: seq[Location], rooks: seq[Location],
|
||||
bishops: seq[Location], knights: seq[Location],
|
||||
|
@ -29,7 +29,7 @@ type
|
|||
|
||||
PieceColor* = enum
|
||||
## A piece color enumeration
|
||||
None = 0,
|
||||
None = 0'i8,
|
||||
White,
|
||||
Black
|
||||
|
||||
|
@ -50,7 +50,7 @@ type
|
|||
|
||||
MoveFlag* = enum
|
||||
## An enumeration of move flags
|
||||
Default, # Move is a regular move
|
||||
Default = 0'i8, # Move is a regular move
|
||||
# Castling
|
||||
CastleLong,
|
||||
CastleShort,
|
||||
|
@ -76,14 +76,14 @@ type
|
|||
# Number of half-moves that were performed
|
||||
# to reach this position starting from the
|
||||
# root of the tree
|
||||
plyFromRoot: int
|
||||
plyFromRoot: int16
|
||||
# Number of half moves since
|
||||
# last piece capture or pawn movement.
|
||||
# Used for the 50-move rule
|
||||
halfMoveClock: int
|
||||
halfMoveClock: int8
|
||||
# Full move counter. Increments
|
||||
# every 2 ply
|
||||
fullMoveCount: int
|
||||
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
|
||||
|
@ -122,11 +122,12 @@ proc makeMove*(self: ChessBoard, move: Move): Move
|
|||
proc makeMove*(self: ChessBoard, startSquare, targetSquare: Location): Move
|
||||
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 isValid*(a: Location): bool = a.row in 0..7 and a.col in 0..7
|
||||
func isValid*(a: Location): bool {.inline.} = a.row in 0..7 and a.col in 0..7
|
||||
proc generateMoves(self: ChessBoard, location: Location): seq[Move]
|
||||
proc isAttacked*(self: ChessBoard, loc: Location): bool
|
||||
proc undoLastMove*(self: ChessBoard): Move
|
||||
proc undoLastMove*(self: ChessBoard): Move {.discardable.}
|
||||
proc isLegal(self: ChessBoard, move: Move): bool
|
||||
proc doMove(self: ChessBoard, move: Move)
|
||||
|
||||
# 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
|
||||
|
@ -152,12 +153,12 @@ func queenSideRook(color: PieceColor): Location {.inline.} = (if color == White:
|
|||
func bottomLeftKnightMove(color: PieceColor, long: bool = true): Location {.inline.} =
|
||||
if color == White:
|
||||
if long:
|
||||
return (-2, 1)
|
||||
return (2, -1)
|
||||
else:
|
||||
return (1, -2)
|
||||
return (-1, -2)
|
||||
elif color == Black:
|
||||
if long:
|
||||
return (2, -1)
|
||||
return (-2, 1)
|
||||
else:
|
||||
return (1, -2)
|
||||
|
||||
|
@ -201,24 +202,24 @@ func topRightKnightMove(color: PieceColor, long: bool = true): Location {.inline
|
|||
return (-1, 2)
|
||||
|
||||
|
||||
proc getActiveColor*(self: ChessBoard): PieceColor =
|
||||
func getActiveColor*(self: ChessBoard): PieceColor {.inline.} =
|
||||
## Returns the currently active color
|
||||
## (turn of who has to move)
|
||||
return self.position.turn
|
||||
|
||||
|
||||
proc getEnPassantTarget*(self: ChessBoard): Location =
|
||||
func getEnPassantTarget*(self: ChessBoard): Location =
|
||||
## Returns the current en passant target square
|
||||
return self.position.enPassantSquare.targetSquare
|
||||
|
||||
|
||||
proc getMoveCount*(self: ChessBoard): int =
|
||||
func getMoveCount*(self: ChessBoard): int =
|
||||
## Returns the number of full moves that
|
||||
## have been played
|
||||
return self.position.fullMoveCount
|
||||
|
||||
|
||||
proc getHalfMoveCount*(self: ChessBoard): int =
|
||||
func getHalfMoveCount*(self: ChessBoard): int {.inline.} =
|
||||
## Returns the current number of half-moves
|
||||
## since the last irreversible move
|
||||
return self.position.halfMoveClock
|
||||
|
@ -275,8 +276,8 @@ proc newChessboardFromFEN*(state: string): ChessBoard =
|
|||
result = newChessboard()
|
||||
var
|
||||
# Current location in the grid
|
||||
row = 0
|
||||
column = 0
|
||||
row: int8 = 0
|
||||
column: int8 = 0
|
||||
# Current section in the FEN string
|
||||
section = 0
|
||||
# Current index into the FEN string
|
||||
|
@ -349,7 +350,7 @@ proc newChessboardFromFEN*(state: string): ChessBoard =
|
|||
let x = int(uint8(c) - uint8('0')) - 1
|
||||
if x > 7:
|
||||
raise newException(ValueError, "invalid skip value (> 8) in FEN string")
|
||||
column += x
|
||||
column += int8(x)
|
||||
else:
|
||||
raise newException(ValueError, "invalid piece identifier in FEN string")
|
||||
of 1:
|
||||
|
@ -402,14 +403,14 @@ proc newChessboardFromFEN*(state: string): ChessBoard =
|
|||
# Backtrack so the space is seen by the
|
||||
# next iteration of the loop
|
||||
dec(index)
|
||||
result.position.halfMoveClock = parseInt(s)
|
||||
result.position.halfMoveClock = parseInt(s).int8
|
||||
of 5:
|
||||
# Fullmove number
|
||||
var s = ""
|
||||
while index <= state.high():
|
||||
s.add(state[index])
|
||||
inc(index)
|
||||
result.position.fullMoveCount = parseInt(s)
|
||||
result.position.fullMoveCount = parseInt(s).int8
|
||||
else:
|
||||
raise newException(ValueError, "too many fields in FEN string")
|
||||
inc(index)
|
||||
|
@ -469,13 +470,13 @@ proc countPieces*(self: ChessBoard, piece: Piece): int =
|
|||
return self.countPieces(piece.kind, piece.color)
|
||||
|
||||
|
||||
func rankToColumn(rank: int): int =
|
||||
func rankToColumn(rank: int): int8 =
|
||||
## Converts a chess rank (1-indexed)
|
||||
## into a 0-indexed column value for our
|
||||
## board. This converter is necessary because
|
||||
## chess positions are indexed differently with
|
||||
## respect to our internal representation
|
||||
const indeces = [7, 6, 5, 4, 3, 2, 1, 0]
|
||||
const indeces: array[8, int8] = [7, 6, 5, 4, 3, 2, 1, 0]
|
||||
return indeces[rank - 1]
|
||||
|
||||
|
||||
|
@ -497,9 +498,9 @@ proc algebraicToLocation*(s: string): Location =
|
|||
if s[1] notin '1'..'8':
|
||||
raise newException(ValueError, &"algebraic position has invalid second character ('{s[1]}')")
|
||||
|
||||
let rank = int(uint8(s[0]) - uint8('a'))
|
||||
let rank = int8(uint8(s[0]) - uint8('a'))
|
||||
# Convert the file character to a number
|
||||
let file = rankToColumn(int(uint8(s[1]) - uint8('0')))
|
||||
let file = rankToColumn(int8(uint8(s[1]) - uint8('0')))
|
||||
return (file, rank)
|
||||
|
||||
|
||||
|
@ -828,7 +829,7 @@ proc generateMoves(self: ChessBoard, location: Location): seq[Move] =
|
|||
return @[]
|
||||
|
||||
|
||||
proc generateLegalMoves(self: ChessBoard, location: Location): seq[Move] =
|
||||
proc generateLegalMoves*(self: ChessBoard, location: Location): seq[Move] =
|
||||
## Returns the list of possible legal chess moves for the
|
||||
## piece in the given location
|
||||
for move in self.generateMoves(location):
|
||||
|
@ -836,6 +837,29 @@ proc generateLegalMoves(self: ChessBoard, location: Location): seq[Move] =
|
|||
result.add(move)
|
||||
|
||||
|
||||
proc generateAllMoves*(self: ChessBoard): seq[Move] =
|
||||
## Returns the list of all possible pseudo-legal moves
|
||||
## in the current position
|
||||
for i, row in self.grid:
|
||||
for j, piece in row:
|
||||
if self.grid[i, j].color != self.getActiveColor():
|
||||
continue
|
||||
for move in self.generateMoves((int8(i), int8(j))):
|
||||
result.add(move)
|
||||
|
||||
|
||||
|
||||
proc countLegalMoves*(self: ChessBoard, ply: int): int =
|
||||
## Counts the number of legal positions reached after
|
||||
## the given number of half moves
|
||||
if ply == 0:
|
||||
return 1
|
||||
for move in self.generateAllMoves():
|
||||
if self.isLegal(move):
|
||||
#echo &"{move.piece.color} {move.piece.kind} {move.startSquare.locationToAlgebraic()} {move.targetSquare.locationtoAlgebraic()}"
|
||||
result += self.countLegalMoves(ply - 1)
|
||||
|
||||
|
||||
proc getAttackers*(self: ChessBoard, square: Location): seq[Piece] =
|
||||
## Returns all the attackers of the given square
|
||||
for move in self.position.attacked.black:
|
||||
|
@ -1161,10 +1185,6 @@ proc doMove(self: ChessBoard, move: Move) =
|
|||
castlingAvailable: castlingAvailable,
|
||||
# Updated at the next call to doMove()
|
||||
move: emptyMove(),
|
||||
# Inherit values from current position
|
||||
# (they are updated later anyway)
|
||||
pieces: self.position.pieces,
|
||||
attacked: self.position.attacked,
|
||||
)
|
||||
# Check for double pawn push
|
||||
if move.piece.kind == Pawn and abs(move.startSquare.row - move.targetSquare.row) == 2:
|
||||
|
@ -1246,17 +1266,7 @@ proc undoLastMove*(self: ChessBoard): Move {.discardable.} =
|
|||
|
||||
proc isLegal(self: ChessBoard, move: Move): bool =
|
||||
## Returns whether the given move is legal
|
||||
|
||||
var move = move
|
||||
# Start square doesn't contain a piece (and it isn't the en passant square)
|
||||
# or it's not this player's turn to move
|
||||
if (move.piece.kind == Empty and move.targetSquare != self.getEnPassantTarget()) or move.piece.color != self.getActiveColor():
|
||||
return false
|
||||
var destination = self.grid[move.targetSquare.row, move.targetSquare.col]
|
||||
# Destination square is occupied by a friendly piece
|
||||
if destination.kind != Empty and destination.color == self.getActiveColor():
|
||||
return false
|
||||
|
||||
if move.piece.kind == King and move.piece.color.longCastleKing() + move.startSquare == move.targetSquare:
|
||||
move.flag = CastleLong
|
||||
elif move.piece.kind == King and move.piece.color.shortCastleKing() + move.startSquare == move.targetSquare:
|
||||
|
@ -1266,7 +1276,7 @@ proc isLegal(self: ChessBoard, move: Move): bool =
|
|||
# or otherwise invalid move)
|
||||
return false
|
||||
self.doMove(move)
|
||||
defer: discard self.undoLastMove()
|
||||
defer: self.undoLastMove()
|
||||
# Move would reveal an attack
|
||||
# on our king: not allowed
|
||||
if self.inCheck(move.piece.color):
|
||||
|
@ -1403,4 +1413,10 @@ when isMainModule:
|
|||
# Queens
|
||||
testPiece(b.getPiece("d1"), Queen, White)
|
||||
testPiece(b.getPiece("d8"), Queen, Black)
|
||||
|
||||
when compileOption("profiler"):
|
||||
import nimprof
|
||||
|
||||
echo b.countLegalMoves(3)
|
||||
|
||||
echo "All tests were successful"
|
Loading…
Reference in New Issue