aboutsummaryrefslogtreecommitdiff
path: root/web/http
diff options
context:
space:
mode:
authorSebastiano Tronto <sebastiano@tronto.net>2025-05-30 14:06:34 +0200
committerSebastiano Tronto <sebastiano@tronto.net>2025-05-30 14:06:34 +0200
commit37a2ebd4bffa99d465396fde0acfc52284ee3b7c (patch)
tree617921a35782fd81bbf76dcbd09ccc539361155e /web/http
parentdc47c318bd7272099ec8a66ddc48810f91fa52b9 (diff)
downloadnissy-core-37a2ebd4bffa99d465396fde0acfc52284ee3b7c.tar.gz
nissy-core-37a2ebd4bffa99d465396fde0acfc52284ee3b7c.zip
More improvements to web solver
Diffstat (limited to '')
-rw-r--r--web/http/index.html77
-rw-r--r--web/http/nissyapp.mjs94
-rw-r--r--web/http/worker.mjs28
3 files changed, 165 insertions, 34 deletions
diff --git a/web/http/index.html b/web/http/index.html
index 04ddf4c..bc6a37d 100644
--- a/web/http/index.html
+++ b/web/http/index.html
@@ -1,13 +1,16 @@
1<!doctype html> 1<!doctype html>
2<html lang="en-US"> 2<html lang="en-US">
3 <head> 3<head>
4 <meta charset="utf-8" /> 4 <meta charset="utf-8" />
5 <meta name="viewport" content="width=device-width" /> 5 <meta name="viewport" content="width=device-width" />
6 <title>Nissy - H48 solver POC</title> 6 <title>Nissy - H48 solver POC</title>
7 <script type="module" src="./nissyapp.mjs"></script> 7 <script type="module" src="./nissyapp.mjs"></script>
8 </head> 8</head>
9 <body> 9
10 <input id="scrambleText" placeholder="Type the scramble here..."> 10<body>
11
12 <div id="scrambleRaw">
13 <input id="scrambleText" placeholder="Type the scramble here..." />
11 <select id="solverSelector"> 14 <select id="solverSelector">
12 <option value="h48h3k2" selected="selected"> 15 <option value="h48h3k2" selected="selected">
13 h48 h=4 k=2 (300 Mb) - light 16 h48 h=4 k=2 (300 Mb) - light
@@ -17,22 +20,44 @@
17 </option> 20 </option>
18 </select> 21 </select>
19 <button id="solveButton">Solve</button> 22 <button id="solveButton">Solve</button>
20 <div id="confirmDownload" style="display:none"> 23 <button id="pauseResumeButton" disabled>Pause</button>
21 <p id="confirmDownloadText"> 24 <button id="cancelSolveButton" disabled>Cancel</button>
22 For this solver to work, a large data table is required. 25 </div>
23 You can either download it or generate it locally. 26
24 </p> 27 <div id="solverConfiguration">
25 <p id="confirmDownloadExtraText"> 28 <label id="minLabel" for="minSlider">Minimum moves: 0</label><br />
26 In either case, this data will be stored locally for future use. You 29 <input id="minMovesSlider" name="minSlider"
27 can delete it at any time by clearing your browser's data. 30 type="range" min="0" max="20" value="0" />
28 </p> 31 <br />
29 <button id="confirmDownloadCancel">Cancel</button> 32 <label id="maxLabel" for="maxSlider">Maximum moves: 20</label><br />
30 <button id="confirmDownloadConfirm">Download</button> 33 <input id="maxMovesSlider" name="maxSlider"
31 <button id="confirmDownloadGenerate">Generate locally</button> 34 type="range" min="0" max="20" value="20" />
32 </div> 35 <br />
33 <p id="resultsLabel"></p> 36 <label for="maxSolutions">Limit number of solutions to</label>
34 <p id="results"></p> 37 <input id="maxSolutions" name="maxSolutions"
35 <button id="toggleShowLog">Show nissy log messages</button> 38 type="number" min="1" max="999" value="1">
36 <p id="logPane" style="display:none"></p> 39 </div>
37 </body> 40
41 <div id="confirmDownload" style="display:none">
42 <p id="confirmDownloadText">
43 For this solver to work, a large data table is required.
44 You can either download it or generate it locally.
45 </p>
46 <p id="confirmDownloadExtraText">
47 In either case, this data will be stored locally for future use. You
48 can delete it at any time by clearing your browser's data.
49 </p>
50 <button id="confirmDownloadCancel">Cancel</button>
51 <button id="confirmDownloadConfirm">Download</button>
52 <button id="confirmDownloadGenerate">Generate locally</button>
53 </div>
54
55
56 <p id="resultsLabel"></p>
57 <p id="results"></p>
58 <button id="toggleShowLog">Show nissy log messages</button>
59 <p id="logPane" style="display:none"></p>
60
61</body>
62
38</html> 63</html>
diff --git a/web/http/nissyapp.mjs b/web/http/nissyapp.mjs
index 1f9fa72..bdc553a 100644
--- a/web/http/nissyapp.mjs
+++ b/web/http/nissyapp.mjs
@@ -3,16 +3,31 @@
3// to this folder when running ./build web. 3// to this folder when running ./build web.
4 4
5var solveButton = document.getElementById("solveButton"); 5var solveButton = document.getElementById("solveButton");
6var pauseResumeButton = document.getElementById("pauseResumeButton");
7var cancelSolveButton = document.getElementById("cancelSolveButton");
6var scrField = document.getElementById("scrambleText"); 8var scrField = document.getElementById("scrambleText");
7var resultsLabel = document.getElementById("resultsLabel"); 9var resultsLabel = document.getElementById("resultsLabel");
8var resultsText = document.getElementById("results"); 10var resultsText = document.getElementById("results");
9var solverSelector = document.getElementById("solverSelector"); 11var solverSelector = document.getElementById("solverSelector");
10var toggleLog = document.getElementById("toggleShowLog"); 12var toggleLog = document.getElementById("toggleShowLog");
11var logPane = document.getElementById("logPane"); 13var logPane = document.getElementById("logPane");
14var minSlider = document.getElementById("minMovesSlider");
15var maxSlider = document.getElementById("maxMovesSlider");
16var minLabel = document.getElementById("minLabel");
17var maxLabel = document.getElementById("maxLabel");
18var maxSolsInput = document.getElementById("maxSolutions");
12 19
20var solveStatus = "run"; // For now this is global
13var lastCallbackId = 0; 21var lastCallbackId = 0;
14var callbacks = new Map(); // Values: { f: function, arg: object } 22var callbacks = new Map(); // Values: { f: function, arg: object }
15var worker = new Worker("./worker.mjs", { type: "module" }); 23var worker = new Worker("./worker.mjs", { type: "module" });
24
25// Periodically send status updates to the worker
26
27const sendStatusUpdateToWorker = () =>
28 worker.postMessage({command: "update status", id: -1, arg: solveStatus });
29setInterval(() => sendStatusUpdateToWorker(), 500);
30
16worker.onmessage = (event) => { 31worker.onmessage = (event) => {
17 if (event.data.command == "log") { 32 if (event.data.command == "log") {
18 logPane.innerText += event.data.object; 33 logPane.innerText += event.data.object;
@@ -32,8 +47,48 @@ function updateResults(label, results, enable) {
32 resultsText.innerText = results; 47 resultsText.innerText = results;
33 solveButton.disabled = !enable; 48 solveButton.disabled = !enable;
34 solverSelector.disabled = !enable; 49 solverSelector.disabled = !enable;
50 minSlider.disabled = !enable;
51 maxSlider.disabled = !enable;
52 maxSolsInput.disabled = !enable;
35} 53}
36 54
55scrField.addEventListener("input", (e) => {
56 const scramble = scrField.value;
57
58 const callbackId = ++lastCallbackId;
59 callbacks.set(callbackId, {
60 f: (callbackArg, validateResult) => {
61 if (validateResult) {
62 scrField.style.border = "";
63 } else {
64 scrField.style.border = "2px solid red";
65 }
66 },
67 arg: scramble
68 });
69
70 worker.postMessage({
71 command: "validate scramble",
72 id: callbackId,
73 arg: scramble
74 });
75});
76
77minSlider.addEventListener("input", () =>
78 minLabel.innerText = "Minimum moves: " + minSlider.value);
79
80maxSlider.addEventListener("input", () =>
81 maxLabel.innerText = "Maximum moves: " + maxSlider.value);
82
83maxSolsInput.addEventListener("keyup", () => {
84 if (maxSolsInput.value != "") {
85 if (parseInt(maxSolsInput.value) < parseInt(maxSolsInput.min))
86 maxSolsInput.value = maxSolsInput.min;
87 if (parseInt(maxSolsInput.value) > parseInt(maxSolsInput.max))
88 maxSolsInput.value = maxSolsInput.max;
89 }
90});
91
37var logVisible = false; 92var logVisible = false;
38toggleLog.addEventListener("click", () => { 93toggleLog.addEventListener("click", () => {
39 logVisible = !logVisible; 94 logVisible = !logVisible;
@@ -67,7 +122,7 @@ solveButton.addEventListener("click", () => {
67 122
68 worker.postMessage({ 123 worker.postMessage({
69 command: "validate scramble", 124 command: "validate scramble",
70 id: lastCallbackId, 125 id: callbackId,
71 arg: scramble 126 arg: scramble
72 }); 127 });
73}); 128});
@@ -110,7 +165,10 @@ function askDownloadThenSolve(solver, scramble) {
110 updateResults("", "", false); 165 updateResults("", "", false);
111 confirmDiv.style.display = "block"; 166 confirmDiv.style.display = "block";
112 167
113 cancel.addEventListener("click", cleanup); 168 cancel.addEventListener("click", () => {
169 cleanup();
170 updateResults("", "", true);
171 });
114 172
115 download.addEventListener("click", () => { 173 download.addEventListener("click", () => {
116 cleanup(); 174 cleanup();
@@ -153,12 +211,38 @@ function downloadOrGenerateThenSolve(solver, scramble, download) {
153 }); 211 });
154} 212}
155 213
214pauseResumeButton.addEventListener("click", (e) => {
215 if (pauseResumeButton.innerText == "Pause") {
216 solveStatus = "pause";
217 pauseResumeButton.innerText = "Resume";
218 updateResults("Solver paused - click Resume to continue", "", false);
219 } else {
220 solveStatus = "run";
221 pauseResumeButton.innerText = "Pause";
222 updateResults("Solving...", "", false);
223 }
224});
225
226cancelSolveButton.addEventListener("click", (e) => {
227 solveStatus = "stop";
228 pauseResumeButton.innerText = "Pause";
229});
230
156function startSolve(solver, scramble) { 231function startSolve(solver, scramble) {
157 updateResults("Solving...", "", false); 232 updateResults("Solving...", "", false);
158 233
159 const callbackId = ++lastCallbackId; 234 const callbackId = ++lastCallbackId;
235 pauseResumeButton.disabled = false;
236 cancelSolveButton.disabled = false;
237 solveStatus = "run";
238
160 callbacks.set(callbackId, { 239 callbacks.set(callbackId, {
161 f: (callbackArg, solveResult) => { 240 f: (callbackArg, solveResult) => {
241 pauseResumeButton.disabled = true;
242 cancelSolveButton.disabled = true;
243 pauseResumeButton.innerText = "Pause";
244 solveStatus = "stop";
245
162 if (solveResult.success) { 246 if (solveResult.success) {
163 const n = solveResult.solutions.length; 247 const n = solveResult.solutions.length;
164 const label = n == 0 ? "No solution found" : 248 const label = n == 0 ? "No solution found" :
@@ -178,9 +262,9 @@ function startSolve(solver, scramble) {
178 arg: { 262 arg: {
179 solver: solver, 263 solver: solver,
180 scramble: scramble, 264 scramble: scramble,
181 minmoves: 0, 265 minmoves: minSlider.value,
182 maxmoves: 20, 266 maxmoves: maxSlider.value,
183 maxsolutions: 1, 267 maxsolutions: maxSolsInput.value,
184 optimal: 20, 268 optimal: 20,
185 threads: window.navigator.hardwareConcurrency, 269 threads: window.navigator.hardwareConcurrency,
186 } 270 }
diff --git a/web/http/worker.mjs b/web/http/worker.mjs
index 12de1dc..d912e8b 100644
--- a/web/http/worker.mjs
+++ b/web/http/worker.mjs
@@ -4,12 +4,19 @@ const nissy = await Nissy();
4const log = (msg) => postMessage({ command: "log", id: -1, object: msg }); 4const log = (msg) => postMessage({ command: "log", id: -1, object: msg });
5nissy.setLogger(nissy._addCallbackFunction(log)); 5nissy.setLogger(nissy._addCallbackFunction(log));
6 6
7var solveStatus = nissy.statusRUN; // For now this is a global variable
8const pollStatusCallback = nissy._addCallbackFunction(() => {
9console.log("Calling pollstatus, returning " + solveStatus);
10return solveStatus;
11});
12
7const commands = [ 13const commands = [
8 { name: "load solver data", exec: loadSolverDataFromStorage }, 14 { name: "load solver data", exec: loadSolverDataFromStorage },
9 { name: "download solver data", exec: downloadSolverData }, 15 { name: "download solver data", exec: downloadSolverData },
10 { name: "generate solver data", exec: generateSolverData }, 16 { name: "generate solver data", exec: generateSolverData },
11 { name: "validate scramble", exec: validateScramble }, 17 { name: "validate scramble", exec: validateScramble },
12 { name: "solve", exec: solve }, 18 { name: "solve", exec: solve },
19 { name: "update status", exec: updateStatus },
13]; 20];
14 21
15// Message structure: 22// Message structure:
@@ -161,10 +168,11 @@ async function solve(id, arg) {
161 return; 168 return;
162 } 169 }
163 170
171 solveStatus = nissy.statusRUN;
172
164 var cube = new nissy.Cube(); 173 var cube = new nissy.Cube();
165 cube.move(arg.scramble); 174 cube.move(arg.scramble);
166 175
167 // TODO: error handling here?
168 const nissFlag = ((str) => { 176 const nissFlag = ((str) => {
169 switch (str) { 177 switch (str) {
170 case "inverse": return nissy.NissFlag.inverse; 178 case "inverse": return nissy.NissFlag.inverse;
@@ -176,15 +184,29 @@ async function solve(id, arg) {
176 })(arg.nissFlag); 184 })(arg.nissFlag);
177 185
178 const result = await nissy.solve(arg.solver, cube, nissFlag, arg.minmoves, 186 const result = await nissy.solve(arg.solver, cube, nissFlag, arg.minmoves,
179 arg.maxmoves, arg.maxsolutions, arg.optimal, arg.threads, id); 187 arg.maxmoves, arg.maxsolutions, arg.optimal, arg.threads,
188 pollStatusCallback);
180 189
181 if (result.err.ok()) { 190 if (result.err.ok()) {
182 //TODO: add solution lenght?
183 var solutions = result.solutions == "" ? [] : result.solutions.split("\n"); 191 var solutions = result.solutions == "" ? [] : result.solutions.split("\n");
184 solutions.pop(); // Solver always returns string ending in newline 192 solutions.pop(); // Solver always returns string ending in newline
193 for (var i = 0; i < solutions.length; i++) {
194 const movecount = await nissy.countMoves(solutions[i]);
195 solutions[i] += " (" + movecount.value + ")";
196 }
185 async_return(true, solutions); 197 async_return(true, solutions);
186 } else { 198 } else {
187 async_return(false, [], 199 async_return(false, [],
188 "Error while solving (error " + result.err.value + ")"); 200 "Error while solving (error " + result.err.value + ")");
189 } 201 }
190}; 202};
203
204function updateStatus(id, arg) {
205 if (arg == "stop") {
206 solveStatus = nissy.statusSTOP;
207 } else if (arg == "pause") {
208 solveStatus = nissy.statusPAUSE;
209 } else {
210 solveStatus = nissy.statusRUN;
211 }
212};

Generated with cgit - Back to sebastiano.tronto.net