C

Qt Quick Ultralite chess Example

// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial
import QtQuick 2.15

Rectangle {
    color: "#ffcccccc";

    readonly property int squareSize: Math.min(width, height) / 8;

    property int hoverCol;
    property int hoverRow;

    // The chess board: simply 64 squares of different colors
    Repeater {
        model: 64

        Rectangle {
            property int row: Math.floor(index/8);
            property int col: index % 8;
            z: 0;
            x: col * squareSize
            y: (7 - row) * squareSize
            height: squareSize
            width: squareSize
            color: {
                var even = ((row + col) % 2) == 0;
                if (!ChessModel.canDrop(col, row))
                    return even ? "#d18b47" : "#ffce9e";
                if (row == hoverRow && col == hoverCol)
                    return even ? "#d18bff" : "#ffceff";
                return even ? "#ff8b47" : "#ffaa88";
            }
        }
    }

    // The chess pieces: There are 32 chess pieces, the first 16 are white, and the
    // last 16 are black.
    Repeater {
        model: 32
        Item {
            id: pieceText;
            visible: ChessModel.col(modelData) >= 0;
            x: squareSize * ChessModel.col(modelData);
            y: squareSize * (7 - ChessModel.row(modelData));
            // Note: with QUL, the item in a repeater might not get the same order relative to
            // the item, so we use the z order to ensure that pieces are on top
            z: 1
            height: squareSize
            width: squareSize

            Text {
                color: index < 16 ? "#eee" : "#444"
                text: {
                    var p = index % 16;
                    switch (p) {
                    case 0:
                        return "♚";
                    case 1:
                        return "♛";
                    case 2:
                    case 3:
                        return "♜";
                    case 4:
                    case 5:
                        return "♝";
                    case 6:
                    case 7:
                        return "♞";
                    }
                    return "♟";
                }
                x: (pieceTouch.pressed ? pieceTouch.mouseX - pieceTouch.pressedX : 0);
                y: (pieceTouch.pressed ? pieceTouch.mouseY - pieceTouch.pressedY : 0);
                height: squareSize
                width: squareSize
                horizontalAlignment: Text.AlignHCenter
                verticalAlignment: Text.AlignVCenter
            }
            MouseArea {
                id: pieceTouch;
                anchors.fill: pieceText;
                property real pressedX: 0
                property real pressedY: 0
                onPressed: {
                    pressedX = mouse.x
                    pressedY = mouse.y
                }
                onPressedChanged: {
                    if (pressed) {
                        ChessModel.setActivePiece(modelData);
                    } else {
                        ChessModel.release(modelData, hoverCol,  hoverRow);
                        ChessModel.setActivePiece(-1);
                    }
                }

                onMouseXChanged: hoverCol = (pieceText.x + pieceTouch.mouseX - pieceTouch.pressedX + squareSize / 2) / squareSize;
                onMouseYChanged: hoverRow = 7 - (pieceText.y + pieceTouch.mouseY - pieceTouch.pressedY  - squareSize / 3) / squareSize;
            }
        }
    }

    // Some indicator to show whose turn it is
    Rectangle {
        color: ChessModel.whiteTurn ? "#eee" : "#444"
        width: squareSize;
        height: width / 2;
        anchors.top: parent.width > parent.height ? parent.top : parent.bottom
        anchors.topMargin: parent.width > parent.height ? 0 : -height
        anchors.right: parent.width > parent.height ? parent.right : parent.left
        anchors.rightMargin: parent.width > parent.height ? 0 : -width
    }

    Text {
        id: moveTime
        anchors.bottom: parent.bottom
        anchors.right: parent.right
        text: ChessModel.secondsSinceMove
        visible: ChessModel.secondsSinceMove >= 0
    }

    Text {
        id: invalidLabel
        anchors.bottom: moveTime.top
        anchors.right: parent.right
        text: "Invalid Move!"
        visible: false
    }

    ChessModel.onInvalidMove: invalidLabel.visible = true
    ChessModel.onValidMove: {
        invalidLabel.visible = false;
        console.log("valid move ", col, row);
    }
}