justAkid
justAkid

Reputation: 11

How do i fix a logic error meaning that an incorrect piece is moved in my spritekit game?

I am making a basic chess game in Swift with Spritekit:

    func moveSelectedPiece(to newPosition: (Int, Int)) {
        guard let pieceToMove = selectedPiece else { return }

        // Calculate the target position on the board
        let targetPosition = board[newPosition.0][newPosition.1].position

        // Check if there is a piece to capture at the new position
        if pieces[newPosition.0][newPosition.1] != nil {
            // Capture the piece
            capturePiece(at: newPosition)
        }

        // Create the move action
        let moveAction = SKAction.move(to: targetPosition, duration: 0.3)

        // Run the move action
        pieceToMove.run(moveAction) {
            // Update the pieces array
            let currentPiecePosition = self.pieces.enumerated().compactMap { rowIndex, row in
                row.enumerated().compactMap { colIndex, piece in
                    piece == pieceToMove ? (rowIndex, colIndex) : nil
                }.first
            }.first ?? (0, 0)

            self.pieces[currentPiecePosition.0][currentPiecePosition.1] = nil
            self.pieces[newPosition.0][newPosition.1] = pieceToMove

            // Reset the previous selected square color
            self.deselectSquare()

            // Handle pawn promotion
            if pieceToMove.name == "whitepawn" && newPosition.0 == 7 {
                pieceToMove.texture = SKTexture(imageNamed: "whitequeen")
                pieceToMove.name = "whitequeen"
            } else if pieceToMove.name == "blackpawn" && newPosition.0 == 0 {
                pieceToMove.texture = SKTexture(imageNamed: "blackqueen")
                pieceToMove.name = "blackqueen"
            }

            // Use pieceToMove instead of selectedPiece
            self.isInCheck((pieceToMove.name?.hasPrefix("white")) ?? false ? "black" : "white")
            
            // Reset selectedPiece
            self.selectedPiece = nil
        }
    }

    func isInCheck(_ side: String, showChecks: Bool = true) -> Bool {
        guard side == "black" || side == "white" else { return false }
        print("side is valid! side = \(side)\n")
        
        // Find the king's position
        var kingPosition: CGPoint? = nil
        var kingBoardPosition: (Int, Int) = (0, 0)
        for piece in pieces {
            for piece_ in piece {
                if piece_?.name == "\(side)king" {
                    kingPosition = piece_?.position
                    print("kingPosition set to: \(kingPosition)\n")
                    for (rowIndex, row) in board.enumerated() {
                        for (colIndex, tile) in row.enumerated() {
                            if tile.position == kingPosition {
                                kingBoardPosition = (rowIndex, colIndex)
                                print("kingBoardPosition set to: \(kingBoardPosition)\n")
                            }
                        }
                    }
                    break
                }
            }
        }

        guard let kingPos = kingPosition else { return false }
        print("kingPosition is valid!\n")

        let originalPiece = selectedPiece
        
        // Check if any opponent's piece can move to the king's position
        for piece in pieces {
            for piece_ in piece where piece_?.name?.hasPrefix(side) == false {
                self.selectedPiece = piece_
                
                let moves = evaluateMoves(checkForChecks: false)
                for move in moves {
                    if board[move.0][move.1].contains(kingPos) {
                        print("isInCheck returns true!")
                        return true
                    }
                }
            }
        }

        selectedPiece = originalPiece
        if selectedPiece?.name != "\(side)king" {
            let defaultColor: SKColor = (kingBoardPosition.0 + kingBoardPosition.1) % 2 == 0 ? .darkGray : .white
            board[kingBoardPosition.0][kingBoardPosition.1].fillColor = defaultColor
        }
        print("isInCheck returns false!")
        return false
    }

    override func mouseDown(with event: NSEvent) {
        let location = event.location(in: self)
        let boardRow = Int((location.y + tileSize * 4) / tileSize)
        let boardCol = Int((location.x + tileSize * 4) / tileSize)

        // Deselect the previously selected square
        deselectSquare()

        if let piece = selectedPiece {
            // Evaluate the valid moves for the selected piece
            let validMoves = evaluateMoves(showCheckedChecks: false)

            // Check if the touch location is one of the valid moves
            if validMoves.contains(where: { $0 == (boardRow, boardCol) }) {
                // Move the selected piece
                moveSelectedPiece(to: (boardRow, boardCol))

                // Toggle the turn
                isWhitesMove.toggle()
                selectedPiece = nil // Ensure the piece is deselected after the move
                return
            } else {
                // Deselect the piece if the move is invalid
                selectedPiece = nil
            }
        }

        guard boardCol >= 0 && boardCol <= 7 && boardRow >= 0 && boardRow <= 7 else { return }
        
        // Select the piece at the touch location
        if let piece = pieces[boardRow][boardCol] {
            // Check if the piece belongs to the correct side
            if (isWhitesMove && piece.name?.contains("white") == true) || (!isWhitesMove && piece.name?.contains("black") == true) {
                self.selectPiece(at: (boardRow, boardCol))
            } else {
                // No piece is selected, ensure selectedPiece is nil
                selectedPiece = nil
            }
        } else {
            // No piece is selected, ensure selectedPiece is nil
            selectedPiece = nil
        }
    }

Please see the full project for a lot more important functions and all the files to run this yourself if you want. This on it's own in not quite the entire problem.

Full app (for macOS): https://github.com/meepy6/Broken-Chess-Game

I need to fix some logic errors where if i check the king and then i select another piece and try to move it to block the king, the piece checking (eg. if i had originally done Bb5+, the bishop) would move to that square instead. I have got this to work before but i tried adding something which i had to undo. The logic error is probably around selectedPiece being changed unexpectedly, but the complex codeflow has made it tricky for me to figure out where. Apart from this, all other movement is correct.

I had tried adding debug statements, with no success. I had had some code to fix another error which i had to undo, so in the debug statements i had figured out that in the isInCheck function, false positives were arising. Now however, i find that I couldn't really make sense of the debug anyway, hence the removal from the code.

The problematic part of code (i think!) is the isInCheck function. The movePiece function works fine and the mouseDown could potentially have an issue from it being the entry point for triggering moves. There are also several functions in the full file such as selectPiece, deselectSquare and renderBoard etc. that won't be causing the issue because they don't change the selectedPiece or they do so in a way that would not produce the problem. In general, a function that doesn't reference selectedPiece will be OK.

So in the actual code from mouseDown, I check to see if the selectedPiece has a valid move on that click location with evaluateMoves (btw, that could be the problem due to it checking for illegal moves that induce check. I would've included it now, but it is very long) and if so, i move it and deselect the piece etc., otherwise deselecting the piece. If the piece is being selected, i simply highlight the piece and select it.

Upvotes: 1

Views: 34

Answers (0)

Related Questions