aboutsummaryrefslogtreecommitdiff
path: root/qt
diff options
context:
space:
mode:
Diffstat (limited to 'qt')
-rw-r--r--qt/CMakeLists.txt27
-rw-r--r--qt/Main.qml332
-rw-r--r--qt/Makefile15
-rw-r--r--qt/adapter.cpp161
-rw-r--r--qt/adapter.h55
-rw-r--r--qt/main.cpp16
6 files changed, 0 insertions, 606 deletions
diff --git a/qt/CMakeLists.txt b/qt/CMakeLists.txt
deleted file mode 100644
index 42c6d52..0000000
--- a/qt/CMakeLists.txt
+++ /dev/null
@@ -1,27 +0,0 @@
1cmake_minimum_required(VERSION 3.16)
2
3project(nissyqt VERSION 0.1 LANGUAGES CXX)
4
5set(CMAKE_CXX_STANDARD_REQUIRED ON)
6set(CMAKE_CXX_STANDARD 20)
7
8find_package(Qt6 REQUIRED COMPONENTS Quick Concurrent)
9
10qt_standard_project_setup(REQUIRES 6.5)
11
12qt_add_executable(appnissyqt
13 main.cpp
14 adapter.cpp adapter.h
15 ../cpp/nissy.h ../cpp/nissy.cpp
16 ../nissy.o
17)
18
19qt_add_qml_module(appnissyqt
20 URI nissyqt
21 VERSION 1.0
22 QML_FILES Main.qml
23)
24
25target_link_libraries(appnissyqt
26 PRIVATE Qt6::Quick
27)
diff --git a/qt/Main.qml b/qt/Main.qml
deleted file mode 100644
index a6e034a..0000000
--- a/qt/Main.qml
+++ /dev/null
@@ -1,332 +0,0 @@
1import QtQuick
2import QtQuick.Controls
3import QtQuick.Layouts
4
5Window {
6 id: mainWindow
7
8 width: 800
9 height: 600
10 visible: true
11 title: "Nissy 3.0 - Preview"
12
13 SplitView {
14 id: splitView
15 anchors.fill: parent
16 orientation: Qt.Vertical
17
18 handle: Rectangle {
19 implicitHeight: 3
20 color: SplitHandle.hovered ? "black" : "#AAAAAA"
21 }
22
23 component MyScrollBar: ScrollBar {
24 orientation: Qt.Vertical
25 size: parent.height
26 policy: ScrollBar.AlwaysOn
27 anchors.right: parent.right
28 anchors.top: parent.top
29 anchors.bottom: parent.bottom
30 contentItem: Rectangle {
31 implicitWidth: 4
32 radius: implicitWidth/2
33 color: "black"
34 }
35 background: Rectangle {
36 implicitWidth: 4
37 radius: implicitWidth/2
38 color: "#AAAAAA"
39 }
40 }
41
42 ColumnLayout {
43 id: mainArea
44
45 property alias scramble: scrambleRow.scramble
46 property alias solver: solverCfg.solver
47 property alias minmoves: solverCfg.minmoves
48 property alias maxmoves: solverCfg.maxmoves
49 property alias maxsolutions: solverCfg.maxsolutions
50 property alias optimal: solverCfg.optimal
51 property alias sols: sols.text
52 property alias solsHeader: solsHeader.text
53
54 property bool solutionsLoading: false
55
56 anchors.top: parent.top
57 anchors.left: parent.left
58 anchors.right: parent.right
59 anchors.bottom: logView.top
60 anchors.margins: 6
61 spacing: 10
62
63 SplitView.minimumHeight: 180
64 SplitView.preferredHeight: 500
65
66 component Separator: Rectangle {
67 height: 1
68 Layout.fillWidth: true
69 color: "black"
70 }
71
72 component OptionalValue: RowLayout {
73 property alias currentValue: valueRect.value
74 property alias from: spinBox.from
75 property alias to: spinBox.to
76 property alias defaultValue: spinBox.value
77 property alias defaultEnabled: sw.checked
78 property alias label: sw.text
79 property int defaultSavedValue: 1
80 property int savedValue: defaultSavedValue
81
82 Switch {
83 id: sw
84
85 checked: true
86
87 onToggled: () => {
88 if (checked) {
89 currentValue = savedValue
90 } else {
91 savedValue = currentValue
92 currentValue = spinBox.to
93 }
94 }
95 }
96
97 Rectangle {
98 id: valueRect
99
100 property alias enabled: sw.checked
101 property alias value: spinBox.value
102
103 width: 65
104 height: 20
105
106 SpinBox {
107 id: spinBox
108
109 width: parent.width
110 editable: true
111 enabled: parent.enabled
112 }
113 }
114 }
115
116 ColumnLayout {
117 id: scrambleRow
118
119 property alias scramble: scrambleRowLayout.scramble
120
121 RowLayout {
122 id: scrambleRowLayout
123
124 property alias scramble: scrambleEditor.text
125
126 spacing: 6
127
128 TextField {
129 id: scrambleEditor
130
131 placeholderText: "Enter scramble here"
132 Layout.fillWidth: true
133 padding: 4
134
135 readonly property bool empty: text.trim().length == 0
136 readonly property bool valid: NissyAdapter.isValidScramble(text)
137
138 onAccepted: if (!empty && valid) submitScramble()
139 }
140
141 Button {
142 id: solveButton
143
144 enabled: !scrambleEditor.empty && scrambleEditor.valid &&
145 !mainArea.solutionsLoading
146 text: "Solve!"
147
148 onPressed: submitScramble()
149 }
150 }
151
152 Label {
153 id: invalidScrambleWarning
154 text: scrambleEditor.empty || scrambleEditor.valid ?
155 "" : "Invalid Scramble"
156 }
157 }
158
159 Separator {}
160
161 ColumnLayout {
162 id: solverCfg
163
164 property alias minmoves: minMaxRow.min
165 property alias maxmoves: minMaxRow.max
166 property alias maxsolutions: maxSols.currentValue
167 property alias optimal: optimal.currentValue
168 property alias solver: solverRow.solver
169
170 RowLayout {
171 id: solverRow
172
173 property alias solver: comboBox.currentValue
174
175 Label { text: "Solver" }
176 ComboBox {
177 id: comboBox
178
179 currentIndex: 3
180 textRole: "text"
181 valueRole: "name"
182 implicitContentWidthPolicy: ComboBox.WidestTextWhenCompleted
183
184 model: ListModel {
185 ListElement { text: "h48 h=0, k=4 (59 Mb)"; name: "h48h0k4" }
186 ListElement { text: "h48 h=1, k=2 (115 Mb)"; name: "h48h1k2" }
187 ListElement { text: "h48 h=2, k=2 (171 Mb)"; name: "h48h2k2" }
188 ListElement { text: "h48 h=3, k=2 (283 Mb)"; name: "h48h3k2" }
189 ListElement { text: "h48 h=7, k=2 (3.6 Gb)"; name: "h48h7k2" }
190 }
191 }
192 }
193
194 RowLayout {
195 id: minMaxRow
196
197 property alias min: slider.min
198 property alias max: slider.max
199
200 Rectangle {
201 width: 100
202 height: 20
203 Label { text: "Min moves: " + slider.min }
204 }
205 RangeSlider {
206 id: slider
207 from: 0
208 to: 20
209 first.value: from
210 second.value: to
211 stepSize: 1
212 snapMode: RangeSlider.SnapAlways
213
214 readonly property int min: Math.round(first.value)
215 readonly property int max: Math.round(second.value)
216 }
217 Rectangle {
218 width: 100
219 height: 20
220 Label { text: "Max moves: " + slider.max }
221 }
222 }
223
224 OptionalValue {
225 id: optimal
226
227 label: "Above optimal by at most"
228 from: 0
229 to: 20
230 defaultValue: 20
231 defaultEnabled: false
232 defaultSavedValue: 0
233 }
234
235 OptionalValue {
236 id: maxSols
237
238 label: "Limit number of solutions to"
239 from: 1
240 to: 999
241 defaultValue: 1
242 defaultEnabled: true
243 defaultSavedValue: 1
244 }
245 }
246
247 Separator {}
248
249 StackLayout {
250 Layout.maximumHeight: 30
251 currentIndex: mainArea.solutionsLoading ? 0 : 1
252
253 BusyIndicator { running: mainArea.solutionsLoading }
254 Label { id: solsHeader }
255 }
256
257 ScrollView {
258 Layout.fillHeight: true
259 Layout.fillWidth: true
260 Layout.bottomMargin: 10
261 ScrollBar.vertical: MyScrollBar {}
262
263 TextEdit {
264 id: sols
265 readOnly: true
266 font.family: "Monospace"
267 }
268 }
269 }
270
271 ScrollView {
272 id: logView
273
274 property alias text: logText.text
275
276 anchors.left: parent.left
277 anchors.right: parent.right
278 anchors.bottom: parent.bottom
279 anchors.margins: 6
280
281 SplitView.preferredHeight: 300
282
283 background: Rectangle {
284 color: "#404040"
285 radius: 4
286 }
287
288 ScrollBar.vertical: MyScrollBar {
289 id: scrollBar
290 position: 1.0 - size
291 }
292 Label {
293 id: logText
294
295 font.family: "Monospace"
296 color: "white"
297 }
298 }
299 }
300
301 function submitScramble() {
302 mainArea.solutionsLoading = true;
303 mainArea.solsHeader = ""
304 mainArea.sols = ""
305 logView.text = ""
306 NissyAdapter.requestSolve(
307 mainArea.scramble,
308 mainArea.solver,
309 mainArea.minmoves,
310 mainArea.maxmoves,
311 mainArea.maxsolutions,
312 mainArea.optimal
313 )
314 }
315
316 Connections {
317 target: NissyAdapter
318 function onSolutionsReady(header, sols) {
319 mainArea.solutionsLoading = false
320 mainArea.solsHeader = header
321 mainArea.sols = sols
322 }
323 function onSolverError(msg) {
324 mainArea.solutionsLoading = false
325 mainArea.solsHeader = msg
326 mainArea.sols = ""
327 }
328 function onAppendLog(msg) {
329 logView.text += msg
330 }
331 }
332}
diff --git a/qt/Makefile b/qt/Makefile
deleted file mode 100644
index 5b13026..0000000
--- a/qt/Makefile
+++ /dev/null
@@ -1,15 +0,0 @@
1all: nissyqt
2
3nissyqt:
4 mkdir -p build
5 cmake . -B build
6 cd build && make
7 cp build/appnissyqt ./run
8
9run:
10 QT_LOGGING_RULES="*.debug=true; qt.*.debug=false" ./run
11
12clean:
13 rm -rf run build
14
15.PHONY: all nissyqt run clean
diff --git a/qt/adapter.cpp b/qt/adapter.cpp
deleted file mode 100644
index cb690a4..0000000
--- a/qt/adapter.cpp
+++ /dev/null
@@ -1,161 +0,0 @@
1#include "adapter.h"
2
3#include <fstream>
4#include <sstream>
5#include <string>
6#include <vector>
7#include <QDebug>
8#include <QtConcurrent/QtConcurrent>
9
10void logWrapper(const char *str, void *data)
11{
12 auto f = *reinterpret_cast<std::function<void(std::string)>*>(data);
13 f(std::string{str});
14}
15
16NissyAdapter::NissyAdapter()
17{
18 // TODO: this list must be kept in sync with UI code, it is a bit ugly
19 std::vector<std::string> solverNames {
20 "h48h0k4",
21 "h48h1k2",
22 "h48h2k2",
23 "h48h3k2",
24 "h48h7k2",
25 };
26
27 for (auto s : solverNames)
28 initSolver(s);
29
30 writeLog = [&](std::string str) {
31 emit appendLog(QString::fromStdString(str));
32 };
33
34 nissy::set_logger(&logWrapper, &writeLog);
35}
36
37void NissyAdapter::initSolver(const std::string& s) {
38 auto se = nissy::solver::get(s);
39 if (std::holds_alternative<nissy::error>(se)) {
40 qDebug("Error loading solver!");
41 return;
42 }
43 auto ss = std::get<nissy::solver>(se);
44 solvers.push_back(ss);
45}
46
47bool NissyAdapter::loadSolverData(nissy::solver& solver) {
48 if (solver.data_checked)
49 return true;
50
51 std::filesystem::path filePath("./tables/" + solver.id);
52 if (!std::filesystem::exists(filePath)) {
53 logLine("Data file for solver " + solver.name + " not found, "
54 "generating it...");
55 auto err = solver.generate_data();
56 if (!err.ok()) {
57 emit solverError(QString("Error generating data!"));
58 return false;
59 }
60 std::filesystem::create_directory("./tables/");
61 std::ofstream ofs(filePath, std::ios::binary);
62 ofs.write(reinterpret_cast<char *>(solver.data.data()),
63 solver.size);
64 ofs.close();
65 logLine("Data generated succesfully");
66 } else {
67 logLine("Reading data for solver " + solver.name +
68 " from file");
69 std::ifstream ifs(filePath, std::ios::binary);
70 solver.read_data(ifs);
71 ifs.close();
72 logLine("Data loaded");
73 }
74
75 logLine("Checking data integrity "
76 "(this is done only once per solver per session)...");
77 if (!solver.check_data().ok()) {
78 emit solverError(QString("Error reading data!"));
79 return false;
80 }
81 logLine("Data checked");
82
83 return true;
84}
85
86Q_INVOKABLE bool NissyAdapter::isValidScramble(QString qscr)
87{
88 nissy::cube c;
89 return c.move(qscr.toStdString()).ok();
90}
91
92Q_INVOKABLE void NissyAdapter::requestSolve(
93 QString scramble,
94 QString solver,
95 int minmoves,
96 int maxmoves,
97 int maxsolutions,
98 int optimal
99)
100{
101 nissy::cube c;
102 if (!c.move(scramble.toStdString()).ok()) {
103 emit solverError(QString("Unexpected error: invalid scramble"));
104 return;
105 }
106
107 nissy::solver *ss = nullptr;
108 for (auto& s : solvers)
109 if (s.name == solver)
110 ss = &s;
111 if (ss == nullptr) {
112 std::string msg = "Error: solver '" + solver.toStdString()
113 + "' not available";
114 emit solverError(QString::fromStdString(msg));
115 return;
116 }
117
118 SolveOptions opts{c, ss, (unsigned)minmoves, (unsigned)maxmoves,
119 (unsigned)maxsolutions, (unsigned)optimal};
120 auto _ = QtConcurrent::run(&NissyAdapter::startSolve, this, opts);
121 return;
122}
123
124void NissyAdapter::startSolve(SolveOptions opts)
125{
126 loadSolverData(*opts.solver);
127
128 auto result = opts.solver->solve(opts.cube, nissy::nissflag::NORMAL,
129 opts.minmoves, opts.maxmoves, opts.maxsolutions, opts.optimal, 8);
130
131 if (!result.err.ok()) {
132 std::string msg = "Error computing solutions: " +
133 std::to_string(result.err.value);
134 emit solverError(QString::fromStdString(msg));
135 return;
136 }
137
138 auto& sols = result.solutions;
139 if (sols.size() == 0) {
140 emit solutionsReady("No solution found", "");
141 } else {
142 std::stringstream hs;
143 hs << "Found " << sols.size() << " solution"
144 << (sols.size() > 1 ? "s:" : ":");
145
146 std::stringstream ss;
147 for (auto s : sols) {
148 auto n = nissy::count_moves(s).value;
149 ss << s << " (" << n << ")" << std::endl; // TODO: remove last newline
150 }
151 emit solutionsReady(QString::fromStdString(hs.str()),
152 QString::fromStdString(ss.str()));
153 }
154}
155
156void NissyAdapter::logLine(std::string str)
157{
158 std::stringstream ss;
159 ss << str << std::endl;
160 writeLog(ss.str());
161}
diff --git a/qt/adapter.h b/qt/adapter.h
deleted file mode 100644
index e961459..0000000
--- a/qt/adapter.h
+++ /dev/null
@@ -1,55 +0,0 @@
1#ifndef ADAPTER_H
2#define ADAPTER_H
3
4#include "../cpp/nissy.h"
5
6#include <map>
7#include <string>
8#include <QObject>
9#include <QtQmlIntegration>
10
11struct SolveOptions {
12 nissy::cube cube;
13 nissy::solver *solver;
14 unsigned minmoves;
15 unsigned maxmoves;
16 unsigned maxsolutions;
17 unsigned optimal;
18};
19
20class NissyAdapter : public QObject {
21 Q_OBJECT
22 QML_SINGLETON
23 QML_ELEMENT
24
25public:
26 static constexpr int maxSolutionsHardLimit = 9999;
27
28 NissyAdapter();
29
30 Q_INVOKABLE bool isValidScramble(QString);
31 Q_INVOKABLE void requestSolve(
32 QString scramble,
33 QString solver,
34 int minmoves,
35 int maxmoves,
36 int maxsolutions,
37 int optimal
38 );
39
40signals:
41 void solutionsReady(QString, QString);
42 void solverError(QString);
43 void appendLog(QString);
44
45private:
46 std::vector<nissy::solver> solvers;
47 std::function<void(std::string)> writeLog;
48
49 void initSolver(const std::string&);
50 void startSolve(SolveOptions);
51 bool loadSolverData(nissy::solver&);
52 void logLine(std::string);
53};
54
55#endif
diff --git a/qt/main.cpp b/qt/main.cpp
deleted file mode 100644
index a012642..0000000
--- a/qt/main.cpp
+++ /dev/null
@@ -1,16 +0,0 @@
1#include "adapter.h"
2
3#include <QGuiApplication>
4#include <QQmlApplicationEngine>
5
6int main(int argc, char *argv[])
7{
8 QGuiApplication app(argc, argv);
9 QQmlApplicationEngine engine;
10 QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed,
11 &app, []() { QCoreApplication::exit(-1); }, Qt::QueuedConnection);
12
13 engine.loadFromModule("nissyqt", "Main");
14
15 return app.exec();
16}

Generated with cgit - Back to sebastiano.tronto.net