Files
Daniel Ledda f26ab99960 udpate
2025-12-11 16:23:12 +01:00

526 lines
28 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// asbind/assemblyscript loader
var t="0.7.0";const e="undefined"!=typeof BigUint64Array,r=Symbol(),n=new TextDecoder("utf-16le");function s(t, e){const r=new Uint32Array(t)[e+-4>>>2]>>>1,s=new Uint16Array(t,e,r);return r<=32?String.fromCharCode.apply(String,s):n.decode(s)}function i(t){const e={};function r(t, e){return t?s(t.buffer,e):"<yet unknown>"}const n=t.env=t.env||{};return n.abort=n.abort||function(t, s, i, o){const a=e.memory||n.memory;throw Error(`abort: ${r(a,t)} at ${r(a,s)}:${i}:${o}`)},n.trace=n.trace||function(t, s, ...i){const o=e.memory||n.memory;console.log(`trace: ${r(o,t)}${s?" ":""}${i.slice(0,s).join(", ")}`)},n.seed=n.seed||Date.now,t.Math=t.Math||Math,t.Date=t.Date||Date,e}const o=function(){throw Error("Operation requires compiling with --exportRuntime")};function a(t, r){const n=r.exports,i=n.memory,a=n.table,c=n.__new||o,u=n.__pin||o,y=n.__unpin||o,l=n.__collect||o,p=n.__rtti_base,d=p?function(t){return t[p>>>2]}:o;function b(t){const e=function(t){const e=new Uint32Array(i.buffer);if((t>>>=0)>=d(e))throw Error(`invalid id: ${t}`);return e[(p+4>>>2)+2*t]}(t);if(!(7&e))throw Error(`not an array: ${t}, flags=${e}`);return e}function h(t){const e=new Uint32Array(i.buffer);if((t>>>=0)>=d(e))throw Error(`invalid id: ${t}`);return e[(p+4>>>2)+2*t+1]}function m(t){return 31-Math.clz32(t>>>6&31)}function g(t, e, r){const n=i.buffer;if(r)switch(t){case 2:return new Float32Array(n);case 3:return new Float64Array(n)}else switch(t){case 0:return new(e?Int8Array:Uint8Array)(n);case 1:return new(e?Int16Array:Uint16Array)(n);case 2:return new(e?Int32Array:Uint32Array)(n);case 3:return new(e?BigInt64Array:BigUint64Array)(n)}throw Error(`unsupported align: ${t}`)}function A(t){const e=new Uint32Array(i.buffer),r=b(e[t+-8>>>2]),n=m(r);let s=4&r?t:e[t+4>>>2];const o=2&r?e[t+12>>>2]:e[s+-4>>>2]>>>n;return g(n,2048&r,4096&r).subarray(s>>>=n,s+o)}function w(t, e, r){return new t(_(t,e,r))}function _(t, e, r){const n=i.buffer,s=new Uint32Array(n),o=s[r+4>>>2];return new t(n,o,s[o+-4>>>2]>>>e)}function T(e, r, n){t[`__get${r}`]=w.bind(null,e,n),t[`__get${r}View`]=_.bind(null,e,n)}return t.__new=c,t.__pin=u,t.__unpin=y,t.__collect=l,t.__newString=function(t){if(null==t)return 0;const e=t.length,r=c(e<<1,1),n=new Uint16Array(i.buffer);for(var s=0,o=r>>>1; s<e; ++s)n[o+s]=t.charCodeAt(s);return r},t.__getString=function(t){if(!t)return null;const e=i.buffer;if(1!==new Uint32Array(e)[t+-8>>>2])throw Error(`not a string: ${t}`);return s(e,t)},t.__newArray=function(t, e){const r=b(t),n=m(r),s=e.length,o=c(s<<n,4&r?t:0);let a;if(4&r)a=o;else{u(o);const e=c(2&r?16:12,t);y(o);const l=new Uint32Array(i.buffer);l[e+0>>>2]=o,l[e+4>>>2]=o,l[e+8>>>2]=s<<n,2&r&&(l[e+12>>>2]=s),a=e}const l=g(n,2048&r,4096&r);if(16384&r)for(let t=0; t<s; ++t){const r=e[t];l[(o>>>n)+t]=r}else l.set(e,o>>>n);return a},t.__getArrayView=A,t.__getArray=function(t){const e=A(t),r=e.length,n=new Array(r);for(let t=0; t<r; t++)n[t]=e[t];return n},t.__getArrayBuffer=function(t){const e=i.buffer,r=new Uint32Array(e)[t+-4>>>2];return e.slice(t,t+r)},[Int8Array,Uint8Array,Uint8ClampedArray,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array].forEach((t=>{T(t,t.name,31-Math.clz32(t.BYTES_PER_ELEMENT))})),e&&[BigUint64Array,BigInt64Array].forEach((t=>{T(t,t.name.slice(3),3)})),t.__instanceof=function(t, e){const r=new Uint32Array(i.buffer);let n=r[t+-8>>>2];if(n<=d(r))do{if(n==e)return!0;n=h(n)}while(n);return!1},t.memory=t.memory||i,t.table=t.table||a,f(n,t)}function c(t){return"undefined"!=typeof Response&&t instanceof Response}function u(t){return t instanceof WebAssembly.Module}async function y(t, e={}){if(c(t=await t))return l(t,e);const r=u(t)?t:await WebAssembly.compile(t),n=i(e),s=await WebAssembly.instantiate(r,e);return{module:r,instance:s,exports:a(n,s)}}async function l(t, e={}){if(!WebAssembly.instantiateStreaming)return y(c(t=await t)?t.arrayBuffer():t,e);const r=i(e),n=await WebAssembly.instantiateStreaming(t,e),s=a(r,n.instance);return{...n,exports:s}}function f(t, e={}){const n=t.__argumentsLength? e=>{t.__argumentsLength.value=e}:t.__setArgumentsLength||t.__setargc||(()=>{});for(let s in t){if(!Object.prototype.hasOwnProperty.call(t,s))continue;const i=t[s];let o=s.split("."),a=e;for(; o.length>1;){let t=o.shift();Object.prototype.hasOwnProperty.call(a,t)||(a[t]={}),a=a[t]}let c=o[0],u=c.indexOf("#");if(u>=0){const e=c.substring(0,u),o=a[e];if(void 0===o||!o.prototype){const t=function(...e){return t.wrap(t.prototype.constructor(0,...e))};t.prototype={valueOf(){return this[r]}},t.wrap=function(e){return Object.create(t.prototype,{[r]:{value:e,writable:!1}})},o&&Object.getOwnPropertyNames(o).forEach((e=>Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(o,e)))),a[e]=t}if(c=c.substring(u+1),a=a[e].prototype,/^(get|set):/.test(c)){if(!Object.prototype.hasOwnProperty.call(a,c=c.substring(4))){let e=t[s.replace("set:","get:")],n=t[s.replace("get:","set:")];Object.defineProperty(a,c,{get(){return e(this[r])},set(t){n(this[r],t)},enumerable:!0})}}else"constructor"===c?(a[c]=(...t)=>(n(t.length),i(...t))).original=i:(a[c]=function(...t){return n(t.length),i(this[r],...t)}).original=i}else/^(get|set):/.test(c)?Object.prototype.hasOwnProperty.call(a,c=c.substring(4))||Object.defineProperty(a,c,{get:t[s.replace("set:","get:")],set:t[s.replace("get:","set:")],enumerable:!0}):"function"==typeof i&&i!==n?(a[c]=(...t)=>(n(t.length),i(...t))).original=i:a[c]=i}return e}var p={instantiate:y,instantiateSync:function(t, e={}){const r=u(t)?t:new WebAssembly.Module(t),n=i(e),s=new WebAssembly.Instance(r,e);return{module:r,instance:s,exports:a(n,s)}},instantiateStreaming:l,demangle:f};function d(t, e, r){return e}function b(t, e, r){return t.exports.__getArrayBuffer(e)}function h(t, e, r){return t.exports[`__get${function(t){return t.startsWith("~lib/typedarray/")?((t=t.slice("~lib/typedarray/".length)).startsWith("Big")&&(t=t.slice(3)),t):t}(r)}View`](e)}function m(t, e, r){return t.exports.__newArray(t.getTypeId(r),e)}function g(t){if(!t.startsWith("~lib/array/Array"))throw Error(`${JSON.stringify(t)} is not an array type`);return t.slice("~lib/array/Array<".length,-1)}const A=new Map([["void",{ascToJs:d,jsToAsc:d}],[/^(i|u|f)(8|16|32|64)|[ui]size|bool|externref$/,{ascToJs:d,jsToAsc:d}],["~lib/string/String",{ascToJs:function(t, e, r){return t.exports.__getString(e)},jsToAsc:function(t, e, r){return t.exports.__newString(e)}}],["~lib/typedarray/Int8Array",{ascToJs:h,jsToAsc:m}],["~lib/typedarray/Int16Array",{ascToJs:h,jsToAsc:m}],["~lib/typedarray/Int32Array",{ascToJs:h,jsToAsc:m}],["~lib/typedarray/Uint8Array",{ascToJs:h,jsToAsc:m}],["~lib/typedarray/Uint16Array",{ascToJs:h,jsToAsc:m}],["~lib/typedarray/Uint32Array",{ascToJs:h,jsToAsc:m}],["~lib/typedarray/Int64Array",{ascToJs:h,jsToAsc:m}],["~lib/typedarray/Uint64Array",{ascToJs:h,jsToAsc:m}],["~lib/typedarray/Uint8ClampedArray",{ascToJs:h,jsToAsc:m}],["~lib/typedarray/Float32Array",{ascToJs:b,jsToAsc:m}],["~lib/typedarray/Float64Array",{ascToJs:b,jsToAsc:m}],["~lib/arraybuffer/ArrayBuffer",{ascToJs:b,jsToAsc:function(t, e, r){const n=t.exports.__new(e.byteLength,t.getTypeId(r));return new Uint8Array(t.exports.memory.buffer,n,e.byteLength).set(new Uint8Array(e)),n}}],[/^~lib\/array\/Array<.+>$/,{ascToJs:function(t, e, r){const n=g(r),s=_(n);return t.exports.__getArray(e).map((e=>s.ascToJs(t,e,n)))},jsToAsc:function(t, e, r){const n=g(r),s=_(n),i=e.map((e=>s.jsToAsc(t,e,n)));return t.exports.__newArray(t.getTypeId(r),i)}}]]),w=new Set;function _(t){for(const[e,r]of A)if("string"!=typeof e){if(e.test(t))return r}else if(e===t)return r;return w.has(t)||(console.warn(`No converter for ${JSON.stringify(t)}, using pass-through`),w.add(t)),{ascToJs:d,jsToAsc:d}}function T(t){var e;return null===(e=_(t))||void 0===e?void 0:e.ascToJs}function j(t){var e;return null===(e=_(t))||void 0===e?void 0:e.jsToAsc}function O(t, e, r){const n=r.parameters.map(T),s=j(r.returnType);return function(...i){if(i.length!=n.length)throw Error(`Expected ${n.length} arguments, got ${i.length}`);const o=i.map(((e, s)=>n[s](t,e,r.parameters[s]))),a=e(...o);return s(t,a,r.returnType)}}function U(t, e, r){const n=r.parameters.map(j),s=T(r.returnType);return(...i)=>{if(i.length!=n.length)throw Error(`Expected ${n.length} arguments, got ${i.length}`);const o=i.map(((e, s)=>n[s](t,e,r.parameters[s]))),a=e(...o);return s(t,a,r.returnType)}}function x(t, {depth:e=Number.POSITIVE_INFINITY}={}){return e<=0||!t||"object"!=typeof t?t:Object.fromEntries(Object.entries(t).map((([t,r])=>[t,x(r,{depth:e-1})])))}function E(t){const e=WebAssembly.Module.customSections(t,"as-bind_bindings"),r=new TextDecoder("utf8").decode(new Uint8Array(e[0]));try{return JSON.parse(r)}catch(t){throw Error(`Couldnt decode type descriptor: ${t.message}`)}}class S{constructor(){this.unboundExports={},this.exports={},this.importObject={}}getTypeId(t){if(t in this.typeDescriptor.typeIds)return this.typeDescriptor.typeIds[t].id;throw Error(`Unknown type ${JSON.stringify(t)}`)}getTypeSize(t){if(t in this.typeDescriptor.typeIds)return this.typeDescriptor.typeIds[t].byteSize;throw Error(`Unknown type ${JSON.stringify(t)}`)}_validate(){if(!WebAssembly.Module.exports(this.module).find((t=>"__new"===t.name)))throw Error("The AssemblyScript wasm module was not built with --exportRuntime, which is required.");if(1!==WebAssembly.Module.customSections(this.module,"as-bind_bindings").length)throw new Error("The AssemblyScript wasm module was not built with the as-bind transform.")}async _instantiate(t, e){this.module=await async function(t){if(t=await Promise.resolve(t),"undefined"!=typeof Response&&t instanceof Response){if(WebAssembly.compileStreaming)return WebAssembly.compileStreaming(t);t=await t.arrayBuffer()}return WebAssembly.compile(t)}(t),this._validate(),this.typeDescriptor=E(this.module),this._instantiateBindImportFunctions(e),this.loadedModule=await async function(t, e){return p.instantiate(t,e)}(this.module,this.importObject),this._instantiateBindUnboundExports()}_instantiateSync(t, e){this.module=new WebAssembly.Module(t),this._validate(),this.typeDescriptor=E(this.module),this._instantiateBindImportFunctions(e),this.loadedModule=function(t, e){return p.instantiateSync(t,e)}(this.module,this.importObject),this._instantiateBindUnboundExports()}_instantiateBindImportFunctions(t){this.importObject=x(t,{depth:2});for(const[e,r]of Object.entries(this.typeDescriptor.importedFunctions))for(const[n,s]of Object.entries(r))this.importObject[e][`__asbind_unbound_${n}`]=t[e][n],this.importObject[e][n]=O(this,t[e][n],s)}_instantiateBindUnboundExports(){const t=this.loadedModule.exports;this.exports=x(t,{depth:1});for(const[e,r]of Object.entries(this.typeDescriptor.exportedFunctions))this.exports[e]=U(this,t[e],r)}}async function I(t, e){let r=new S;return await r._instantiate(t,e),r}function $(t, e){let r=new S;return r._instantiateSync(t,e),r};
const instantiate = I;
// js solver
async function solveFnJs(polycubes, dimX, dimY, dimZ) {
const solver = new SomaSolver(dimX, dimY, dimZ);
const voxelSpaces = new Array();
for (let i = 0; i < polycubes.length; i++) {
voxelSpaces.push(new VoxelSpaceBoolean({
id: i,
dims: [dimX, dimY, dimZ],
space: polycubes[i],
cullEmpty: true
}));
}
await solver.solve(voxelSpaces);
return solver.getSolutions();
}
class VoxelSpaceBoolean {
constructor(options) {
this.length = options.dims[0] * options.dims[1] * options.dims[2];
if (!options.space) {
options.space = new Array(options.dims[0] * options.dims[1] * options.dims[2]);
options.space.fill(false);
}
else if (!Array.isArray(options.space)) {
const newSpace = [];
for (let i = 0; i < this.length; i++) {
const mask = 1n << BigInt(i);
newSpace.push((options.space & mask) !== 0n);
}
options.space = newSpace;
}
this.id = options.id;
this.dims = options.dims;
this.space = options.space;
this.color = options.color ?? "red";
if (options.cullEmpty !== false) {
this.cullEmptySpace();
}
}
setColor(color) {
this.color = color;
}
getColor() {
return this.color;
}
binaryRep() {
return this.space.reduce((prev, curr) => prev + (curr ? "1" : "0"), "");
}
getExtrema() {
const extrema = {
xMax: 0,
xMin: this.dims[0],
yMax: 0,
yMin: this.dims[1],
zMax: 0,
zMin: this.dims[2],
};
this.forEachCell((val, x, y, z) => {
if (val) {
extrema.xMax = Math.max(extrema.xMax, x);
extrema.xMin = Math.min(extrema.xMin, x);
extrema.yMax = Math.max(extrema.yMax, y);
extrema.yMin = Math.min(extrema.yMin, y);
extrema.zMax = Math.max(extrema.zMax, z);
extrema.zMin = Math.min(extrema.zMin, z);
}
});
return extrema;
}
cullEmptySpace() {
const extrema = this.getExtrema();
const newX = extrema.xMax - extrema.xMin + 1;
const newY = extrema.yMax - extrema.yMin + 1;
const newZ = extrema.zMax - extrema.zMin + 1;
const newSpace = new Array(newX * newY * newZ);
newSpace.fill(false);
let index = 0;
for (let x = extrema.xMin; x <= extrema.xMax; x++) {
for (let y = extrema.yMin; y <= extrema.yMax; y++) {
for (let z = extrema.zMin; z <= extrema.zMax; z++) {
if (this.at(x, y, z)) {
newSpace[index] = true;
}
index++;
}
}
}
this.dims[0] = newX;
this.dims[1] = newY;
this.dims[2] = newZ;
this.space = newSpace;
}
forEachCell(cb) {
loopStart: for (let x = 0; x < this.dims[0]; x++) {
for (let y = 0; y < this.dims[1]; y++) {
for (let z = 0; z < this.dims[2]; z++) {
if (cb(this.at(x, y, z), x, y, z) === 0) {
break loopStart;
}
}
}
}
}
getId() {
return this.id;
}
print() {
let accum = "";
console.log("---");
for (let i = 0; i < this.dims[0]; i++) {
for (let j = 0; j < this.dims[1]; j++) {
for (let k = 0; k < this.dims[2]; k++) {
accum += this.at(i, j, k) ? '#' : 'O';
}
console.log(accum);
accum = "";
}
if (i !== this.dims[0] - 1) {
console.log("-");
}
}
console.log("---");
}
getUniqueRotations() {
const rotations = [];
const refSpace = this.clone();
VoxelSpaceBoolean.pushNewUniqueSpaces(rotations, refSpace.getAxisSpins('x'));
refSpace.rot90('y');
VoxelSpaceBoolean.pushNewUniqueSpaces(rotations, refSpace.getAxisSpins('x'));
refSpace.rot90('y');
VoxelSpaceBoolean.pushNewUniqueSpaces(rotations, refSpace.getAxisSpins('x'));
refSpace.rot90('y');
VoxelSpaceBoolean.pushNewUniqueSpaces(rotations, refSpace.getAxisSpins('x'));
refSpace.rot90('z');
VoxelSpaceBoolean.pushNewUniqueSpaces(rotations, refSpace.getAxisSpins('x'));
refSpace.rot90('z');
refSpace.rot90('z');
VoxelSpaceBoolean.pushNewUniqueSpaces(rotations, refSpace.getAxisSpins('x'));
return rotations;
}
getAllRotations() {
const rotations = [];
const refSpace = this.clone();
rotations.push(...refSpace.getAxisSpins('x'));
refSpace.rot90('y');
rotations.push(...refSpace.getAxisSpins('x'));
refSpace.rot90('y');
rotations.push(...refSpace.getAxisSpins('x'));
refSpace.rot90('y');
rotations.push(...refSpace.getAxisSpins('x'));
refSpace.rot90('z');
rotations.push(...refSpace.getAxisSpins('x'));
refSpace.rot90('z');
refSpace.rot90('z');
rotations.push(...refSpace.getAxisSpins('x'));
return rotations;
}
static pushNewUniqueSpaces(existingSpaces, newSpaces) {
for (const newSpace of newSpaces) {
let matchFound = false;
for (const existingSpace of existingSpaces) {
if (newSpace.matches(existingSpace)) {
matchFound = true;
break;
}
}
if (!matchFound) {
existingSpaces.push(newSpace);
}
}
}
getAllPositionsInPrism(cubeDimX, cubeDimY, cubeDimZ) {
const cubePositions = [];
if (this.dims[0] > cubeDimX || this.dims[1] > cubeDimY || this.dims[2] > cubeDimZ) {
return cubePositions;
}
for (let xOffset = 0; xOffset < (cubeDimX - this.dims[0] + 1); xOffset++) {
for (let yOffset = 0; yOffset < (cubeDimY - this.dims[1] + 1); yOffset++) {
for (let zOffset = 0; zOffset < (cubeDimZ - this.dims[2] + 1); zOffset++) {
const cubePos = new VoxelSpaceBoolean({ id: this.id, dims: [cubeDimX, cubeDimY, cubeDimZ], color: this.color, cullEmpty: false });
this.forEachCell((val, x, y, z) => {
cubePos.set(xOffset + x, yOffset + y, zOffset + z, val);
});
cubePositions.push(cubePos);
}
}
}
return cubePositions;
}
matches(space) {
const otherDims = space.getDims();
for (let i = 0; i < this.dims.length; i++) {
if (otherDims[i] !== this.dims[i]) {
return false;
}
}
const otherRaw = space.getRaw();
if (typeof otherRaw === "bigint") {
return space.binaryRep() === this.binaryRep();
}
return this.space.reduce((prev, unit, i) => (unit === otherRaw[i]) && prev, true);
}
clone() {
return new VoxelSpaceBoolean({ id: this.id, dims: this.getDims(), space: this.getRaw(), color: this.getColor(), cullEmpty: false });
}
getAxisSpins(axis) {
const rotations = [this.clone()];
for (let i = 0; i < 3; i++) {
rotations.push(rotations[i].rotated90(axis));
}
return rotations;
}
getDims() {
return this.dims.slice();
}
getRaw() {
return this.space.slice();
}
// [1, 0, 0] [x] [ x]
// [0, 0, -1] * [y] = [-z]
// [0, 1, 0] [z] [ y]
newIndexRotX(x, y, z) {
return this.dims[2] * this.dims[1] * x + this.dims[1] * (this.dims[2] - 1 - z) + y;
}
// [ 0, 0, 1] [x] [ z]
// [ 0, 1, 0] * [y] = [ y]
// [-1, 0, 0] [z] [-x]
newIndexRotY(x, y, z) {
return this.dims[1] * this.dims[0] * z + this.dims[0] * y + (this.dims[0] - 1 - x);
}
// [0, -1, 0] [x] [-y]
// [1, 0, 0] * [y] = [ x]
// [0, 0, 1] [z] [ z]
newIndexRotZ(x, y, z) {
return this.dims[0] * this.dims[2] * (this.dims[1] - 1 - y) + this.dims[2] * x + z;
}
at(x, y, z) {
return this.space[this.index(x, y, z)];
}
index(x, y, z) {
return this.dims[1] * this.dims[2] * x + this.dims[2] * y + z;
}
toggle(x, y, z) {
const index = this.index(x, y, z);
this.space[index] = !this.space[index];
}
set(x, y, z, val) {
this.space[this.index(x, y, z)] = val;
}
rotated90(dim) {
const newSpace = new Array(this.dims[0] * this.dims[1] * this.dims[2]);
newSpace.fill(false);
let newDims;
let rotIndex;
if (dim === 'x') {
newDims = [this.dims[0], this.dims[2], this.dims[1]];
rotIndex = this.newIndexRotX.bind(this);
}
else if (dim === 'y') {
newDims = [this.dims[2], this.dims[1], this.dims[0]];
rotIndex = this.newIndexRotY.bind(this);
}
else {
newDims = [this.dims[1], this.dims[0], this.dims[2]];
rotIndex = this.newIndexRotZ.bind(this);
}
this.forEachCell((val, i, j, k) => {
if (val) {
newSpace[rotIndex(i, j, k)] = true;
}
});
return new VoxelSpaceBoolean({ id: this.id, dims: newDims, space: newSpace, color: this.color, cullEmpty: false });
}
rot90(dim) {
const rot = this.rotated90(dim);
this.space = rot.getRaw();
this.dims = rot.getDims();
}
plus(space) {
const newSpace = new Array(this.dims[0] * this.dims[1] * this.dims[2]);
newSpace.fill(false);
let clash = false;
space.forEachCell((val, x, y, z) => {
if (this.at(x, y, z) !== val) {
newSpace[this.index(x, y, z)] = true;
}
else {
if (val) {
clash = true;
}
}
});
if (!clash) {
return new VoxelSpaceBoolean({ id: this.id, dims: this.getDims(), space: newSpace, color: this.color, cullEmpty: false });
}
return null;
}
size() {
let size = 0;
this.forEachCell((val) => {
if (val) {
size++;
}
});
return size;
}
getDirectNeighbourProfile(x, y, z) {
let result = 0;
if (x < this.dims[0] - 1 && this.at(x + 1, y, z)) {
result += 1;
}
if (y < this.dims[1] - 1 && this.at(x, y + 1, z)) {
result += 2;
}
if (z < this.dims[2] - 1 && this.at(x, y, z + 1)) {
result += 4;
}
if (x > 0 && this.at(x - 1, y, z)) {
result += 8;
}
if (y > 0 && this.at(x, y - 1, z)) {
result += 16;
}
if (z > 0 && this.at(x, y, z - 1)) {
result += 32;
}
return result;
}
getAllPermutationsInPrism(prismDimX, prismDimY, prismDimZ) {
const rotations = this.getUniqueRotations();
let result = new Array();
for (let i = 0; i < rotations.length; i++) {
result = result.concat(rotations[i].getAllPositionsInPrism(prismDimX, prismDimY, prismDimZ));
}
return result;
}
}
class SomaSolver {
constructor(dimX, dimY, dimZ) {
this.visualiser = { async showSoln(soln) { }, async showSpace(cube) { } };
this.solutions = new Array();
this.iterations = 0;
this.dimX = dimX;
this.dimY = dimY;
this.dimZ = dimZ;
this.solutionCube = new VoxelSpaceBoolean({ id: 0, dims: [dimX, dimY, dimZ], cullEmpty: false });
}
setDebug(visualiser) {
this.visualiser = visualiser;
}
async solve(polycubes) {
if (polycubes.length === 0) {
throw new Error("You must pass at least one polycube to solve the puzzle.");
}
this.solutions.splice(0, this.solutions.length);
const combosWithRots = new Array();
for (let i = 1; i < polycubes.length; i++) {
const rots = polycubes[i].getAllPermutationsInPrism(this.dimX, this.dimY, this.dimZ);
combosWithRots.push(rots);
}
let combos = new Array();
combos.push(polycubes[0].getAllPositionsInPrism(this.dimX, this.dimY, this.dimZ));
combos = combos.concat(combosWithRots);
for (const combo of combos) {
for (const rot of combo) {
await this.visualiser.showSpace(rot);
}
}
await this.backtrackSolve(this.solutionCube, combos, new SomaSolution(this.dimX, this.dimY, this.dimZ));
this.solutions = SomaSolution.filterUnique(this.solutions);
}
getSolutions() {
return this.solutions.slice();
}
async backtrackSolve(workingSolution, polycubes, currentSoln, depth = 0) {
const nextCubeGroup = polycubes[0];
for (let i = 0; i < nextCubeGroup.length; i++) {
const fusionAttempt = workingSolution.plus(nextCubeGroup[i]);
++this.iterations;
if (fusionAttempt) {
const nextSoln = currentSoln.clone();
nextSoln.addSpace(nextCubeGroup[i]);
await this.visualiser.showSoln(nextSoln);
if (polycubes.length === 1) {
this.solutions.push(nextSoln);
currentSoln = new SomaSolution(this.dimX, this.dimY, this.dimZ);
return;
}
else {
await this.backtrackSolve(fusionAttempt, polycubes.slice(1), nextSoln, depth + 1);
}
}
}
}
}
class SomaSolution {
constructor(dimX, dimY, dimZ) {
this.dimX = dimX;
this.dimY = dimY;
this.dimZ = dimZ;
this.solutionSpaces = [];
}
static filterUnique(solutions) {
if (solutions.length === 0) {
return [];
}
const uniqueSolns = [solutions[0]];
for (const solution of solutions) {
let foundMatch = false;
for (const rotation of solution.getRotations()) {
let end = uniqueSolns.length;
for (let i = 0; i < end; i++) {
if (rotation.matches(uniqueSolns[i])) {
foundMatch = true;
}
}
}
if (!foundMatch) {
uniqueSolns.push(solution);
}
}
return uniqueSolns;
}
getRotations() {
if (this.solutionSpaces.length === 0) {
return [];
}
const result = [];
const allRots = this.solutionSpaces.map(space => space.getAllRotations());
for (let i = 0; i < allRots[0].length; i++) {
const solnRot = new SomaSolution(this.dimX, this.dimY, this.dimZ);
allRots.forEach(rotGroup => solnRot.addSpace(rotGroup[i]));
result.push(solnRot);
}
return result;
}
matches(solution) {
for (let i = 0; i < this.solutionSpaces.length; i++) {
if (!this.solutionSpaces[i].matches(solution.solutionSpaces[i])) {
return false;
}
}
return true;
}
addSpace(space) {
this.solutionSpaces.push(space);
}
print() {
let accum = "";
console.log("---");
for (let x = 0; x < this.dimX; x++) {
for (let y = 0; y < this.dimY; y++) {
for (let z = 0; z < this.dimZ; z++) {
for (const space of this.solutionSpaces) {
if (space.at(x, y, z)) {
accum += space.getId();
}
}
}
console.log(accum);
accum = "";
}
if (x !== this.dimX - 1) {
console.log("-");
}
}
console.log("---");
}
at(x, y, z) {
for (const space of this.solutionSpaces) {
if (space.at(x, y, z)) {
return space.getId();
}
}
return 0;
}
clone() {
const clone = new SomaSolution(this.dimX, this.dimY, this.dimZ);
clone.solutionSpaces = this.solutionSpaces.slice();
return clone;
}
getDims() {
return [this.dimX, this.dimY, this.dimZ];
}
forEachCell(cb) {
loopStart: for (let x = 0; x < this.dimX; x++) {
for (let y = 0; y < this.dimY; y++) {
for (let z = 0; z < this.dimZ; z++) {
cb(this.at(x, y, z), x, y, z);
}
}
}
}
getPieces() {
return this.solutionSpaces.slice();
}
}
// init wasm
let solveFnWasm;
async function load() {
const file = fetch("../solver/main.wasm");
const instance = await instantiate(file);
solveFnWasm = instance.exports.solve;
}
load();
// worker
self.addEventListener('message', async (event) => {
const { type, polycubes, dimX, dimY, dimZ } = event.data;
if (type === "wasm") {
const solutions = solveFnWasm(polycubes, dimX, dimY, dimZ);
self.postMessage(solutions.map(soln => soln.toString()));
} else if (type === "js") {
const solutions = await solveFnJs(polycubes, dimX, dimY, dimZ);
self.postMessage({solns: solutions.map(soln => soln.getPieces().map(piece => piece.binaryRep())), log: JSON.stringify(solutions)});
} else {
self.postMessage({error: `Invalid solver type passed to worker: ${type}`})
}
});