aboutsummaryrefslogtreecommitdiff
path: root/web
diff options
context:
space:
mode:
authorSebastiano Tronto <sebastiano@tronto.net>2025-05-23 17:06:02 +0200
committerSebastiano Tronto <sebastiano@tronto.net>2025-05-23 17:06:02 +0200
commitecb04c6b7fbf5d06c2fdce5128e83575cac2bd21 (patch)
tree3946a99938c10b61f7822c715771c3529400cebc /web
parentc6a77f30f64be73a5e55e06336975f2ecfbb2324 (diff)
downloadnissy-core-ecb04c6b7fbf5d06c2fdce5128e83575cac2bd21.tar.gz
nissy-core-ecb04c6b7fbf5d06c2fdce5128e83575cac2bd21.zip
Web version (work in progress)
Diffstat (limited to 'web')
-rw-r--r--web/adapter.cpp91
-rw-r--r--web/callback.js32
-rw-r--r--web/examples/solve.mjs12
-rw-r--r--web/http/index.html20
-rw-r--r--web/http/mime1
-rw-r--r--web/storage.cpp67
-rw-r--r--web/storage.h7
7 files changed, 210 insertions, 20 deletions
diff --git a/web/adapter.cpp b/web/adapter.cpp
index 1774187..3613730 100644
--- a/web/adapter.cpp
+++ b/web/adapter.cpp
@@ -1,11 +1,15 @@
1#include "../cpp/nissy.h" 1#include "../cpp/nissy.h"
2#include "storage.h"
2 3
4#include <emscripten.h>
3#include <emscripten/bind.h> 5#include <emscripten/bind.h>
4#include <map> 6#include <map>
5#include <set> 7#include <set>
6#include <string> 8#include <string>
7#include <vector> 9#include <vector>
8 10
11EM_ASYNC_JS(void, fake_async, (), {});
12
9extern "C" { 13extern "C" {
10 extern int addCallbackFunction(/* args intentionally unspecified */); 14 extern int addCallbackFunction(/* args intentionally unspecified */);
11 extern void callFunction(int, const char *); 15 extern void callFunction(int, const char *);
@@ -47,26 +51,43 @@ const std::set<std::string> available_solvers
47std::map<std::string, nissy::solver> loaded_solvers; 51std::map<std::string, nissy::solver> loaded_solvers;
48 52
49// TODO: this should ask the user if they want to download or generate. 53// TODO: this should ask the user if they want to download or generate.
50// TODO: this should also save the data to a file (IDBFS / NODEFS)
51bool init_solver(const std::string& name) 54bool init_solver(const std::string& name)
52{ 55{
53 auto se = nissy::solver::get(name); 56 auto se = nissy::solver::get(name);
54 nissy::solver solver = std::get<nissy::solver>(se); 57 nissy::solver solver = std::get<nissy::solver>(se);
55 log("Generating data for solver " + solver.name + "\n"); 58
56 auto err = solver.generate_data(); 59 solver.data.resize(solver.size);
57 if (!err.ok()) { 60 if (storage::read(solver.id, solver.size,
58 log("Error generating the data!\n"); 61 reinterpret_cast<char *>(solver.data.data()))) {
59 return false; 62 log("Data for solver " + solver.name + " read from storage\n");
63 } else {
64 log("Could not read data for solver " + solver.name +
65 " from storage, generating it\n");
66 auto err = solver.generate_data();
67
68 if (!err.ok()) {
69 log("Error generating the data!\n");
70 return false;
71 }
60 } 72 }
73
61 log("Checking data integrity " 74 log("Checking data integrity "
62 "(this is done only once per session per solver)...\n"); 75 "(this is done only once per session per solver)...\n");
63 if (!solver.check_data().ok()) { 76 if (!solver.check_data().ok()) {
64 log("Error generating the data!\n"); 77 log("Error! Data is corrupted!\n");
65 return false; 78 return false;
66 } 79 }
67 loaded_solvers.insert({name, solver}); 80 loaded_solvers.insert({name, solver});
68 log("Data generated successfully, but not saved " 81
69 "(feature not yet available)\n"); 82 if (storage::write(solver.id, solver.size,
83 reinterpret_cast<const char *>(solver.data.data()))) {
84 log("Data for solver " + solver.name + " stored\n");
85 } else {
86 log("Error storing the data (the solver is usable, "
87 "but the data will have to be re-generated next "
88 "time you want to use it)");
89 }
90
70 return true; 91 return true;
71} 92}
72 93
@@ -87,17 +108,31 @@ bool solver_valid(const std::string& name)
87 108
88int poll_status(void *arg) 109int poll_status(void *arg)
89{ 110{
111 return nissy::status::RUN.value;
112/*
113TODO: reintroduce poll status
90 int id = *(int *)arg; 114 int id = *(int *)arg;
91 if (id == -1) 115 if (id == -1)
92 return nissy::status::RUN.value; 116 return nissy::status::RUN.value;
93 return callFunctionInt(id); 117 return callFunctionInt(id);
118*/
94} 119}
95 120
121#if 0
122
96nissy::solver::solve_result solve(std::string name, 123nissy::solver::solve_result solve(std::string name,
97 nissy::cube cube, nissy::nissflag nissflag, unsigned minmoves, 124 nissy::cube cube, nissy::nissflag nissflag, unsigned minmoves,
98 unsigned maxmoves, unsigned maxsols, unsigned optimal, unsigned threads, 125 unsigned maxmoves, unsigned maxsols, unsigned optimal, unsigned threads,
99 int poll_status_id) 126 int poll_status_id)
100{ 127{
128 // Here we use a dirty trick to make this function always return the
129 // same kind of JavaScript object. If we did not do this, the returned
130 // object would be a Promise on the first run of the solver for each
131 // session (because when loading the table some async JS code is
132 // called), and a regular object otherwise.
133 // TODO figure out if there is a better way to do this.
134 fake_async();
135
101 if (!solver_valid(name)) 136 if (!solver_valid(name))
102 return nissy::solver::solve_result 137 return nissy::solver::solve_result
103 {.err = nissy::error::INVALID_SOLVER}; 138 {.err = nissy::error::INVALID_SOLVER};
@@ -106,6 +141,35 @@ nissy::solver::solve_result solve(std::string name,
106 maxmoves, maxsols, optimal, threads, NULL, &poll_status_id); 141 maxmoves, maxsols, optimal, threads, NULL, &poll_status_id);
107} 142}
108 143
144#else
145
146std::string solve(std::string name,
147 nissy::cube cube, nissy::nissflag nissflag, unsigned minmoves,
148 unsigned maxmoves, unsigned maxsols, unsigned optimal, unsigned threads,
149 int poll_status_id)
150{
151 // Here we use a dirty trick to make this function always return the
152 // same kind of JavaScript object. If we did not do this, the returned
153 // object would be a Promise on the first run of the solver for each
154 // session (because when loading the table some async JS code is
155 // called), and a regular object otherwise.
156 // TODO figure out if there is a better way to do this.
157 fake_async();
158
159 if (!solver_valid(name))
160 return "";
161/*
162 return nissy::solver::solve_result
163 {.err = nissy::error::INVALID_SOLVER};
164*/
165
166 return loaded_solvers.at(name).solve(cube, nissflag, minmoves,
167 maxmoves, maxsols, optimal, threads, NULL, &poll_status_id)
168 .solutions[0];
169}
170
171#endif
172
109EMSCRIPTEN_BINDINGS(Nissy) 173EMSCRIPTEN_BINDINGS(Nissy)
110{ 174{
111 emscripten::class_<nissy::nissflag>("NissFlag") 175 emscripten::class_<nissy::nissflag>("NissFlag")
@@ -134,13 +198,11 @@ EMSCRIPTEN_BINDINGS(Nissy)
134 emscripten::constant("statusRUN", nissy::status::RUN.value); 198 emscripten::constant("statusRUN", nissy::status::RUN.value);
135 emscripten::constant("statusSTOP", nissy::status::STOP.value); 199 emscripten::constant("statusSTOP", nissy::status::STOP.value);
136 emscripten::constant("statusPAUSE", nissy::status::PAUSE.value); 200 emscripten::constant("statusPAUSE", nissy::status::PAUSE.value);
137 /*
138 emscripten::class_<nissy::status>("Status") 201 emscripten::class_<nissy::status>("Status")
139 .class_property("run", &nissy::status::RUN) 202 .class_property("run", &nissy::status::RUN)
140 .class_property("stop", &nissy::status::STOP) 203 .class_property("stop", &nissy::status::STOP)
141 .class_property("pause", &nissy::status::PAUSE) 204 .class_property("pause", &nissy::status::PAUSE)
142 ; 205 ;
143 */
144 206
145 emscripten::class_<nissy::cube>("Cube") 207 emscripten::class_<nissy::cube>("Cube")
146 .constructor<>() 208 .constructor<>()
@@ -150,10 +212,17 @@ EMSCRIPTEN_BINDINGS(Nissy)
150 .function("toString", &nissy::cube::to_string) 212 .function("toString", &nissy::cube::to_string)
151 ; 213 ;
152 214
215 emscripten::register_vector<std::string>("StringVector");
216 emscripten::value_array<nissy::solver::solve_result>("SolveResult")
217 .element(&nissy::solver::solve_result::err)
218 .element(&nissy::solver::solve_result::solutions)
219 ;
220/*
153 emscripten::class_<nissy::solver::solve_result>("SolveResult") 221 emscripten::class_<nissy::solver::solve_result>("SolveResult")
154 .property("err", &nissy::solver::solve_result::err) 222 .property("err", &nissy::solver::solve_result::err)
155 .property("solutions", &nissy::solver::solve_result::solutions) 223 .property("solutions", &nissy::solver::solve_result::solutions)
156 ; 224 ;
225*/
157 226
158 emscripten::function("countMoves", &nissy::count_moves); 227 emscripten::function("countMoves", &nissy::count_moves);
159 emscripten::function("solve", &solve, 228 emscripten::function("solve", &solve,
diff --git a/web/callback.js b/web/callback.js
index 1b3028e..178b8e9 100644
--- a/web/callback.js
+++ b/web/callback.js
@@ -2,20 +2,44 @@ addToLibrary({
2 2
3cbfl: [], 3cbfl: [],
4 4
5validateCallbackId__deps: [ 'cbfl' ],
6validateCallbackId: function(i) {
7 if (i < 0) {
8 console.log("--- WARNING ---");
9 console.log("Trying to access callback function of invalid id " + i);
10 console.log("--- WARNING ---");
11 return false;
12 }
13
14 if (i >= _cbfl.length) {
15 console.log("--- WARNING ---");
16 console.log("Trying to access callback function " + i + ", but only "
17 + _cbfl.length + " have been registered. This may be caused by a "
18 + "call outside of the main thread.");
19 console.log("--- WARNING ---");
20 return false;
21 }
22
23 return true;
24},
25
5addCallbackFunction__deps: [ 'cbfl' ], 26addCallbackFunction__deps: [ 'cbfl' ],
6addCallbackFunction: function(f) { 27addCallbackFunction: function(f) {
7 _cbfl.push(f) 28 _cbfl.push(f)
8 return _cbfl.length - 1 29 return _cbfl.length - 1
9}, 30},
10 31
11callFunction__deps: [ 'cbfl' ], 32callFunction__deps: [ 'cbfl', 'validateCallbackId' ],
12callFunction: function(id, arg) { 33callFunction: function(id, arg) {
13 _cbfl[id](UTF8ToString(arg)) 34 if (_validateCallbackId(id))
35 _cbfl[id](UTF8ToString(arg));
14}, 36},
15 37
16callFunctionInt__deps: [ 'cbfl' ], 38callFunctionInt__deps: [ 'cbfl', 'validateCallbackId' ],
17callFunctionInt: function(id) { 39callFunctionInt: function(id) {
18 return _cbfl[id]() 40 if (_validateCallbackId(id))
41 return _cbfl[id]();
42 return 0;
19}, 43},
20 44
21}); 45});
diff --git a/web/examples/solve.mjs b/web/examples/solve.mjs
index c7bce71..8ff59c0 100644
--- a/web/examples/solve.mjs
+++ b/web/examples/solve.mjs
@@ -1,10 +1,12 @@
1import Nissy from '../nissy_web_module.mjs' 1import Nissy from '../nissy_web_module.mjs'
2 2
3const nissy = await Nissy() 3const nissy = await Nissy();
4 4
5nissy.setLogger(nissy._addCallbackFunction(console.log)) 5var log = process.stdout.write.bind(process.stdout);
6//var log = console.log
7nissy.setLogger(nissy._addCallbackFunction(log))
6 8
7var cube = new nissy.Cube() 9var cube = new nissy.Cube();
8cube.move('R\' U\' F') 10cube.move('R\' U\' F');
9 11
10nissy.solve('h48h0k4', cube, nissy.NissFlag.normal, 0, 8, 2, 99, 4, -1) 12nissy.solve('h48h0k4', cube, nissy.NissFlag.normal, 0, 8, 2, 99, 4, -1);
diff --git a/web/http/index.html b/web/http/index.html
new file mode 100644
index 0000000..245205d
--- /dev/null
+++ b/web/http/index.html
@@ -0,0 +1,20 @@
1<!doctype html>
2<html lang="en-US">
3 <head>
4 <meta charset="utf-8" />
5 <meta name="viewport" content="width=device-width" />
6 <title>Nissy - H48 solver POC</title>
7 <script type="module" src="./solve.mjs"></script>
8 </head>
9 <body>
10 <input id="scrambleText" placeholder="Type the scramble here...">
11 <select id="solverSelector">
12 <option value="h48h0k4" selected="selected">h48 h=0 k=4 (59Mb)</option>
13 <option value="h48h3k2">h48 h=3 k=2 (283Mb)</option>
14 <option value="h48h7k2">h48 h=7 k=2 (3.6Gb)</option>
15 </select>
16 <button id="solveButton">Solve!</button>
17 <p>Solution:</p>
18 <p id="solution"></p>
19 </body>
20</html>
diff --git a/web/http/mime b/web/http/mime
new file mode 100644
index 0000000..8c956e1
--- /dev/null
+++ b/web/http/mime
@@ -0,0 +1 @@
text/javascript mjs
diff --git a/web/storage.cpp b/web/storage.cpp
new file mode 100644
index 0000000..4be3066
--- /dev/null
+++ b/web/storage.cpp
@@ -0,0 +1,67 @@
1#include "storage.h"
2
3#include "emscripten.h"
4#include <filesystem>
5#include <fstream>
6
7EM_JS(int, inbrowser, (), { return typeof window !== 'undefined'; });
8EM_JS(int, inworker, (), { return typeof WorkerGlobalScope !== 'undefined' &&
9 self instanceof WorkerGlobalScope; });
10
11std::string getprefix() {
12 return inbrowser() || inworker() ? "/tables/" : "./tables/";
13}
14
15EM_ASYNC_JS(int, loadfs, (), {
16 const dir = '/tables';
17 const inBrowser = typeof window !== 'undefined';
18 const inWorker = typeof WorkerGlobalScope !== 'undefined' &&
19 self instanceof WorkerGlobalScope;
20
21 if (!(inBrowser || inWorker)) return;
22
23 if (!FS.analyzePath(dir).exists)
24 FS.mkdir(dir);
25
26 if (FS.analyzePath(dir).object.mount.mountpoint != dir) {
27 FS.mount(IDBFS, { autoPersist: true }, dir);
28
29 await new Promise((resolve, reject) => {
30 FS.syncfs(true, function (err) {
31 if (err) {
32 reject(err);
33 } else {
34 resolve(true);
35 }
36 });
37 });
38 }
39});
40
41bool storage::read(std::string key, size_t data_size, char *data)
42{
43 loadfs();
44
45 std::filesystem::path path(getprefix() + key);
46 if (!std::filesystem::exists(path))
47 return false;
48
49 std::ifstream ifs(path, std::ios::binary);
50 ifs.read(data, data_size);
51 ifs.close();
52
53 return !ifs.fail();
54}
55
56bool storage::write(std::string key, size_t data_size, const char *data)
57{
58 loadfs();
59
60 std::filesystem::path path(getprefix() + key);
61
62 std::ofstream ofs(path, std::ios::binary);
63 ofs.write(data, data_size);
64 ofs.close();
65
66 return !ofs.fail();
67}
diff --git a/web/storage.h b/web/storage.h
new file mode 100644
index 0000000..fa6b748
--- /dev/null
+++ b/web/storage.h
@@ -0,0 +1,7 @@
1#include <string>
2#include <fstream>
3
4namespace storage {
5 bool read(std::string, size_t, char *);
6 bool write(std::string, size_t, const char *);
7}

Generated with cgit - Back to sebastiano.tronto.net