-2

我一直很难找到为什么我的代码很慢。如果有人能弄清楚发生了什么,将不胜感激。每个棋子都表示为一个 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]
4

0 回答 0