Code Sketch


who
By: Mhalsakant School
// === 100% WORKING REAL RULES & 3D PERSPECTIVE CHESS GAME ===

cleari()
originBottomLeft()

val initialBoard = Array(
    Array("r", "n", "b", "q", "k", "b", "n", "r"),
    Array("p", "p", "p", "p", "p", "p", "p", "p"),
    Array(".", ".", ".", ".", ".", ".", ".", "."),
    Array(".", ".", ".", ".", ".", ".", ".", "."),
    Array(".", ".", ".", ".", ".", ".", ".", "."),
    Array(".", ".", ".", ".", ".", ".", ".", "."),
    Array("P", "P", "P", "P", "P", "P", "P", "P"),
    Array("R", "N", "B", "Q", "K", "B", "N", "R")
)

var board = initialBoard
var selectedX = -1
var selectedY = -1
var turn = "WHITE" 
var statusMessage = "White's Turn: Click a piece then destination square."

val tileSize = 65.0
val startX = (cwidth - (8 * tileSize)) / 2.0 - 50.0
val startY = 100.0

// Real Check Validation for all pieces including Pawn (Puda)
def isValidMove(piece: String, startX: Int, startY: Int, endX: Int, endY: Int): Boolean = {
    val dx = (endX - startX).abs
    val dy = (endY - startY).abs
    val p = piece

    p match {
        case "P" => // White Pawn (Puda moves UP: +1 row)
            if (startX == endX && board(endY)(endX) == ".") {
                if (endY - startY == 1) return true
                if (startY == 1 && endY - startY == 2 && board(2)(startX) == ".") return true
            }
            // Capture diagonally
            if (dx == 1 && endY - startY == 1 && board(endY)(endX) != ".") return true
            false

        case "p" => // Black Pawn (Puda moves DOWN: -1 row)
            if (startX == endX && board(endY)(endX) == ".") {
                if (endY - startY == -1) return true
                if (startY == 6 && endY - startY == -2 && board(5)(startX) == ".") return true
            }
            // Capture diagonally
            if (dx == 1 && endY - startY == -1 && board(endY)(endX) != ".") return true
            false

        case "R" | "r" => // Rook (Hatti)
            startX == endX || startY == endY

        case "N" | "n" => // Knight (Ghoda)
            (dx == 2 && dy == 1) || (dx == 1 && dy == 2)

        case "B" | "b" => // Bishop (Unt)
            dx == dy

        case "Q" | "q" => // Queen (Rani)
            (startX == endX || startY == endY) || (dx == dy)

        case "K" | "k" => // King (Raja)
            dx <= 1 && dy <= 1

        case _ => false
    }
}

onMouseClick { (x, y) =>
    val bx = ((x - startX) / tileSize).toInt
    val by = 7 - ((y - startY) / tileSize).toInt

    if (bx >= 0 && bx < 8 && by >= 0 && by < 8) {
        val clickedPiece = board(by)(bx)

        if (selectedX == -1 && selectedY == -1) {
            if (clickedPiece != ".") {
                val isWhitePiece = clickedPiece.toUpperCase == clickedPiece
                if ((turn == "WHITE" && isWhitePiece) || (turn == "BLACK" && !isWhitePiece)) {
                    selectedX = bx
                    selectedY = by
                    statusMessage = s"Selected $clickedPiece. Now click destination."
                } else {
                    statusMessage = s"Not your piece! It's $turn's turn."
                }
            }
        } else {
            val movingPiece = board(selectedY)(selectedX)
            val targetPiece = board(by)(bx)
            val targetIsWhite = targetPiece != "." && targetPiece.toUpperCase == targetPiece
            val movingIsWhite = movingPiece.toUpperCase == movingPiece

            if (!(targetPiece != "." && targetIsWhite == movingIsWhite)) {
                if (isValidMove(movingPiece, selectedX, selectedY, bx, by)) {
                    board(by)(bx) = movingPiece
                    board(selectedY)(selectedX) = "."

                    turn = if (turn == "WHITE") "BLACK" else "WHITE"
                    statusMessage = s"Move successful! Turn: $turn"
                } else {
                    statusMessage = "Invalid move for this piece!"
                }
            } else {
                statusMessage = "Cannot capture your own piece!"
            }

            selectedX = -1
            selectedY = -1
        }
    }
}

def drawChessPiece(canvas: CanvasDraw, piece: String, x: Double, y: Double) {
    val isWhite = piece.toUpperCase == piece
    
    // 3D Shadow Base
    canvas.fill(10, 10, 10, 100)
    canvas.noStroke()
    canvas.ellipse(x + tileSize / 2.0 + 4, y + 12, tileSize * 0.5, tileSize * 0.2)

    if (isWhite) {
        canvas.fill(245, 245, 245)
        canvas.stroke(160, 160, 160)
    } else {
        canvas.fill(45, 45, 45)
        canvas.stroke(90, 90, 90)
    }
    canvas.strokeWeight(2)

    piece.toUpperCase match {
        case "P" =>
            canvas.ellipse(x + tileSize / 2.0, y + tileSize * 0.45, tileSize * 0.32, tileSize * 0.4)
            canvas.ellipse(x + tileSize / 2.0, y + tileSize * 0.22, tileSize * 0.18, tileSize * 0.18)
        case "R" =>
            canvas.rect(x + tileSize * 0.3, y + tileSize * 0.25, tileSize * 0.4, tileSize * 0.5)
        case "N" =>
            canvas.rect(x + tileSize * 0.32, y + tileSize * 0.2, tileSize * 0.35, tileSize * 0.55)
            canvas.ellipse(x + tileSize * 0.52, y + tileSize * 0.3, tileSize * 0.22, tileSize * 0.22)
        case "B" =>
            canvas.ellipse(x + tileSize / 2.0, y + tileSize * 0.45, tileSize * 0.35, tileSize * 0.48)
            canvas.ellipse(x + tileSize / 2.0, y + tileSize * 0.18, tileSize * 0.16, tileSize * 0.16)
        case "Q" =>
            canvas.ellipse(x + tileSize / 2.0, y + tileSize * 0.45, tileSize * 0.4, tileSize * 0.52)
            canvas.ellipse(x + tileSize / 2.0, y + tileSize * 0.15, tileSize * 0.22, tileSize * 0.22)
        case "K" =>
            canvas.ellipse(x + tileSize / 2.0, y + tileSize * 0.45, tileSize * 0.42, tileSize * 0.58)
            canvas.rect(x + tileSize * 0.45, y + tileSize * 0.1, tileSize * 0.1, tileSize * 0.2)
        case _ => 
    }

    canvas.fill(if (isWhite) 40 else 230, if (isWhite) 40 else 230, if (isWhite) 40 else 230)
    canvas.text(piece, x + tileSize / 2.0 - 6, y + tileSize / 2.0 + 5)
}

def viewState(canvas: CanvasDraw) {
    // 3D Background Gradient Style
    canvas.fill(20, 24, 35)
    canvas.noStroke()
    canvas.rect(0, 0, cwidth, cheight)

    // 3D Wooden Board Outer Base
    canvas.fill(85, 50, 20)
    canvas.rect(startX - 15, startY - 15, (8 * tileSize) + 30, (8 * tileSize) + 30)

    var row = 0
    while (row < 8) {
        var col = 0
        while (col < 8) {
            val sqX = startX + (col * tileSize)
            val sqY = startY + ((7 - row) * tileSize)

            if ((row + col) % 2 == 0) {
                canvas.fill(160, 115, 75)
            } else {
                canvas.fill(235, 205, 165)
            }

            if (col == selectedX && row == selectedY) {
                canvas.fill(100, 200, 50)
            }

            canvas.noStroke()
            canvas.rect(sqX, sqY, tileSize, tileSize)

            val piece = board(row)(col)
            if (piece != ".") {
                drawChessPiece(canvas, piece, sqX, sqY)
            }

            col += 1
        }
        row += 1
    }

    // UI Panel
    canvas.fill(255, 255, 255)
    canvas.text(s"3D REAL-RULES CHESS | Turn: $turn", 40, cheight - 40)
    canvas.text(statusMessage, 40, cheight - 15)
}

animateWithCanvasDraw { canvas =>
    viewState(canvas)
}