nissy-core

The "engine" of nissy, including the H48 optimal solver.
git clone https://git.tronto.net/nissy-core
Download | Log | Files | Refs | README | LICENSE

worker.mjs (6042B)


      1 import Nissy from "./nissy_web_module.mjs"
      2 
      3 const nissy = await Nissy();
      4 
      5 const log = (cstr) => postMessage({
      6 	command: "log",
      7 	id: -1,
      8 	object: nissy.UTF8ToString(cstr)
      9 });
     10 nissy.setLogger(nissy.addFunction(log, "vp"));
     11 
     12 var solveStatus = nissy.statusRUN; // For now this is a global variable
     13 const pollStatus = () => solveStatus;
     14 const pollStatusCallback = nissy.addFunction(pollStatus, "i");
     15 
     16 const commands = [
     17   { name: "load solver data", exec: loadSolverDataFromStorage },
     18   { name: "download solver data", exec: downloadSolverData },
     19   { name: "generate solver data", exec: generateSolverData },
     20   { name: "validate scramble", exec: validateScramble },
     21   { name: "solve", exec: solve },
     22   { name: "update status", exec: updateStatus },
     23 ];
     24 
     25 // Message structure:
     26 // {
     27 //   command: string // The function to run.
     28 //   id: number      // An identification number for the command, used for
     29 //                   // sending responses back to the caller and having them
     30 //                   // caught by the correct handler.
     31 //   arg: object     // The actual argument of the message, command-specific.
     32 //                   // See below for details.
     33 // }
     34 onmessage = (event) => {
     35   for (var i = 0; i < commands.length; i++) {
     36     if (commands[i].name == event.data.command) {
     37       commands[i].exec(event.data.id, event.data.arg);
     38       console.log("[worker] Received '" + commands[i].name + "'");
     39       return;
     40     }
     41   }
     42  
     43   console.log("[nissy worker] unknown command " + event.data.command);
     44 };
     45 
     46 // Load solver data from storage.
     47 // Argument: string (the name of the solver)
     48 async function loadSolverDataFromStorage(id, solver) {
     49   const async_return = (success, message = "") => postMessage({
     50     command: "load solver data",
     51     id: id,
     52     arg: {
     53       success: success,
     54       message: message
     55     }
     56   });
     57 
     58   if (!(await nissy.isSolverAvailable(solver))) {
     59     async_return(false, "Error: solver " + solver +
     60       " is not available in this version of nissy.");
     61     return;
     62   }
     63 
     64   if (await nissy.isSolverLoaded(solver)) {
     65     async_return(true);
     66     return;
     67   }
     68 
     69   if (await nissy.initSolverFromStorage(solver)) {
     70     async_return(true);
     71   } else {
     72     async_return(false, "Could not read data for " + solver +
     73       " from local storage");
     74   }
     75 }
     76 
     77 // Download solver data.
     78 // Argument: string (the name of the solver)
     79 async function downloadSolverData(id, solver) {
     80   const async_return = (success, message = "") => postMessage({
     81     command: "download solver data",
     82     id: id,
     83     arg: {
     84       success: success,
     85       message: message
     86     }
     87   });
     88 
     89   if (!(await nissy.isSolverAvailable(solver))) {
     90     async_return(false, "Error: solver " + arg.solver +
     91       " is not available in this version of nissy.");
     92     return;
     93   }
     94 
     95   if (await nissy.initSolverDownload(solver, "/tables")) {
     96     async_return(true);
     97   } else {
     98     async_return(false, "Error retrieving solver data");
     99   }
    100 }
    101 
    102 // Generate solver data locally.
    103 // Argument: string (the name of the solver)
    104 async function generateSolverData(id, solver) {
    105   const async_return = (success, message = "") => postMessage({
    106     command: "generate solver data",
    107     id: id,
    108     arg: {
    109       success: success,
    110       message: message
    111     }
    112   });
    113 
    114   if (!(await nissy.isSolverAvailable(solver))) {
    115     async_return(false, "Error: solver " + arg.solver +
    116       " is not available in this version of nissy.");
    117     return;
    118   }
    119 
    120   if (await nissy.initSolverGenerate(solver)) {
    121     async_return(true);
    122   } else {
    123     async_return(false, "Error generating solver data");
    124   }
    125 }
    126 
    127 // Check if the scramble is valid.
    128 // Argument: string (the scramble).
    129 async function validateScramble(id, arg) {
    130   const async_return = (success) => postMessage({
    131     command: "validate scramble",
    132     id: id,
    133     arg: success
    134   });
    135 
    136   var cube = new nissy.Cube();
    137   async_return(cube.move(arg).ok());
    138 }
    139 
    140 // Solve the cube with the given options.
    141 // Argument: {
    142 //   solver: string
    143 //   scramble: string
    144 //   minmoves: number
    145 //   maxmoves: number
    146 //   maxsolutions: number
    147 //   optimal: number
    148 //   threads: number
    149 // }
    150 async function solve(id, arg) {
    151   const async_return = (success, solutions = [], message = "") => postMessage({
    152     command: "solve",
    153     id: id,
    154     arg: {
    155       success: success,
    156       solutions: solutions,
    157       message: message
    158     }
    159   });
    160 
    161   if (!(await nissy.isSolverAvailable(arg.solver))) {
    162     async_return(false, [], "Error: solver " + arg.solver +
    163       " is not available in this version of nissy.");
    164     return;
    165   }
    166 
    167   if (!(await nissy.isSolverLoaded(arg.solver)) &&
    168       !(await nissy.initSolverFromStorage(arg.solver))) {
    169     async_return(false, [], "Error: solver " + arg.solver + " has not been " +
    170       "loaded. Its data must be donwloaded or generated before using it.");
    171     return;
    172   }
    173 
    174   solveStatus = nissy.statusRUN;
    175 
    176   var cube = new nissy.Cube();
    177   cube.move(arg.scramble);
    178 
    179   const nissFlag = ((str) => {
    180     switch (str) {
    181       case "inverse": return nissy.NissFlag.inverse;
    182       case "mixed": return nissy.NissFlag.mixed;
    183       case "linear": return nissy.NissFlag.linear;
    184       case "all": return nissy.NissFlag.all;
    185       default: return nissy.NissFlag.normal;
    186     }
    187   })(arg.nissFlag);
    188 
    189   const result = await nissy.solve(arg.solver, cube, nissFlag, arg.minmoves,
    190     arg.maxmoves, arg.maxsolutions, arg.optimal, arg.threads,
    191     pollStatusCallback);
    192 
    193   if (result.err.ok()) {
    194     var solutions = result.solutions == "" ? [] : result.solutions.split("\n");
    195     solutions.pop(); // Solver always returns string ending in newline
    196     for (var i = 0; i < solutions.length; i++) {
    197       const movecount = await nissy.countMoves(solutions[i]);
    198       solutions[i] += " (" + movecount.value + ")";
    199     }
    200     async_return(true, solutions);
    201   } else {
    202     async_return(false, [],
    203       "Error while solving (error " + result.err.value + ")");
    204   }
    205 };
    206 
    207 function updateStatus(id, arg) {
    208   if (arg == "stop") {
    209     solveStatus = nissy.statusSTOP;
    210   } else if (arg == "pause") {
    211     solveStatus = nissy.statusPAUSE;
    212   } else {
    213     solveStatus = nissy.statusRUN;
    214   }
    215 };