Reputation: 11
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