C

Qt Quick Ultralite chess Example

/****************************************************************************** ** ** Copyright (C) 2020 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Quick Ultralite module. ** ** $QT_BEGIN_LICENSE:COMM$ ** ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** $QT_END_LICENSE$ ** ******************************************************************************/
#pragma once #include <qul/property.h> #include <qul/singleton.h> #include <qul/timer.h> #include <qul/signal.h> /** * This is a very simple example demonstrating how to integrate a C++ Object * with Qt Quick Ultralite * * The example does not conform to the rules of chess, it has been simplified * on purpose * * The idea is just to expose a few properties and functions from a Singleton. * By inheriting from Qul::Singleton, we tell the qmlinterfacegenerator tool * to expose the type to QML. (the qul_target_generate_interfaces macro in CMakeLists.txt * will make sure that the tool is ran on this file) */ struct ChessModel : Qul::Singleton<ChessModel> { /// This property is public and will be exposed to QML Qul::Property<bool> whiteTurn; Qul::Property<int> secondsSinceMove; Qul::Signal<void(int col, int row)> validMove; Qul::Signal<void()> invalidMove; /// Return the current column position of a specific piece /// (or -1 if it is not in the chess board). /// This function is public and can be called from the QML int col(int piece) { return position[piece].value().first; } /// Return the current row position of a specific piece /// (or -1 if it is not in the chess board). /// This function is public and can be called from the QML int row(int piece) { return position[piece].value().second; } /// Return true if it is allowed to drop the piece on thie case /// This function is public and can be called from the QML bool canDrop(int col, int row) { if (col < 0 || col > 7 || row < 0 || row > 7) return false; return squares[col][row].value().canDrop; } /// Set the current active piece /// (if piece is negative, there shall not be any active piece) /// This function is public and can be called from the QML void setActivePiece(int piece) { if (piece < 0) { for (int r = 0; r < 8; ++r) for (int c = 0; c < 8; ++c) { SquareInfo g = squares[c][r].value(); if (g.canDrop) { g.canDrop = false; squares[c][r].setValue(g); } } return; } if (whiteTurn.value() ? piece >= 16 : piece < 16) return; // piece of the wrong color Coordinates currentPos = position[piece].value(); squares[currentPos.first][currentPos.second].setValue(SquareInfo(piece, true)); switch (piece % 16) { case 0: // King setMove(piece, currentPos.first + 1, currentPos.second); setMove(piece, currentPos.first - 1, currentPos.second); setMove(piece, currentPos.first, currentPos.second + 1); setMove(piece, currentPos.first, currentPos.second - 1); setMove(piece, currentPos.first + 1, currentPos.second + 1); setMove(piece, currentPos.first - 1, currentPos.second - 1); setMove(piece, currentPos.first + 1, currentPos.second - 1); setMove(piece, currentPos.first - 1, currentPos.second + 1); break; case 1: // Queen for (int i = 1; setMove(piece, currentPos.first + i, currentPos.second); ++i) ; for (int i = 1; setMove(piece, currentPos.first - i, currentPos.second); ++i) ; for (int i = 1; setMove(piece, currentPos.first, currentPos.second + i); ++i) ; for (int i = 1; setMove(piece, currentPos.first, currentPos.second - i); ++i) ; for (int i = 1; setMove(piece, currentPos.first + i, currentPos.second + i); ++i) ; for (int i = 1; setMove(piece, currentPos.first - i, currentPos.second + i); ++i) ; for (int i = 1; setMove(piece, currentPos.first + i, currentPos.second - i); ++i) ; for (int i = 1; setMove(piece, currentPos.first - i, currentPos.second - i); ++i) ; break; case 2: case 3: // Rock for (int i = 1; setMove(piece, currentPos.first + i, currentPos.second); ++i) ; for (int i = 1; setMove(piece, currentPos.first - i, currentPos.second); ++i) ; for (int i = 1; setMove(piece, currentPos.first, currentPos.second + i); ++i) ; for (int i = 1; setMove(piece, currentPos.first, currentPos.second - i); ++i) ; break; case 4: case 5: // Bishop for (int i = 1; setMove(piece, currentPos.first + i, currentPos.second + i); ++i) ; for (int i = 1; setMove(piece, currentPos.first - i, currentPos.second + i); ++i) ; for (int i = 1; setMove(piece, currentPos.first + i, currentPos.second - i); ++i) ; for (int i = 1; setMove(piece, currentPos.first - i, currentPos.second - i); ++i) ; break; case 6: case 7: // Knight setMove(piece, currentPos.first + 1, currentPos.second + 2); setMove(piece, currentPos.first + 1, currentPos.second - 2); setMove(piece, currentPos.first - 1, currentPos.second + 2); setMove(piece, currentPos.first - 1, currentPos.second - 2); setMove(piece, currentPos.first + 2, currentPos.second + 1); setMove(piece, currentPos.first + 2, currentPos.second - 1); setMove(piece, currentPos.first - 2, currentPos.second + 1); setMove(piece, currentPos.first - 2, currentPos.second - 1); break; default: // Pawn if (whiteTurn.value()) { if (setMove(piece, currentPos.first, currentPos.second + 1, true) && currentPos.second == 1) setMove(piece, currentPos.first, currentPos.second + 2, true); pawnTake(currentPos.first + 1, currentPos.second + 1); pawnTake(currentPos.first - 1, currentPos.second + 1); } else { if (setMove(piece, currentPos.first, currentPos.second - 1, true) && currentPos.second == 6) setMove(piece, currentPos.first, currentPos.second - 2, true); pawnTake(currentPos.first + 1, currentPos.second - 1); pawnTake(currentPos.first - 1, currentPos.second - 1); } } } /// Release the given piece in the given position. void release(int piece, int col, int row) { if (!canDrop(col, row)) { invalidMove(); return; } Coordinates pos = position[piece].value(); if (pos.first == col && pos.second == row) return; // remove the piece from its old position squares[pos.first][pos.second].setValue(SquareInfo()); // set the new position of the piece position[piece].setValue(std::make_pair(col, row)); // Take the old piece out of the board int old = squares[col][row].value().piece; if (old >= 0) { position[old].setValue(std::make_pair(-1, -1)); } // set the piece in its new square squares[col][row].setValue(SquareInfo(piece)); whiteTurn.setValue(!whiteTurn.value()); secondsSinceMove.setValue(0); moveTimer.start(); validMove(col, row); } ChessModel() : whiteTurn(true) { // Set the chessboard to the initial position for (int c = 0; c <= 1; ++c) { int r = c == 0 ? 0 : 7; int o = c == 0 ? 0 : 16; position[o + 0].setValue(std::make_pair(4, r)); position[o + 1].setValue(std::make_pair(3, r)); position[o + 2].setValue(std::make_pair(0, r)); position[o + 3].setValue(std::make_pair(7, r)); position[o + 4].setValue(std::make_pair(2, r)); position[o + 5].setValue(std::make_pair(5, r)); position[o + 6].setValue(std::make_pair(1, r)); position[o + 7].setValue(std::make_pair(6, r)); r = c == 0 ? 1 : 6; o += 8; for (int p = 0; p < 8; ++p) { position[o + p].setValue(std::make_pair(p, r)); } } for (int i = 0; i < 16 * 2; ++i) { const Coordinates &pp = position[i].value(); squares[pp.first][pp.second].setValue(SquareInfo(i)); } secondsSinceMove.setValue(-1); // Set a timer to fire every seconds to update the secondsSinceMove value IncrementMoveTime incrementer; incrementer.model = this; moveTimer.onTimeout(incrementer); moveTimer.setInterval(1000); } private: // These private properties are not directly accessible from QML. // But by calling value() or setValue() on them, we still create // bindings to them. struct SquareInfo { explicit SquareInfo(int piece_ = -1, bool canDrop_ = false) : piece(piece_) , canDrop(canDrop_) {} int piece; bool canDrop; bool operator==(const SquareInfo &other) const { return piece == other.piece && canDrop == other.canDrop; } }; Qul::Property<SquareInfo> squares[8][8]; typedef std::pair<int, int> Coordinates; Qul::Property<Coordinates> position[32]; // Example of a timer and its slot (initialized in the constructor) Qul::Timer moveTimer; struct IncrementMoveTime { ChessModel *model; void operator()() { model->secondsSinceMove.setValue(model->secondsSinceMove.value() + 1); } }; /// Helper function to set that the piece can be dropped on the given square // returns true if the square was empty bool setMove(int, int col, int row, bool pawn = false) { if (col < 0 || col > 7 || row < 0 || row > 7) return false; int old = squares[col][row].value().piece; if (old < 0) { squares[col][row].setValue(SquareInfo(old, true)); return true; } if (pawn) // pawn can't take like normal piece return false; // One can only take a piece of the opposite color if (whiteTurn.value() ? old >= 16 : old < 16) { squares[col][row].setValue(SquareInfo(old, true)); } return false; }; /// Helper function to set that a pawn can be dropped in the given location if /// it takes another piece void pawnTake(int col, int row) { if (col < 0 || col > 7 || row < 0 || row > 7) return; int old = squares[col][row].value().piece; if (old < 0) return; if (whiteTurn.value() ? old >= 16 : old < 16) { squares[col][row].setValue(SquareInfo(old, true)); } }; };