commit cdf46d85efe7fa54e8c302a255214341c567b4af
parent 24c2fd2ffa582ea83172184b1da2816555340a01
Author: Sebastiano Tronto <sebastiano@tronto.net>
Date: Fri, 25 Apr 2025 19:27:06 +0200
Moved qt app to its own repo
Diffstat:
9 files changed, 2 insertions(+), 634 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -9,8 +9,6 @@ utils/.DS_Store
perf.data
perf.data.old
run
-nissyqt
-qt/build
debugrun
shell/lasttest.out
shell/lasttest.err
diff --git a/Makefile b/Makefile
@@ -21,7 +21,7 @@ wasmnissy.o:
${EMCC} ${MACROS} ${WASMFLAGS} -c -o wasmnissy.o src/nissy.c
clean:
- rm -rf *.out *.o *.s *.so run debugrun nissyqt qt/build
+ rm -rf *.out *.o *.s *.so run debugrun
test: debugnissy.o
CC="${CC} ${MACROS} ${DBGFLAGS}" OBJ=debugnissy.o ./test/test.sh
@@ -52,4 +52,4 @@ qt: nissy.o
cd qt/build && make
cp qt/build/appnissyqt ./nissyqt
-.PHONY: all clean test tool debugtool shell debugshell python qt
+.PHONY: all clean test tool debugtool shell debugshell python
diff --git a/README.md b/README.md
@@ -98,13 +98,6 @@ file in the tools/results folder.
To build and run a tool in debug mode, use `make debugtool`.
-## User interfaces
-
-User interfaces for this software are in an experimental stage. Some
-examples are included in this repository, but they are not polished.
-If you are a developer and you are interested in contributing, have a
-look at the examples and let me know!
-
## Command-line interface
The `shell` folder contains the code for a rudimentary shell that can
@@ -158,23 +151,6 @@ F' U' R
For a full list of available command, use `./run help`.
-### QT
-
-A draft for a QT-based GUI is contained in the `qt` folder. In order
-to build it you need QT libraries and headers (for QML / QT Quick
-development) and CMake. If your environment is set up, you can
-build the QT GUI with
-
-```
-$ make qt
-```
-
-And then run it with
-
-```
-$ ./nissyqt
-```
-
## Using this software as a library
This tool has been developed as a library, so it can be easily included
diff --git a/qt/CMakeLists.txt b/qt/CMakeLists.txt
@@ -1,27 +0,0 @@
-cmake_minimum_required(VERSION 3.16)
-
-project(nissyqt VERSION 0.1 LANGUAGES CXX)
-
-set(CMAKE_CXX_STANDARD_REQUIRED ON)
-set(CMAKE_CXX_STANDARD 20)
-
-find_package(Qt6 REQUIRED COMPONENTS Quick Concurrent)
-
-qt_standard_project_setup(REQUIRES 6.5)
-
-qt_add_executable(appnissyqt
- main.cpp
- adapter.cpp adapter.h
- ../cpp/nissy.h ../cpp/nissy.cpp
- ../nissy.o
-)
-
-qt_add_qml_module(appnissyqt
- URI nissyqt
- VERSION 1.0
- QML_FILES Main.qml
-)
-
-target_link_libraries(appnissyqt
- PRIVATE Qt6::Quick
-)
diff --git a/qt/Main.qml b/qt/Main.qml
@@ -1,332 +0,0 @@
-import QtQuick
-import QtQuick.Controls
-import QtQuick.Layouts
-
-Window {
- id: mainWindow
-
- width: 800
- height: 600
- visible: true
- title: "Nissy 3.0 - Preview"
-
- SplitView {
- id: splitView
- anchors.fill: parent
- orientation: Qt.Vertical
-
- handle: Rectangle {
- implicitHeight: 3
- color: SplitHandle.hovered ? "black" : "#AAAAAA"
- }
-
- component MyScrollBar: ScrollBar {
- orientation: Qt.Vertical
- size: parent.height
- policy: ScrollBar.AlwaysOn
- anchors.right: parent.right
- anchors.top: parent.top
- anchors.bottom: parent.bottom
- contentItem: Rectangle {
- implicitWidth: 4
- radius: implicitWidth/2
- color: "black"
- }
- background: Rectangle {
- implicitWidth: 4
- radius: implicitWidth/2
- color: "#AAAAAA"
- }
- }
-
- ColumnLayout {
- id: mainArea
-
- property alias scramble: scrambleRow.scramble
- property alias solver: solverCfg.solver
- property alias minmoves: solverCfg.minmoves
- property alias maxmoves: solverCfg.maxmoves
- property alias maxsolutions: solverCfg.maxsolutions
- property alias optimal: solverCfg.optimal
- property alias sols: sols.text
- property alias solsHeader: solsHeader.text
-
- property bool solutionsLoading: false
-
- anchors.top: parent.top
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.bottom: logView.top
- anchors.margins: 6
- spacing: 10
-
- SplitView.minimumHeight: 180
- SplitView.preferredHeight: 500
-
- component Separator: Rectangle {
- height: 1
- Layout.fillWidth: true
- color: "black"
- }
-
- component OptionalValue: RowLayout {
- property alias currentValue: valueRect.value
- property alias from: spinBox.from
- property alias to: spinBox.to
- property alias defaultValue: spinBox.value
- property alias defaultEnabled: sw.checked
- property alias label: sw.text
- property int defaultSavedValue: 1
- property int savedValue: defaultSavedValue
-
- Switch {
- id: sw
-
- checked: true
-
- onToggled: () => {
- if (checked) {
- currentValue = savedValue
- } else {
- savedValue = currentValue
- currentValue = spinBox.to
- }
- }
- }
-
- Rectangle {
- id: valueRect
-
- property alias enabled: sw.checked
- property alias value: spinBox.value
-
- width: 65
- height: 20
-
- SpinBox {
- id: spinBox
-
- width: parent.width
- editable: true
- enabled: parent.enabled
- }
- }
- }
-
- ColumnLayout {
- id: scrambleRow
-
- property alias scramble: scrambleRowLayout.scramble
-
- RowLayout {
- id: scrambleRowLayout
-
- property alias scramble: scrambleEditor.text
-
- spacing: 6
-
- TextField {
- id: scrambleEditor
-
- placeholderText: "Enter scramble here"
- Layout.fillWidth: true
- padding: 4
-
- readonly property bool empty: text.trim().length == 0
- readonly property bool valid: NissyAdapter.isValidScramble(text)
-
- onAccepted: if (!empty && valid) submitScramble()
- }
-
- Button {
- id: solveButton
-
- enabled: !scrambleEditor.empty && scrambleEditor.valid &&
- !mainArea.solutionsLoading
- text: "Solve!"
-
- onPressed: submitScramble()
- }
- }
-
- Label {
- id: invalidScrambleWarning
- text: scrambleEditor.empty || scrambleEditor.valid ?
- "" : "Invalid Scramble"
- }
- }
-
- Separator {}
-
- ColumnLayout {
- id: solverCfg
-
- property alias minmoves: minMaxRow.min
- property alias maxmoves: minMaxRow.max
- property alias maxsolutions: maxSols.currentValue
- property alias optimal: optimal.currentValue
- property alias solver: solverRow.solver
-
- RowLayout {
- id: solverRow
-
- property alias solver: comboBox.currentValue
-
- Label { text: "Solver" }
- ComboBox {
- id: comboBox
-
- currentIndex: 3
- textRole: "text"
- valueRole: "name"
- implicitContentWidthPolicy: ComboBox.WidestTextWhenCompleted
-
- model: ListModel {
- ListElement { text: "h48 h=0, k=4 (59 Mb)"; name: "h48h0k4" }
- ListElement { text: "h48 h=1, k=2 (115 Mb)"; name: "h48h1k2" }
- ListElement { text: "h48 h=2, k=2 (171 Mb)"; name: "h48h2k2" }
- ListElement { text: "h48 h=3, k=2 (283 Mb)"; name: "h48h3k2" }
- ListElement { text: "h48 h=7, k=2 (3.6 Gb)"; name: "h48h7k2" }
- }
- }
- }
-
- RowLayout {
- id: minMaxRow
-
- property alias min: slider.min
- property alias max: slider.max
-
- Rectangle {
- width: 100
- height: 20
- Label { text: "Min moves: " + slider.min }
- }
- RangeSlider {
- id: slider
- from: 0
- to: 20
- first.value: from
- second.value: to
- stepSize: 1
- snapMode: RangeSlider.SnapAlways
-
- readonly property int min: Math.round(first.value)
- readonly property int max: Math.round(second.value)
- }
- Rectangle {
- width: 100
- height: 20
- Label { text: "Max moves: " + slider.max }
- }
- }
-
- OptionalValue {
- id: optimal
-
- label: "Above optimal by at most"
- from: 0
- to: 20
- defaultValue: 20
- defaultEnabled: false
- defaultSavedValue: 0
- }
-
- OptionalValue {
- id: maxSols
-
- label: "Limit number of solutions to"
- from: 1
- to: 999
- defaultValue: 1
- defaultEnabled: true
- defaultSavedValue: 1
- }
- }
-
- Separator {}
-
- StackLayout {
- Layout.maximumHeight: 30
- currentIndex: mainArea.solutionsLoading ? 0 : 1
-
- BusyIndicator { running: mainArea.solutionsLoading }
- Label { id: solsHeader }
- }
-
- ScrollView {
- Layout.fillHeight: true
- Layout.fillWidth: true
- Layout.bottomMargin: 10
- ScrollBar.vertical: MyScrollBar {}
-
- TextEdit {
- id: sols
- readOnly: true
- font.family: "Monospace"
- }
- }
- }
-
- ScrollView {
- id: logView
-
- property alias text: logText.text
-
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.bottom: parent.bottom
- anchors.margins: 6
-
- SplitView.preferredHeight: 300
-
- background: Rectangle {
- color: "#404040"
- radius: 4
- }
-
- ScrollBar.vertical: MyScrollBar {
- id: scrollBar
- position: 1.0 - size
- }
- Label {
- id: logText
-
- font.family: "Monospace"
- color: "white"
- }
- }
- }
-
- function submitScramble() {
- mainArea.solutionsLoading = true;
- mainArea.solsHeader = ""
- mainArea.sols = ""
- logView.text = ""
- NissyAdapter.requestSolve(
- mainArea.scramble,
- mainArea.solver,
- mainArea.minmoves,
- mainArea.maxmoves,
- mainArea.maxsolutions,
- mainArea.optimal
- )
- }
-
- Connections {
- target: NissyAdapter
- function onSolutionsReady(header, sols) {
- mainArea.solutionsLoading = false
- mainArea.solsHeader = header
- mainArea.sols = sols
- }
- function onSolverError(msg) {
- mainArea.solutionsLoading = false
- mainArea.solsHeader = msg
- mainArea.sols = ""
- }
- function onAppendLog(msg) {
- logView.text += msg
- }
- }
-}
diff --git a/qt/Makefile b/qt/Makefile
@@ -1,15 +0,0 @@
-all: nissyqt
-
-nissyqt:
- mkdir -p build
- cmake . -B build
- cd build && make
- cp build/appnissyqt ./run
-
-run:
- QT_LOGGING_RULES="*.debug=true; qt.*.debug=false" ./run
-
-clean:
- rm -rf run build
-
-.PHONY: all nissyqt run clean
diff --git a/qt/adapter.cpp b/qt/adapter.cpp
@@ -1,161 +0,0 @@
-#include "adapter.h"
-
-#include <fstream>
-#include <sstream>
-#include <string>
-#include <vector>
-#include <QDebug>
-#include <QtConcurrent/QtConcurrent>
-
-void logWrapper(const char *str, void *data)
-{
- auto f = *reinterpret_cast<std::function<void(std::string)>*>(data);
- f(std::string{str});
-}
-
-NissyAdapter::NissyAdapter()
-{
- // TODO: this list must be kept in sync with UI code, it is a bit ugly
- std::vector<std::string> solverNames {
- "h48h0k4",
- "h48h1k2",
- "h48h2k2",
- "h48h3k2",
- "h48h7k2",
- };
-
- for (auto s : solverNames)
- initSolver(s);
-
- writeLog = [&](std::string str) {
- emit appendLog(QString::fromStdString(str));
- };
-
- nissy::set_logger(&logWrapper, &writeLog);
-}
-
-void NissyAdapter::initSolver(const std::string& s) {
- auto se = nissy::solver::get(s);
- if (std::holds_alternative<nissy::error>(se)) {
- qDebug("Error loading solver!");
- return;
- }
- auto ss = std::get<nissy::solver>(se);
- solvers.push_back(ss);
-}
-
-bool NissyAdapter::loadSolverData(nissy::solver& solver) {
- if (solver.data_checked)
- return true;
-
- std::filesystem::path filePath("./tables/" + solver.id);
- if (!std::filesystem::exists(filePath)) {
- logLine("Data file for solver " + solver.name + " not found, "
- "generating it...");
- auto err = solver.generate_data();
- if (!err.ok()) {
- emit solverError(QString("Error generating data!"));
- return false;
- }
- std::filesystem::create_directory("./tables/");
- std::ofstream ofs(filePath, std::ios::binary);
- ofs.write(reinterpret_cast<char *>(solver.data.data()),
- solver.size);
- ofs.close();
- logLine("Data generated succesfully");
- } else {
- logLine("Reading data for solver " + solver.name +
- " from file");
- std::ifstream ifs(filePath, std::ios::binary);
- solver.read_data(ifs);
- ifs.close();
- logLine("Data loaded");
- }
-
- logLine("Checking data integrity "
- "(this is done only once per solver per session)...");
- if (!solver.check_data().ok()) {
- emit solverError(QString("Error reading data!"));
- return false;
- }
- logLine("Data checked");
-
- return true;
-}
-
-Q_INVOKABLE bool NissyAdapter::isValidScramble(QString qscr)
-{
- nissy::cube c;
- return c.move(qscr.toStdString()).ok();
-}
-
-Q_INVOKABLE void NissyAdapter::requestSolve(
- QString scramble,
- QString solver,
- int minmoves,
- int maxmoves,
- int maxsolutions,
- int optimal
-)
-{
- nissy::cube c;
- if (!c.move(scramble.toStdString()).ok()) {
- emit solverError(QString("Unexpected error: invalid scramble"));
- return;
- }
-
- nissy::solver *ss = nullptr;
- for (auto& s : solvers)
- if (s.name == solver)
- ss = &s;
- if (ss == nullptr) {
- std::string msg = "Error: solver '" + solver.toStdString()
- + "' not available";
- emit solverError(QString::fromStdString(msg));
- return;
- }
-
- SolveOptions opts{c, ss, (unsigned)minmoves, (unsigned)maxmoves,
- (unsigned)maxsolutions, (unsigned)optimal};
- auto _ = QtConcurrent::run(&NissyAdapter::startSolve, this, opts);
- return;
-}
-
-void NissyAdapter::startSolve(SolveOptions opts)
-{
- loadSolverData(*opts.solver);
-
- auto result = opts.solver->solve(opts.cube, nissy::nissflag::NORMAL,
- opts.minmoves, opts.maxmoves, opts.maxsolutions, opts.optimal, 8);
-
- if (!result.err.ok()) {
- std::string msg = "Error computing solutions: " +
- std::to_string(result.err.value);
- emit solverError(QString::fromStdString(msg));
- return;
- }
-
- auto& sols = result.solutions;
- if (sols.size() == 0) {
- emit solutionsReady("No solution found", "");
- } else {
- std::stringstream hs;
- hs << "Found " << sols.size() << " solution"
- << (sols.size() > 1 ? "s:" : ":");
-
- std::stringstream ss;
- for (auto s : sols) {
- auto n = nissy::count_moves(s).value;
- ss << s << " (" << n << ")" << std::endl; // TODO: remove last newline
- }
- emit solutionsReady(QString::fromStdString(hs.str()),
- QString::fromStdString(ss.str()));
- }
-}
-
-void NissyAdapter::logLine(std::string str)
-{
- std::stringstream ss;
- ss << str << std::endl;
- writeLog(ss.str());
-}
diff --git a/qt/adapter.h b/qt/adapter.h
@@ -1,55 +0,0 @@
-#ifndef ADAPTER_H
-#define ADAPTER_H
-
-#include "../cpp/nissy.h"
-
-#include <map>
-#include <string>
-#include <QObject>
-#include <QtQmlIntegration>
-
-struct SolveOptions {
- nissy::cube cube;
- nissy::solver *solver;
- unsigned minmoves;
- unsigned maxmoves;
- unsigned maxsolutions;
- unsigned optimal;
-};
-
-class NissyAdapter : public QObject {
- Q_OBJECT
- QML_SINGLETON
- QML_ELEMENT
-
-public:
- static constexpr int maxSolutionsHardLimit = 9999;
-
- NissyAdapter();
-
- Q_INVOKABLE bool isValidScramble(QString);
- Q_INVOKABLE void requestSolve(
- QString scramble,
- QString solver,
- int minmoves,
- int maxmoves,
- int maxsolutions,
- int optimal
- );
-
-signals:
- void solutionsReady(QString, QString);
- void solverError(QString);
- void appendLog(QString);
-
-private:
- std::vector<nissy::solver> solvers;
- std::function<void(std::string)> writeLog;
-
- void initSolver(const std::string&);
- void startSolve(SolveOptions);
- bool loadSolverData(nissy::solver&);
- void logLine(std::string);
-};
-
-#endif
diff --git a/qt/main.cpp b/qt/main.cpp
@@ -1,16 +0,0 @@
-#include "adapter.h"
-
-#include <QGuiApplication>
-#include <QQmlApplicationEngine>
-
-int main(int argc, char *argv[])
-{
- QGuiApplication app(argc, argv);
- QQmlApplicationEngine engine;
- QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed,
- &app, []() { QCoreApplication::exit(-1); }, Qt::QueuedConnection);
-
- engine.loadFromModule("nissyqt", "Main");
-
- return app.exec();
-}