diff options
| author | Sebastiano Tronto <sebastiano@tronto.net> | 2025-05-23 17:06:02 +0200 |
|---|---|---|
| committer | Sebastiano Tronto <sebastiano@tronto.net> | 2025-05-23 17:06:02 +0200 |
| commit | ecb04c6b7fbf5d06c2fdce5128e83575cac2bd21 (patch) | |
| tree | 3946a99938c10b61f7822c715771c3529400cebc | |
| parent | c6a77f30f64be73a5e55e06336975f2ecfbb2324 (diff) | |
| download | nissy-core-ecb04c6b7fbf5d06c2fdce5128e83575cac2bd21.tar.gz nissy-core-ecb04c6b7fbf5d06c2fdce5128e83575cac2bd21.zip | |
Web version (work in progress)
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | README.md | 13 | ||||
| -rwxr-xr-x | build | 34 | ||||
| -rw-r--r-- | web/adapter.cpp | 91 | ||||
| -rw-r--r-- | web/callback.js | 32 | ||||
| -rw-r--r-- | web/examples/solve.mjs | 12 | ||||
| -rw-r--r-- | web/http/index.html | 20 | ||||
| -rw-r--r-- | web/http/mime | 1 | ||||
| -rw-r--r-- | web/storage.cpp | 67 | ||||
| -rw-r--r-- | web/storage.h | 7 |
10 files changed, 247 insertions, 32 deletions
| @@ -22,6 +22,8 @@ runcpp | |||
| 22 | runtest.js | 22 | runtest.js |
| 23 | runtest.wasm | 23 | runtest.wasm |
| 24 | web/*.wasm | 24 | web/*.wasm |
| 25 | web/http/*.wasm | ||
| 26 | web/http/*.mjs | ||
| 25 | web/nissy_web_module.* | 27 | web/nissy_web_module.* |
| 26 | runtool | 28 | runtool |
| 27 | tools/.DS_Store | 29 | tools/.DS_Store |
| @@ -251,6 +251,19 @@ using [nodejs](https://nodejs.org): | |||
| 251 | $ node web/examples/[filename] | 251 | $ node web/examples/[filename] |
| 252 | ``` | 252 | ``` |
| 253 | 253 | ||
| 254 | An example web app running nissy can be found in the `web/http` folder. | ||
| 255 | You can run a web server in that folder to check it out, but you need | ||
| 256 | to set some extra headers to make it work. For example, if you are using | ||
| 257 | [darkhttpd](https://github.com/emikulic/darkhttpd) you can start the server | ||
| 258 | with the following command: | ||
| 259 | |||
| 260 | ``` | ||
| 261 | $ darkhttpd web/http/ \ | ||
| 262 | --header 'Cross-Origin-Opener-Policy: same-origin' \ | ||
| 263 | --header 'Cross-Origin-Embedder-Policy: require-corp' \ | ||
| 264 | --mimetypes web/http/mime | ||
| 265 | ``` | ||
| 266 | |||
| 254 | ## Cube format | 267 | ## Cube format |
| 255 | 268 | ||
| 256 | This format is a "base 32" encoding of the cube. It is not meant to be | 269 | This format is a "base 32" encoding of the cube. It is not meant to be |
| @@ -129,9 +129,12 @@ CPPFLAGS="-std=c++20 -pthread" | |||
| 129 | # Build flags for emscripten (WASM target) | 129 | # Build flags for emscripten (WASM target) |
| 130 | WASMCFLAGS="-std=c11 -fPIC -D_POSIX_C_SOURCE=199309L -pthread" | 130 | WASMCFLAGS="-std=c11 -fPIC -D_POSIX_C_SOURCE=199309L -pthread" |
| 131 | WASMMFLAGS="-DTHREADS=$THREADS -DWASMSIMD" | 131 | WASMMFLAGS="-DTHREADS=$THREADS -DWASMSIMD" |
| 132 | #WASMLINKFLAGS="--no-entry -sEXPORT_NAME='Nissy' -sMODULARIZE \ | ||
| 133 | # -sALLOW_MEMORY_GROWTH -sSTACK_SIZE=5MB -sPTHREAD_POOL_SIZE=$THREADS \ | ||
| 134 | # -sNODERAWFS -sASYNCIFY -sLINKABLE -sEXPORT_ALL" | ||
| 132 | WASMLINKFLAGS="--no-entry -sEXPORT_NAME='Nissy' -sMODULARIZE \ | 135 | WASMLINKFLAGS="--no-entry -sEXPORT_NAME='Nissy' -sMODULARIZE \ |
| 133 | -sALLOW_MEMORY_GROWTH -sSTACK_SIZE=5MB -sPTHREAD_POOL_SIZE=$THREADS \ | 136 | -sALLOW_MEMORY_GROWTH -sSTACK_SIZE=5MB -sPTHREAD_POOL_SIZE=$THREADS \ |
| 134 | -sLINKABLE -sEXPORT_ALL" | 137 | -sASYNCIFY -sLINKABLE -sEXPORT_ALL" |
| 135 | 138 | ||
| 136 | if (command -v "python3-config" >/dev/null 2>&1) ; then | 139 | if (command -v "python3-config" >/dev/null 2>&1) ; then |
| 137 | PYTHON3_INCLUDES="$(python3-config --includes)" | 140 | PYTHON3_INCLUDES="$(python3-config --includes)" |
| @@ -207,7 +210,7 @@ odflags() { | |||
| 207 | 210 | ||
| 208 | build_clean() { | 211 | build_clean() { |
| 209 | run rm -rf -- *.o *.so *.a run runtest runtool runcpp \ | 212 | run rm -rf -- *.o *.so *.a run runtest runtool runcpp \ |
| 210 | web/nissy_web_module.* | 213 | web/nissy_web_module.* web/*.wasm web/http/*.mjs web/http/*.wasm |
| 211 | } | 214 | } |
| 212 | 215 | ||
| 213 | build_nissy() { | 216 | build_nissy() { |
| @@ -233,7 +236,7 @@ build_sharedlib() { | |||
| 233 | } | 236 | } |
| 234 | 237 | ||
| 235 | build_shell() { | 238 | build_shell() { |
| 236 | build_nissy | 239 | build_nissy || exit 1 |
| 237 | run $CC $CFLAGS $WFLAGS $(odflags) -o run nissy.o shell/shell.c | 240 | run $CC $CFLAGS $WFLAGS $(odflags) -o run nissy.o shell/shell.c |
| 238 | } | 241 | } |
| 239 | 242 | ||
| @@ -243,7 +246,7 @@ build_python() { | |||
| 243 | echo "Cannot build python module" | 246 | echo "Cannot build python module" |
| 244 | exit 1 | 247 | exit 1 |
| 245 | fi | 248 | fi |
| 246 | build_nissy | 249 | build_nissy || exit 1 |
| 247 | run $CC $CFLAGS $WFLAGS $PYTHON3_INCLUDES $(odflags) -shared \ | 250 | run $CC $CFLAGS $WFLAGS $PYTHON3_INCLUDES $(odflags) -shared \ |
| 248 | -o nissy_python_module.so nissy.o python/nissy_module.c | 251 | -o nissy_python_module.so nissy.o python/nissy_module.c |
| 249 | } | 252 | } |
| @@ -255,7 +258,7 @@ build_cpp() { | |||
| 255 | echo "usage: ./build cpp FILES" | 258 | echo "usage: ./build cpp FILES" |
| 256 | fi | 259 | fi |
| 257 | 260 | ||
| 258 | build_nissy | 261 | build_nissy || exit 1 |
| 259 | run $CXX -std=c++20 -o runcpp cpp/nissy.cpp nissy.o $@ | 262 | run $CXX -std=c++20 -o runcpp cpp/nissy.cpp nissy.o $@ |
| 260 | run ./runcpp | 263 | run ./runcpp |
| 261 | } | 264 | } |
| @@ -265,13 +268,20 @@ build_web() { | |||
| 265 | validate_threads "$THREADS" | 268 | validate_threads "$THREADS" |
| 266 | validate_arch "$ARCH" | 269 | validate_arch "$ARCH" |
| 267 | 270 | ||
| 271 | obj="nissy_web_module" | ||
| 272 | |||
| 268 | run $EMCC $WASMCFLAGS $WFLAGS $WASMMFLAGS $(odflags) -c \ | 273 | run $EMCC $WASMCFLAGS $WFLAGS $WASMMFLAGS $(odflags) -c \ |
| 269 | -o nissy.o src/nissy.c | 274 | -o nissy.o src/nissy.c || exit 1 |
| 270 | run $EMCC -lembind -lidbfs.js -lnodefs.js \ | 275 | #run $EMCC -lembind -lidbfs.js -lnodefs.js -lnoderawfs.js \ |
| 276 | # $CPPFLAGS $(odflags) $WASMLINKFLAGS \ | ||
| 277 | # --js-library web/callback.js -o web/"$obj".mjs \ | ||
| 278 | # cpp/nissy.cpp web/storage.cpp web/adapter.cpp nissy.o | ||
| 279 | run $EMCC -lembind -lidbfs.js \ | ||
| 271 | $CPPFLAGS $(odflags) $WASMLINKFLAGS \ | 280 | $CPPFLAGS $(odflags) $WASMLINKFLAGS \ |
| 272 | --js-library web/callback.js \ | 281 | --js-library web/callback.js -o web/"$obj".mjs \ |
| 273 | -o web/nissy_web_module.mjs \ | 282 | cpp/nissy.cpp web/storage.cpp web/adapter.cpp nissy.o || exit 1 |
| 274 | cpp/nissy.cpp web/adapter.cpp nissy.o | 283 | cp web/"$obj".mjs web/http |
| 284 | cp web/"$obj".wasm web/http | ||
| 275 | } | 285 | } |
| 276 | 286 | ||
| 277 | dotest() { | 287 | dotest() { |
| @@ -310,7 +320,7 @@ build_test_generic() { | |||
| 310 | shift | 320 | shift |
| 311 | fi | 321 | fi |
| 312 | debug="yes" | 322 | debug="yes" |
| 313 | build_$obj | 323 | build_$obj || exit 1 |
| 314 | for t in test/*; do | 324 | for t in test/*; do |
| 315 | dotest || exit 1 | 325 | dotest || exit 1 |
| 316 | done | 326 | done |
| @@ -323,7 +333,7 @@ build_test() { | |||
| 323 | testbuild="$CC $CFLAGS $WFLAGS $DFLAGS $MFLAGS -o $testobj" | 333 | testbuild="$CC $CFLAGS $WFLAGS $DFLAGS $MFLAGS -o $testobj" |
| 324 | testrun="./$testobj" | 334 | testrun="./$testobj" |
| 325 | build_test_generic $@ | 335 | build_test_generic $@ |
| 326 | rm runtest | 336 | rm -f runtest |
| 327 | } | 337 | } |
| 328 | 338 | ||
| 329 | build_webtest() { | 339 | build_webtest() { |
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 | ||
| 11 | EM_ASYNC_JS(void, fake_async, (), {}); | ||
| 12 | |||
| 9 | extern "C" { | 13 | extern "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 | |||
| 47 | std::map<std::string, nissy::solver> loaded_solvers; | 51 | std::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) | ||
| 51 | bool init_solver(const std::string& name) | 54 | bool 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 | ||
| 88 | int poll_status(void *arg) | 109 | int poll_status(void *arg) |
| 89 | { | 110 | { |
| 111 | return nissy::status::RUN.value; | ||
| 112 | /* | ||
| 113 | TODO: 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 | |||
| 96 | nissy::solver::solve_result solve(std::string name, | 123 | nissy::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 | |||
| 146 | std::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 | |||
| 109 | EMSCRIPTEN_BINDINGS(Nissy) | 173 | EMSCRIPTEN_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 | ||
| 3 | cbfl: [], | 3 | cbfl: [], |
| 4 | 4 | ||
| 5 | validateCallbackId__deps: [ 'cbfl' ], | ||
| 6 | validateCallbackId: 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 | |||
| 5 | addCallbackFunction__deps: [ 'cbfl' ], | 26 | addCallbackFunction__deps: [ 'cbfl' ], |
| 6 | addCallbackFunction: function(f) { | 27 | addCallbackFunction: function(f) { |
| 7 | _cbfl.push(f) | 28 | _cbfl.push(f) |
| 8 | return _cbfl.length - 1 | 29 | return _cbfl.length - 1 |
| 9 | }, | 30 | }, |
| 10 | 31 | ||
| 11 | callFunction__deps: [ 'cbfl' ], | 32 | callFunction__deps: [ 'cbfl', 'validateCallbackId' ], |
| 12 | callFunction: function(id, arg) { | 33 | callFunction: function(id, arg) { |
| 13 | _cbfl[id](UTF8ToString(arg)) | 34 | if (_validateCallbackId(id)) |
| 35 | _cbfl[id](UTF8ToString(arg)); | ||
| 14 | }, | 36 | }, |
| 15 | 37 | ||
| 16 | callFunctionInt__deps: [ 'cbfl' ], | 38 | callFunctionInt__deps: [ 'cbfl', 'validateCallbackId' ], |
| 17 | callFunctionInt: function(id) { | 39 | callFunctionInt: 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 @@ | |||
| 1 | import Nissy from '../nissy_web_module.mjs' | 1 | import Nissy from '../nissy_web_module.mjs' |
| 2 | 2 | ||
| 3 | const nissy = await Nissy() | 3 | const nissy = await Nissy(); |
| 4 | 4 | ||
| 5 | nissy.setLogger(nissy._addCallbackFunction(console.log)) | 5 | var log = process.stdout.write.bind(process.stdout); |
| 6 | //var log = console.log | ||
| 7 | nissy.setLogger(nissy._addCallbackFunction(log)) | ||
| 6 | 8 | ||
| 7 | var cube = new nissy.Cube() | 9 | var cube = new nissy.Cube(); |
| 8 | cube.move('R\' U\' F') | 10 | cube.move('R\' U\' F'); |
| 9 | 11 | ||
| 10 | nissy.solve('h48h0k4', cube, nissy.NissFlag.normal, 0, 8, 2, 99, 4, -1) | 12 | nissy.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 | |||
| 7 | EM_JS(int, inbrowser, (), { return typeof window !== 'undefined'; }); | ||
| 8 | EM_JS(int, inworker, (), { return typeof WorkerGlobalScope !== 'undefined' && | ||
| 9 | self instanceof WorkerGlobalScope; }); | ||
| 10 | |||
| 11 | std::string getprefix() { | ||
| 12 | return inbrowser() || inworker() ? "/tables/" : "./tables/"; | ||
| 13 | } | ||
| 14 | |||
| 15 | EM_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 | |||
| 41 | bool 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 | |||
| 56 | bool 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 | |||
| 4 | namespace storage { | ||
| 5 | bool read(std::string, size_t, char *); | ||
| 6 | bool write(std::string, size_t, const char *); | ||
| 7 | } | ||
