我一直很难找到为什么我的代码很慢。如果有人能弄清楚发生了什么,将不胜感激。每个棋子都表示为一个 5 位数字,每个动作都表示为一个 12 位数字。
计算不同棋子移动的测试 正如您所见,它可以在大约一秒内计算所有不同棋子的移动 100,000 次。
移动每一块的测试如您所见,移动每一块 100,000 次也需要大约一秒钟。(撤消移动的速度大约是原来的两倍)
使用不同参数计算所有移动的测试请注意,这里只有 1000 次测试,而不是像其他的 100,000 次。搜索时计算,不检查检查要快得多。因此,这将需要花费的时间缩小到计算移动函数的最后一部分,在该函数中它删除了所有导致检查的移动。但是,MakeMove 和 UnmakeMove 函数都很快,正如我们之前所知道的那样。
检查King 是否在check但是,它在不搜索时唯一要做的另一部分是查看King 是否在check,这也相当快。在 1.7 秒内完成 10,000 次测试。
因此,我不确定是什么让它变慢了,所以如果有人能弄清楚那就太好了。以下是 Board 类中涉及的函数:
某些上下文的初始化函数:
def __init__(self, passedFen = None):
loadPositionInfo = FenUtility.LoadFromFen(passedFen)
self.squares = loadPositionInfo.squares
self.whiteToMove = loadPositionInfo.whiteToMove
# Gamestate
# 0-3 = Castling (0 = K, 3 = q)
# 4-7 = file of ep square (8 = none)
# 8-13 = captured piece
# 14-... = fiftymove
whiteCastle = loadPositionInfo.whiteCastleKingside | loadPositionInfo.whiteCastleQueenside << 1
blackCastle = loadPositionInfo.blackCastleKingside << 2 | loadPositionInfo.blackCastleQueenside << 3
self.__gameState = whiteCastle | blackCastle | loadPositionInfo.epFile << 4 | loadPositionInfo.halfMove << 14
self.__gameStateHistory = [self.__gameState]
self.__plyCount = loadPositionInfo.plyCount
self.__moveList = []
self.squares = loadPositionInfo.squares
# King list
self.kingSquares = [0 for _ in range(2)]
for index, square in enumerate(self.squares):
if PieceRepresentation.Type(square) == PieceRepresentation.king:
# If Black
if PieceRepresentation.IsColour(square, PieceRepresentation.black):
self.kingSquares[self.blackIndex] = index
else:
self.kingSquares[self.whiteIndex] = index
移动:
def MakeMove(self, move):
self.__moveList.append(move)
# Update information
self.whiteToMove = not self.whiteToMove
self.__plyCount += 1
# Gamestate
epFile = 8 if move.moveFlag != Move2.Flag.pawnTwoForward else BoardRepresentation.FileIndex(move.startSquare)
castling = self.__gameState & 0b1111
# Moving white king (or moving piece from that square afterwards means it's already moved so it's fine to remove castling rights again)
if move.startSquare == self.__ssWhiteKing: castling &= 0b1100
elif move.startSquare == self.__ssBlackKing: castling &= 0b0011 # Black King
elif move.startSquare == self.__ssKRook: castling &= 0b1110 # White Kingside Rook
elif move.startSquare == self.__ssQRook: castling &= 0b1101 # White Queenside Rook
elif move.startSquare == self.__sskRook: castling &= 0b1011 # Black Kingside Rook
elif move.startSquare == self.__ssqRook: castling &= 0b0111 # Black Queenside Rook
capturedPiece = self.squares[move.endSquare]
halfMove = self.__gameState >> 14
if ( capturedPiece != PieceRepresentation.none # If capturing
or PieceRepresentation.Type(self.squares[move.startSquare]) == PieceRepresentation.pawn ): # or advancing a pawn
halfMove = 0 # reset half-move clock
else:
halfMove += 1 # otherwise increment
self.__gameState = castling | epFile << 4 | capturedPiece << 8 | halfMove << 14
self.__gameStateHistory.append(self.__gameState)
# Move Piece
# En Passant
if move.moveFlag == Move2.Flag.enPassantCapture:
self.squares[ move.endSquare + (8 if PieceRepresentation.IsColour(self.squares[move.startSquare], PieceRepresentation.black) else -8) ] \
= PieceRepresentation.none
# Castling
elif move.moveFlag == Move2.Flag.castling:
rookStartSquare = ( move.endSquare + 1 if move.endSquare > move.startSquare # Kingside
else move.endSquare - 1 )
rookEndSquare = (move.startSquare + move.endSquare) // 2
# Move rook
self.squares[rookEndSquare] = self.squares[rookStartSquare]
self.squares[rookStartSquare] = PieceRepresentation.none
# Promotion
if not move.isPromotion:
endPiece = self.squares[move.startSquare]
else:
pieceColour = PieceRepresentation.Colour(self.squares[move.startSquare])
if move.moveFlag == Move2.Flag.promoteToQueen:
endPieceType = PieceRepresentation.queen
elif move.moveFlag == Move2.Flag.promoteToKnight:
endPieceType = PieceRepresentation.knight
elif move.moveFlag == Move2.Flag.promoteToRook:
endPieceType = PieceRepresentation.rook
elif move.moveFlag == Move2.Flag.promoteToBishop:
endPieceType = PieceRepresentation.bishop
endPiece = pieceColour + endPieceType
# Move piece
self.squares[move.endSquare] = endPiece
self.squares[move.startSquare] = PieceRepresentation.none
# Moving king
if PieceRepresentation.Type(endPiece) == PieceRepresentation.king:
colourIndex = self.whiteIndex if PieceRepresentation.IsColour(endPiece, PieceRepresentation.white) else self.blackIndex
self.kingSquares[colourIndex] = move.endSquare
取消移动
def UnmakeMove(self):
moveToUndo = self.__moveList.pop()
self.squares[moveToUndo.startSquare] = (
PieceRepresentation.Colour(self.squares[moveToUndo.endSquare]) # Colour of piece
+ PieceRepresentation.pawn # Resets to pawn
if moveToUndo.isPromotion # If the move was a promotion
else self.squares[moveToUndo.endSquare] ) # Otherwise just the end square value
self.squares[moveToUndo.endSquare] = self.__capturedPiece # Remove piece from end square
# Castling
if moveToUndo.moveFlag == Move2.Flag.castling:
if moveToUndo.startSquare > moveToUndo.endSquare: # Queenside
self.squares[moveToUndo.endSquare -1] = self.squares[moveToUndo.endSquare +1]
self.squares[moveToUndo.endSquare +1] = PieceRepresentation.none
else:
self.squares[moveToUndo.endSquare +1] = self.squares[moveToUndo.endSquare -1]
self.squares[moveToUndo.endSquare -1] = PieceRepresentation.none
# En Passant
elif moveToUndo.moveFlag == Move2.Flag.enPassantCapture:
enemyPawn = PieceRepresentation.pawn + PieceRepresentation.GetEnemyColour(self.squares[moveToUndo.startSquare])
enemyPawnSquare = BoardRepresentation.RankFileToSquare(
BoardRepresentation.RankIndex(moveToUndo.startSquare),
BoardRepresentation.FileIndex(moveToUndo.endSquare) )
self.squares[enemyPawnSquare] = enemyPawn
# Update Information
self.__gameStateHistory.pop()
self.__gameState = self.__gameStateHistory[-1]
self.whiteToMove = not self.whiteToMove
self.__plyCount -= 1
CalculateAllLegalMoves
def CalculateAllLegalMoves(self, checkOpponentsMovesInstead = False, inSearch = False):
# If inSearch, Pawns check empty squares diagonally and it's not checked if the move results in being in check, meaning it checks if the previous player is in check.
legalMoves = []
for pieceSquare, piece in enumerate(self.squares):
if (piece == PieceRepresentation.none # Empty square
or ( not checkOpponentsMovesInstead and not PieceRepresentation.IsColour(piece, self.colourToMove) )
# Checking currents players moves and it's not that player's piece
or ( checkOpponentsMovesInstead and PieceRepresentation.IsColour(piece, self.colourToMove) )
# Checking opponents moves to see if in check and it's not that player's piece
):
continue
elif PieceRepresentation.Type(piece) == PieceRepresentation.king:
# King
if not inSearch: # King cannot deliver check so no point. Also creates recursion through checking castling
legalMoves += self.__CalculateMoves(pieceSquare, isKing = True)
# inSearch only matters for checking that castling doesn't move through check because we remove all moves in check after find all.
elif PieceRepresentation.Type(piece) == PieceRepresentation.queen:
# Queen
legalMoves += self.__CalculateMoves(pieceSquare)
elif PieceRepresentation.Type(piece) == PieceRepresentation.rook:
# Rook
legalMoves += self.__CalculateMoves(pieceSquare, end = 3)
elif PieceRepresentation.Type(piece) == PieceRepresentation.bishop:
# Bishop
legalMoves += self.__CalculateMoves(pieceSquare, start = 4)
elif PieceRepresentation.Type(piece) == PieceRepresentation.knight:
# Knight
legalMoves += self.__CalculateMoves(pieceSquare, isKnight = True)
elif PieceRepresentation.Type(piece) == PieceRepresentation.pawn:
# Pawn
legalMoves += self.__CalculatePawnMoves(pieceSquare, inSearch=inSearch)
# Remove moves resulting in check only if not inSearch
if not inSearch:
colourMoving = self.colourToMove
i = 0
while i < len(legalMoves):
self.MakeMove(legalMoves[i])
if self.KingInCheck(colourMoving):
legalMoves.pop(i)
else:
i += 1
self.UnmakeMove()
if checkOpponentsMovesInstead:
self.__allOpponentsMoves = legalMoves
else:
self.__allLegalMoves = legalMoves
计算典当移动
def __CalculatePawnMoves(self, pieceSquare, inSearch = False) -> list:
legalMoves = []
pieceColour = PieceRepresentation.Colour(self.squares[pieceSquare])
if pieceColour == PieceRepresentation.white:
direction = 1
startingRank = 1
else:
direction = -1
startingRank = 6
pawnRank = BoardRepresentation.RankIndex(pieceSquare)
aboutToPromote = pawnRank == startingRank + direction*5
onEpRank = pawnRank == startingRank + direction*3
# 1 Forwards
targetSquare = pieceSquare + 8*direction
if ( self.squares[targetSquare] == PieceRepresentation.none
and not inSearch ): # Pawns can't check empty squares in front of them for ensuring king doesn't castle through check.
if aboutToPromote:
legalMoves += [
Move2(pieceSquare, targetSquare, Move2.Flag.promoteToQueen),
Move2(pieceSquare, targetSquare, Move2.Flag.promoteToKnight),
Move2(pieceSquare, targetSquare, Move2.Flag.promoteToRook),
Move2(pieceSquare, targetSquare, Move2.Flag.promoteToBishop),
]
else:
legalMoves.append( Move2(pieceSquare, targetSquare) )
# 2 Forwards (only works if next line is empty and not on penultimate rank which is previously checked)
if BoardRepresentation.RankIndex(pieceSquare) == startingRank:
targetSquare = pieceSquare + 16*direction
if self.squares[targetSquare] == PieceRepresentation.none:
legalMoves.append( Move2(pieceSquare, targetSquare, Move2.Flag.pawnTwoForward) )
# Normal Captures
targetSquares = []
pawnFile = BoardRepresentation.FileIndex(pieceSquare)
if pawnFile != 0: targetSquares.append(pieceSquare + 8*direction - 1)
if pawnFile != 7: targetSquares.append(pieceSquare + 8*direction + 1)
for targetSquare in targetSquares:
if ( PieceRepresentation.IsEnemyPiece(self.squares[pieceSquare], self.squares[targetSquare])
or inSearch): # Can check empty squares diagonally for checking king doesn't move through check when castling
if aboutToPromote:
legalMoves += [
Move2(pieceSquare, targetSquare, Move2.Flag.promoteToQueen),
Move2(pieceSquare, targetSquare, Move2.Flag.promoteToKnight),
Move2(pieceSquare, targetSquare, Move2.Flag.promoteToRook),
Move2(pieceSquare, targetSquare, Move2.Flag.promoteToBishop),
]
else:
legalMoves.append( Move2(pieceSquare, targetSquare) )
# En Passant
if ( onEpRank # If on the correct rank
and BoardRepresentation.FileIndex(targetSquare) == self.__epFile # and the correct file to en passant
# Assuming target square is empty and square next to pawn is an enemy pawn because otherwise the ep state would not be this file
):
legalMoves.append( Move2(pieceSquare, targetSquare, Move2.Flag.enPassantCapture) )
return legalMoves
计算移动
def __CalculateMoves(self, pieceSquare:int, start:int=0, end:int=7, isKing:bool=False, isKnight:bool=False, inSearch:bool=False) -> list:
legalMoves = []
directions = self.__cardinalDirections[start:end+1] if not isKnight else self.__knightDirections
pieceColour = PieceRepresentation.Colour(self.squares[pieceSquare])
for direction in directions:
rank = BoardRepresentation.RankIndex(pieceSquare)
file = BoardRepresentation.FileIndex(pieceSquare)
while True:
# Update the current square being checked
rank += direction[0]
file += direction[1]
if not (0 <= rank <= 7 and 0 <= file <= 7):
# If off board
break
endSquarePiece = self.GetBoardValue(rank, file)
if PieceRepresentation.IsColour(endSquarePiece, pieceColour):
# Or own piece
break
landingOnEnemyPiece = PieceRepresentation.IsEnemyPiece(pieceColour, endSquarePiece)
if ( endSquarePiece == PieceRepresentation.none # If empty square
or landingOnEnemyPiece ): # Or enemy piece
legalMoves.append(Move2(pieceSquare, rank*8+file))
if landingOnEnemyPiece:
# Don't search beyond enemy piece
break
# Limits king and knight moves to 1 jump
if isKnight or isKing:
break
if isKing:
# Castling
castlingRights = self.__whiteCastling if pieceColour == PieceRepresentation.white else self.__blackCastling
# Queenside
if castlingRights & 0b10 == 0b10:
# Check squares between are empty
if sum(self.squares[pieceSquare-3:pieceSquare]) == PieceRepresentation.none:
# Check not moving through check
if not True in [self.SquareInCheck(s, pieceColour) for s in range(pieceSquare-2, pieceSquare+1)]:
legalMoves.append( Move2(pieceSquare, pieceSquare-2, Move2.Flag.castling) )
# Kingside
if castlingRights & 0b01 == 0b01:
# Check squares between are empty
if sum(self.squares[pieceSquare+1:pieceSquare+3]) == PieceRepresentation.none:
# Check not moving through check
if not True in [self.SquareInCheck(s, pieceColour) for s in range(pieceSquare, pieceSquare+3)]:
legalMoves.append( Move2(pieceSquare, pieceSquare+2, Move2.Flag.castling) )
return legalMoves
KingInCheck
def KingInCheck(self, colour):
kingPos = self.kingSquares[self.whiteIndex] if colour == PieceRepresentation.white else self.kingSquares[self.blackIndex]
return self.SquareInCheck(kingPos, colour)
SquareInCheck
def SquareInCheck(self, square, whoInCheck=None):
if not whoInCheck:
whoInCheck = self.colourToMove # Default to checking if current player is in check
if whoInCheck == self.colourToMove: # If opponent is checking
self.CalculateAllLegalMoves(checkOpponentsMovesInstead = True, inSearch = True)
return square in [move.endSquare for move in self.__allOpponentsMoves]
else: # If current player is checking
self.CalculateAllLegalMoves(inSearch = True)
return square in [move.endSquare for move in self.__allLegalMoves]