aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--web/http/index.html21
-rw-r--r--web/http/nissyapp.mjs134
-rw-r--r--web/worker.mjs89
3 files changed, 222 insertions, 22 deletions
diff --git a/web/http/index.html b/web/http/index.html
index c80f7ec..0846ea8 100644
--- a/web/http/index.html
+++ b/web/http/index.html
@@ -9,12 +9,27 @@
9 <body> 9 <body>
10 <input id="scrambleText" placeholder="Type the scramble here..."> 10 <input id="scrambleText" placeholder="Type the scramble here...">
11 <select id="solverSelector"> 11 <select id="solverSelector">
12 <option value="h48h0k4" selected="selected">h48 h=0 k=4 (59Mb)</option> 12 <option value="h48h0k4">h48 h=0 k=4 (59Mb)</option>
13 <option value="h48h3k2">h48 h=3 k=2 (283Mb)</option> 13 <option value="h48h3k2" selected="selected">h48 h=3 k=2 (283Mb)</option>
14 <option value="h48h7k2">h48 h=7 k=2 (3.6Gb)</option> 14 <!--option value="h48h7k2">h48 h=7 k=2 (3.6Gb)</option-->
15 </select> 15 </select>
16 <button id="solveButton">Solve</button> 16 <button id="solveButton">Solve</button>
17 <div id="confirmDownload" style="display:none">
18 <p id="confirmDownloadText">
19 For this solver to work, a large data table is required.
20 You can either download it or generate it locally.
21 </p>
22 <p id="confirmDownloadExtraText">
23 In either case, this data will be stored locally for future use. You
24 can delete it at any time by clearing your browser's data.
25 </p>
26 <button id="confirmDownloadCancel">Cancel</button>
27 <button id="confirmDownloadConfirm">Download</button>
28 <button id="confirmDownloadGenerate">Generate locally</button>
29 </div>
17 <p id="resultsLabel"></p> 30 <p id="resultsLabel"></p>
18 <p id="results"></p> 31 <p id="results"></p>
32 <button id="toggleShowLog">Show nissy log messages</button>
33 <p id="logPane" style="display:none"></p>
19 </body> 34 </body>
20</html> 35</html>
diff --git a/web/http/nissyapp.mjs b/web/http/nissyapp.mjs
index 23e74d0..b8a525c 100644
--- a/web/http/nissyapp.mjs
+++ b/web/http/nissyapp.mjs
@@ -7,30 +7,149 @@ var scrField = document.getElementById("scrambleText");
7var resultsLabel = document.getElementById("resultsLabel"); 7var resultsLabel = document.getElementById("resultsLabel");
8var resultsText = document.getElementById("results"); 8var resultsText = document.getElementById("results");
9var solverSelector = document.getElementById("solverSelector"); 9var solverSelector = document.getElementById("solverSelector");
10var toggleLog = document.getElementById("toggleShowLog");
11var logPane = document.getElementById("logPane");
10 12
11var lastCallbackId = 0; 13var lastCallbackId = 0;
12var callbacks = new Map(); // Values: { f: function, arg: object } 14var callbacks = new Map(); // Values: { f: function, arg: object }
13var worker = new Worker("./worker.mjs", { type: "module" }); 15var worker = new Worker("./worker.mjs", { type: "module" });
14worker.onmessage = (event) => { 16worker.onmessage = (event) => {
15 if (event.data.command == "log") { 17 if (event.data.command == "log") {
16 console.log(event.data.object); // TODO 18 logPane.innerText += event.data.object;
19 console.log(event.data.object);
17 } else if (!callbacks.has(event.data.id)) { 20 } else if (!callbacks.has(event.data.id)) {
18 console.log("[nissy app] Unknown callback " + event.data.id + 21 console.log("[nissy app] Unknown callback " + event.data.id +
19 " for command " + event.data.command); 22 " for command " + event.data.command);
20 } else { 23 } else {
21 var callback = callbacks.get(event.data.id); 24 var callback = callbacks.get(event.data.id);
25 callbacks.delete(event.data.id);
22 callback.f(callback.arg, event.data.arg); 26 callback.f(callback.arg, event.data.arg);
23 } 27 }
24}; 28};
25 29
26function updateResults(label, results, buttonState) { 30function updateResults(label, results, enable) {
27 resultsLabel.innerText = label; 31 resultsLabel.innerText = label;
28 resultsText.innerText = results; 32 resultsText.innerText = results;
29 solveButton.disable = !buttonState; 33 solveButton.disable = !enable;
34 solverSelector.disable = !enable;
30} 35}
31 36
37var logVisible = false;
38toggleLog.addEventListener("click", () => {
39 logVisible = !logVisible;
40 if (logVisible) {
41 logPane.style.display = "block";
42 toggleLog.innerText = "Hide nissy log messages";
43 } else {
44 logPane.style.display = "none";
45 toggleLog.innerText = "Show nissy log messages";
46 }
47});
48
32solveButton.addEventListener("click", () => { 49solveButton.addEventListener("click", () => {
33 updateResults("Loading solutions...", "", false); 50 const solver = solverSelector.options[solverSelector.selectedIndex].value;
51 const scramble = scrField.value;
52 loadDataThenSolve(solver, scramble);
53
54 const solveCallbackId = ++lastCallbackId;
55 callbacks.set(solveCallbackId, {
56 f: (callbackArg, loadResult) => {
57 if (loadResult.success) {
58 startSolve(callbackArg.solver, callbackArg.scramble);
59 } else {
60 updateResults("", "", true);
61 }
62 },
63 arg: { solver: solver, scramble: scramble }
64 });
65
66 const downloadCallbackId = ++lastCallbackId;
67});
68
69function loadDataThenSolve(solver, scramble) {
70 updateResults("Loading solver " + solver + "...", "", false);
71
72 const callbackId = ++lastCallbackId;
73 callbacks.set(callbackId, {
74 f: (callbackArg, loadResult) => {
75 if (loadResult.success)
76 startSolve(callbackArg.solver, callbackArg.scramble);
77 else
78 askDownloadThenSolve(callbackArg.solver, callbackArg.scramble);
79 },
80 arg: { solver: solver, scramble: scramble }
81 });
82
83 worker.postMessage({
84 command: "load solver data",
85 id: callbackId,
86 arg: solver
87 });
88}
89
90function askDownloadThenSolve(solver, scramble) {
91 var confirmDiv = document.getElementById("confirmDownload");
92 var cancel = document.getElementById("confirmDownloadCancel");
93 var download = document.getElementById("confirmDownloadConfirm");
94 var generate = document.getElementById("confirmDownloadGenerate");
95 const cleanup = () => {
96 confirmDiv.style.display = "none";
97
98 // Remove event listeners by cloning the buttons
99 cancel.outerHTML = cancel.outerHTML;
100 download.outerHTML = download.outerHTML;
101 generate.outerHTML = generate.outerHTML;
102 };
103
104 updateResults("", "", false);
105 confirmDiv.style.display = "block";
106
107 cancel.addEventListener("click", cleanup);
108
109 download.addEventListener("click", () => {
110 cleanup();
111 downloadDataThenSolve(solver, scramble);
112 });
113
114 generate.addEventListener("click", () => {
115 cleanup();
116 generateDataThenSolve(solver, scramble);
117 });
118}
119
120const downloadDataThenSolve = (solver, scramble) =>
121 downloadOrGenerateThenSolve(solver, scramble, true);
122
123const generateDataThenSolve = (solver, scramble) =>
124 downloadOrGenerateThenSolve(solver, scramble, false);
125
126function downloadOrGenerateThenSolve(solver, scramble, download) {
127 const msg = download ? "Downloading data for " : "Generating data for ";
128 const command = download ? "download solver data" : "generate solver data";
129
130 updateResults(msg + solver + "...", "", false);
131
132 const callbackId = ++lastCallbackId;
133 callbacks.set(callbackId, {
134 f: (callbackArg, loadResult) => {
135 if (loadResult.success)
136 startSolve(callbackArg.solver, callbackArg.scramble);
137 else
138 updateResults("Failed", loadResult.message, true);
139 },
140 arg: { solver: solver, scramble: scramble }
141 });
142
143 worker.postMessage({
144 command: command,
145 id: callbackId,
146 arg: solver
147 });
148}
149
150function startSolve(solver, scramble) {
151 updateResults("Solving...", "", false);
152
34 const callbackId = ++lastCallbackId; 153 const callbackId = ++lastCallbackId;
35 callbacks.set(callbackId, { 154 callbacks.set(callbackId, {
36 f: (callbackArg, solveResult) => { 155 f: (callbackArg, solveResult) => {
@@ -46,12 +165,13 @@ solveButton.addEventListener("click", () => {
46 }, 165 },
47 arg: "" // Currently unused 166 arg: "" // Currently unused
48 }); 167 });
168
49 worker.postMessage({ 169 worker.postMessage({
50 command: "solve", 170 command: "solve",
51 id: callbackId, 171 id: callbackId,
52 arg: { 172 arg: {
53 solver: solverSelector.options[solverSelector.selectedIndex].value, 173 solver: solver,
54 scramble: scrField.value, 174 scramble: scramble,
55 minmoves: 0, 175 minmoves: 0,
56 maxmoves: 20, 176 maxmoves: 20,
57 maxsolutions: 1, 177 maxsolutions: 1,
@@ -59,4 +179,4 @@ solveButton.addEventListener("click", () => {
59 threads: window.navigator.hardwareConcurrency, 179 threads: window.navigator.hardwareConcurrency,
60 } 180 }
61 }); 181 });
62}); 182}
diff --git a/web/worker.mjs b/web/worker.mjs
index dca980e..ceceaec 100644
--- a/web/worker.mjs
+++ b/web/worker.mjs
@@ -8,8 +8,10 @@ function log(message) {
8nissy.setLogger(nissy._addCallbackFunction(log)); 8nissy.setLogger(nissy._addCallbackFunction(log));
9 9
10const commands = [ 10const commands = [
11 { name: "solve", exec: solve }, 11 { name: "load solver data", exec: loadSolverDataFromStorage },
12 { name: "download solver data", exec: downloadSolverData }, 12 { name: "download solver data", exec: downloadSolverData },
13 { name: "generate solver data", exec: generateSolverData },
14 { name: "solve", exec: solve },
13]; 15];
14 16
15// Message structure: 17// Message structure:
@@ -25,6 +27,7 @@ onmessage = (event) => {
25 for (var i = 0; i < commands.length; i++) { 27 for (var i = 0; i < commands.length; i++) {
26 if (commands[i].name == event.data.command) { 28 if (commands[i].name == event.data.command) {
27 commands[i].exec(event.data.id, event.data.arg); 29 commands[i].exec(event.data.id, event.data.arg);
30 console.log("[worker] Received '" + commands[i].name + "'");
28 return; 31 return;
29 } 32 }
30 } 33 }
@@ -32,10 +35,41 @@ onmessage = (event) => {
32 log("[nissy worker] unknown command " + event.data.command); 35 log("[nissy worker] unknown command " + event.data.command);
33}; 36};
34 37
38// Load solver data from storage.
39// Argument: string (the name of the solver)
40async function loadSolverDataFromStorage(id, solver) {
41 const async_return = (success, message = "") => postMessage({
42 command: "load solver data",
43 id: id,
44 arg: {
45 success: success,
46 message: message
47 }
48 });
49
50 if (!(await nissy.isSolverAvailable(solver))) {
51 async_return(false, "Error: solver " + arg.solver +
52 " is not available in this version of nissy.");
53 return;
54 }
55
56 if (await nissy.isSolverLoaded(solver)) {
57 async_return(true);
58 return;
59 }
60
61 if (await nissy.initSolverFromStorage(solver)) {
62 async_return(true);
63 } else {
64 async_return(false, "Could not read data for " + solver +
65 " from local storage");
66 }
67}
68
35// Download solver data. 69// Download solver data.
36// Argument: string 70// Argument: string (the name of the solver)
37async function downloadSolverData(id, solver) { 71async function downloadSolverData(id, solver) {
38 const downloadSolverDataReturn = (success, message = "") => postMessage({ 72 const async_return = (success, message = "") => postMessage({
39 command: "download solver data", 73 command: "download solver data",
40 id: id, 74 id: id,
41 arg: { 75 arg: {
@@ -44,10 +78,41 @@ async function downloadSolverData(id, solver) {
44 } 78 }
45 }); 79 });
46 80
47 if (await nissy.initSolverDownload(arg.solver, "/tables")) { 81 if (!(await nissy.isSolverAvailable(solver))) {
48 downloadSolverDataReturn(true); 82 async_return(false, "Error: solver " + arg.solver +
83 " is not available in this version of nissy.");
84 return;
85 }
86
87 if (await nissy.initSolverDownload(solver, "/tables")) {
88 async_return(true);
89 } else {
90 async_return(false, "Error retrieving solver data");
91 }
92}
93
94// Generate solver data locally.
95// Argument: string (the name of the solver)
96async function generateSolverData(id, solver) {
97 const async_return = (success, message = "") => postMessage({
98 command: "generate solver data",
99 id: id,
100 arg: {
101 success: success,
102 message: message
103 }
104 });
105
106 if (!(await nissy.isSolverAvailable(solver))) {
107 async_return(false, "Error: solver " + arg.solver +
108 " is not available in this version of nissy.");
109 return;
110 }
111
112 if (await nissy.initSolverGenerate(solver)) {
113 async_return(true);
49 } else { 114 } else {
50 downloadSolverDataReturn(false, "Error retrieving the solver data"); 115 async_return(false, "Error generating solver data");
51 } 116 }
52} 117}
53 118
@@ -62,7 +127,7 @@ async function downloadSolverData(id, solver) {
62// threads: number 127// threads: number
63// } 128// }
64async function solve(id, arg) { 129async function solve(id, arg) {
65 const solveReturn = (success, solutions = [], message = "") => postMessage({ 130 const async_return = (success, solutions = [], message = "") => postMessage({
66 command: "solve", 131 command: "solve",
67 id: id, 132 id: id,
68 arg: { 133 arg: {
@@ -73,14 +138,14 @@ async function solve(id, arg) {
73 }); 138 });
74 139
75 if (!(await nissy.isSolverAvailable(arg.solver))) { 140 if (!(await nissy.isSolverAvailable(arg.solver))) {
76 solveReturn(false, [], "Error: solver " + arg.solver + 141 async_return(false, [], "Error: solver " + arg.solver +
77 " is not available in this version of nissy."); 142 " is not available in this version of nissy.");
78 return; 143 return;
79 } 144 }
80 145
81 if (!(await nissy.isSolverLoaded(arg.solver)) && 146 if (!(await nissy.isSolverLoaded(arg.solver)) &&
82 !(await nissy.initSolverFromStorage(arg.solver))) { 147 !(await nissy.initSolverFromStorage(arg.solver))) {
83 solveReturn(false, [], "Error: solver " + arg.solver + " has not been " + 148 async_return(false, [], "Error: solver " + arg.solver + " has not been " +
84 "loaded. Its data must be donwloaded or generated before using it."); 149 "loaded. Its data must be donwloaded or generated before using it.");
85 return; 150 return;
86 } 151 }
@@ -106,9 +171,9 @@ async function solve(id, arg) {
106 //TODO: add solution lenght? 171 //TODO: add solution lenght?
107 var solutions = result.solutions == "" ? [] : result.solutions.split("\n"); 172 var solutions = result.solutions == "" ? [] : result.solutions.split("\n");
108 solutions.pop(); // Solver always returns string ending in newline 173 solutions.pop(); // Solver always returns string ending in newline
109 solveReturn(true, solutions); 174 async_return(true, solutions);
110 } else { 175 } else {
111 solveReturn(false, [], 176 async_return(false, [],
112 "Error while solving (error " + result.err.value + ")"); 177 "Error while solving (error " + result.err.value + ")");
113 } 178 }
114}; 179};

Generated with cgit - Back to sebastiano.tronto.net