First commit
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/node_modules
|
||||
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
@@ -0,0 +1,5 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
||||
</state>
|
||||
</component>
|
||||
6
.idea/deno.xml
generated
Normal file
6
.idea/deno.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DenoSettings">
|
||||
<option name="useDeno" value="true" />
|
||||
</component>
|
||||
</project>
|
||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/soma.iml" filepath="$PROJECT_DIR$/.idea/soma.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
12
.idea/soma.iml
generated
Normal file
12
.idea/soma.iml
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
90
.idea/workspace.xml
generated
Normal file
90
.idea/workspace.xml
generated
Normal file
@@ -0,0 +1,90 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="70635ef7-86ab-4681-b98d-dc8e4999995b" name="Default Changelist" comment="" />
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||
</component>
|
||||
<component name="FileTemplateManagerImpl">
|
||||
<option name="RECENT_TEMPLATES">
|
||||
<list>
|
||||
<option value="HTML File" />
|
||||
<option value="JavaScript File" />
|
||||
<option value="TypeScript File" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectId" id="1shUz7NMSUxY207ip64fWIgaebh" />
|
||||
<component name="ProjectViewState">
|
||||
<option name="hideEmptyMiddlePackages" value="true" />
|
||||
<option name="showLibraryContents" value="true" />
|
||||
</component>
|
||||
<component name="PropertiesComponent">
|
||||
<property name="DefaultHtmlFileTemplate" value="HTML File" />
|
||||
<property name="RunOnceActivity.OpenProjectViewOnStart" value="true" />
|
||||
<property name="WebServerToolWindowFactoryState" value="false" />
|
||||
<property name="node.js.detected.package.eslint" value="true" />
|
||||
<property name="node.js.detected.package.tslint" value="true" />
|
||||
<property name="node.js.path.for.package.eslint" value="project" />
|
||||
<property name="node.js.path.for.package.tslint" value="project" />
|
||||
<property name="node.js.selected.package.eslint" value="(autodetect)" />
|
||||
<property name="node.js.selected.package.tslint" value="(autodetect)" />
|
||||
<property name="nodejs_package_manager_path" value="npm" />
|
||||
<property name="ts.external.directory.path" value="$PROJECT_DIR$/node_modules/typescript/lib" />
|
||||
<property name="vue.rearranger.settings.migration" value="true" />
|
||||
</component>
|
||||
<component name="RecentsManager">
|
||||
<key name="MoveFile.RECENT_KEYS">
|
||||
<recent name="$PROJECT_DIR$/src" />
|
||||
</key>
|
||||
</component>
|
||||
<component name="RunManager">
|
||||
<configuration name="test.html" type="JavascriptDebugType" temporary="true" nameIsGenerated="true" uri="http://localhost:63343/soma/src/test.html" useBuiltInWebServerPort="true">
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
<recent_temporary>
|
||||
<list>
|
||||
<item itemvalue="JavaScript Debug.test.html" />
|
||||
</list>
|
||||
</recent_temporary>
|
||||
</component>
|
||||
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
|
||||
<component name="TaskManager">
|
||||
<task active="true" id="Default" summary="Default task">
|
||||
<changelist id="70635ef7-86ab-4681-b98d-dc8e4999995b" name="Default Changelist" comment="" />
|
||||
<created>1621324574891</created>
|
||||
<option name="number" value="Default" />
|
||||
<option name="presentableId" value="Default" />
|
||||
<updated>1621324574891</updated>
|
||||
<workItem from="1621324585296" duration="36860000" />
|
||||
</task>
|
||||
<servers />
|
||||
</component>
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
<option name="version" value="3" />
|
||||
<option name="exactExcludedFiles">
|
||||
<list>
|
||||
<option value="$PROJECT_DIR$/src/main.js" />
|
||||
<option value="$PROJECT_DIR$/src/VoxelSpace.js" />
|
||||
<option value="$PROJECT_DIR$/src/SomaSolver.js" />
|
||||
<option value="$PROJECT_DIR$/src/test.js" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="WindowStateProjectService">
|
||||
<state x="643" y="194" width="614" height="706" key="#com.intellij.refactoring.rename.AutomaticRenamingDialog" timestamp="1621349929863">
|
||||
<screen x="0" y="27" width="1920" height="1053" />
|
||||
</state>
|
||||
<state x="643" y="194" width="614" height="706" key="#com.intellij.refactoring.rename.AutomaticRenamingDialog/1920.0.1920.1080/0.27.1920.1053@0.27.1920.1053" timestamp="1621349929863" />
|
||||
<state x="624" y="245" key="run.anything.popup" timestamp="1621340767679">
|
||||
<screen x="0" y="27" width="1920" height="1053" />
|
||||
</state>
|
||||
<state x="624" y="245" key="run.anything.popup/1920.0.1920.1080/0.27.1920.1053@0.27.1920.1053" timestamp="1621340767679" />
|
||||
<state x="623" y="239" width="672" height="678" key="search.everywhere.popup" timestamp="1621363038727">
|
||||
<screen x="0" y="27" width="1920" height="1053" />
|
||||
</state>
|
||||
<state x="623" y="239" width="672" height="678" key="search.everywhere.popup/1920.0.1920.1080/0.27.1920.1053@0.27.1920.1053" timestamp="1621363038727" />
|
||||
</component>
|
||||
</project>
|
||||
157
package-lock.json
generated
Normal file
157
package-lock.json
generated
Normal file
@@ -0,0 +1,157 @@
|
||||
{
|
||||
"name": "soma",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"@haensl/log": {
|
||||
"version": "1.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@haensl/log/-/log-1.3.4.tgz",
|
||||
"integrity": "sha512-6AZuT9NQVbl5Bh5FW31rN7IO7IctF4plPvENnUHFRCdBqPJ5+zJ3LzuXoRPy6ZHd4KSsbJP9sVU552BEppmIUg==",
|
||||
"requires": {
|
||||
"chalk": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"requires": {
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"chalk": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz",
|
||||
"integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==",
|
||||
"requires": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"requires": {
|
||||
"color-name": "~1.1.4"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
|
||||
},
|
||||
"fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
|
||||
},
|
||||
"glob": {
|
||||
"version": "7.1.7",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
|
||||
"integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==",
|
||||
"requires": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "^3.0.4",
|
||||
"once": "^1.3.0",
|
||||
"path-is-absolute": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
|
||||
},
|
||||
"inflight": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
|
||||
"requires": {
|
||||
"once": "^1.3.0",
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
},
|
||||
"js-profiler": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/js-profiler/-/js-profiler-2.4.2.tgz",
|
||||
"integrity": "sha512-wbOwWUQKYjJJEym2z+OkpApFaIjUv9CKNPHGMTe/UkGz5lLYy1PrDCzH0goR0ocEWjFCew+nYgk9ioVHob9Bmw==",
|
||||
"requires": {
|
||||
"@haensl/log": "^1.2.2",
|
||||
"chalk": "^4.1.0",
|
||||
"glob": "^7.1.6",
|
||||
"node-getopt": "^0.3.2"
|
||||
}
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
},
|
||||
"node-getopt": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/node-getopt/-/node-getopt-0.3.2.tgz",
|
||||
"integrity": "sha512-yqkmYrMbK1wPrfz7mgeYvA4tBperLg9FQ4S3Sau3nSAkpOA0x0zC8nQ1siBwozy1f4SE8vq2n1WKv99r+PCa1Q=="
|
||||
},
|
||||
"once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"requires": {
|
||||
"has-flag": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"typescript": {
|
||||
"version": "4.2.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz",
|
||||
"integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg=="
|
||||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
|
||||
}
|
||||
}
|
||||
}
|
||||
15
package.json
Normal file
15
package.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "soma",
|
||||
"version": "1.0.0",
|
||||
"description": "Custom Somaesque cube solver webapp",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"start": "deno run ./src/main.ts"
|
||||
},
|
||||
"author": "Daniel Ledda",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"js-profiler": "^2.4.2",
|
||||
"typescript": "^4.2.4"
|
||||
}
|
||||
}
|
||||
84
src/Polycube.js
Normal file
84
src/Polycube.js
Normal file
@@ -0,0 +1,84 @@
|
||||
"use strict";
|
||||
var __extends = (this && this.__extends) || (function () {
|
||||
var extendStatics = function (d, b) {
|
||||
extendStatics = Object.setPrototypeOf ||
|
||||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
||||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
|
||||
return extendStatics(d, b);
|
||||
};
|
||||
return function (d, b) {
|
||||
extendStatics(d, b);
|
||||
function __() { this.constructor = d; }
|
||||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
||||
};
|
||||
})();
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var VoxelSpace_1 = __importDefault(require("./VoxelSpace"));
|
||||
var Polycube = /** @class */ (function (_super) {
|
||||
__extends(Polycube, _super);
|
||||
function Polycube(dims, vals, id) {
|
||||
var _this = _super.call(this, dims, vals.map(function (val) { return val ? id : 0; }), true) || this;
|
||||
_this.id = id;
|
||||
return _this;
|
||||
}
|
||||
Polycube.prototype.getId = function () {
|
||||
return this.id;
|
||||
};
|
||||
Polycube.prototype.print = function () {
|
||||
var accum = "";
|
||||
console.log("---");
|
||||
for (var i = 0; i < this.dims[0]; i++) {
|
||||
for (var j = 0; j < this.dims[1]; j++) {
|
||||
for (var k = 0; k < this.dims[2]; k++) {
|
||||
accum += this.at(i, j, k) === 0 ? "O" : "#";
|
||||
}
|
||||
console.log(accum);
|
||||
accum = "";
|
||||
}
|
||||
if (i !== this.dims[0] - 1) {
|
||||
console.log("-");
|
||||
}
|
||||
}
|
||||
console.log("---");
|
||||
};
|
||||
Polycube.prototype.matches = function (cube) {
|
||||
var otherDims = cube.getDims();
|
||||
for (var i = 0; i < this.dims.length; i++) {
|
||||
if (otherDims[i] !== this.dims[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
var otherVals = cube.getVals();
|
||||
for (var i = 0; i < this.vals.length; i++) {
|
||||
if (Number(this.vals[i]) !== Number(otherVals[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
Polycube.prototype.size = function () {
|
||||
var size = 0;
|
||||
this.forEachCell(function (val) {
|
||||
if (val) {
|
||||
size++;
|
||||
}
|
||||
});
|
||||
return size;
|
||||
};
|
||||
Polycube.prototype.rotated90 = function (dim) {
|
||||
var rotated = _super.prototype.rotated90.call(this, dim);
|
||||
return new Polycube(rotated.getDims(), rotated.getVals(), this.id);
|
||||
};
|
||||
Polycube.prototype.clone = function () {
|
||||
return new Polycube(this.getDims(), this.getVals(), this.id);
|
||||
};
|
||||
Polycube.prototype.getUniqueRotations = function () {
|
||||
var _this = this;
|
||||
return _super.prototype.getUniqueRotations.call(this).map(function (rot) { return new Polycube(rot.getDims(), rot.getVals(), _this.id); });
|
||||
};
|
||||
return Polycube;
|
||||
}(VoxelSpace_1.default));
|
||||
exports.default = Polycube;
|
||||
70
src/Polycube.ts
Normal file
70
src/Polycube.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import VoxelSpace, {DimensionDef} from "./VoxelSpace";
|
||||
|
||||
export default class Polycube extends VoxelSpace {
|
||||
private id: number;
|
||||
constructor(dims: DimensionDef, vals: boolean[], id: number) {
|
||||
super(dims, vals.map(val => val ? id : 0), true);
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
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) === 0 ? "O" : "#";
|
||||
}
|
||||
console.log(accum);
|
||||
accum = "";
|
||||
}
|
||||
if (i !== this.dims[0] - 1) {
|
||||
console.log("-");
|
||||
}
|
||||
}
|
||||
console.log("---");
|
||||
}
|
||||
|
||||
matches(cube: VoxelSpace) {
|
||||
const otherDims = cube.getDims();
|
||||
for (let i = 0; i < this.dims.length; i++) {
|
||||
if (otherDims[i] !== this.dims[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const otherVals = cube.getVals();
|
||||
for (let i = 0; i < this.vals.length; i++) {
|
||||
if (Number(this.vals[i]) !== Number(otherVals[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
size() {
|
||||
let size = 0;
|
||||
this.forEachCell((val) => {
|
||||
if (val) {
|
||||
size++;
|
||||
}
|
||||
});
|
||||
return size;
|
||||
}
|
||||
|
||||
rotated90(dim: "x" | "y" | "z"): Polycube {
|
||||
const rotated = super.rotated90(dim);
|
||||
return new Polycube(rotated.getDims(), rotated.getVals() as unknown as boolean[], this.id);
|
||||
}
|
||||
|
||||
clone(): Polycube {
|
||||
return new Polycube(this.getDims(), this.getVals() as unknown as boolean[], this.id);
|
||||
}
|
||||
|
||||
getUniqueRotations(): Polycube[] {
|
||||
return super.getUniqueRotations().map(rot => new Polycube(rot.getDims(), rot.getVals() as unknown as boolean[], this.id));
|
||||
}
|
||||
}
|
||||
58
src/SomaSolver.js
Normal file
58
src/SomaSolver.js
Normal file
@@ -0,0 +1,58 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var VoxelSpace_1 = __importDefault(require("./VoxelSpace"));
|
||||
var SomaSolver = /** @class */ (function () {
|
||||
function SomaSolver(dimension) {
|
||||
this.solutions = [];
|
||||
this.iterations = 0;
|
||||
if (dimension % 1 !== 0 || dimension < 0) {
|
||||
throw new Error("The argument 'dimension' must be a positive whole number");
|
||||
}
|
||||
this.dim = dimension;
|
||||
this.solutionCube = new VoxelSpace_1.default([dimension, dimension, dimension], Array(Math.pow(dimension, 3)).fill(0));
|
||||
}
|
||||
SomaSolver.prototype.solve = function (polycubes) {
|
||||
if (polycubes.length === 0) {
|
||||
throw new Error("You must pass at least one polycube to solve the puzzle.");
|
||||
}
|
||||
var cumulativeSize = polycubes.reduce(function (prev, curr) { return prev + curr.size(); }, 0);
|
||||
if (cumulativeSize !== Math.pow(this.dim, 3)) {
|
||||
throw new Error("The polycubes passed do not add up to exactly enough units to form a cube of dimension " + this.dim + "! Got: " + cumulativeSize + ", need: " + Math.pow(this.dim, 3));
|
||||
}
|
||||
this.iterations = 0;
|
||||
this.backtrackSolve(this.solutionCube, polycubes);
|
||||
this.solutions = VoxelSpace_1.default.filterUnique(this.solutions);
|
||||
this.solutions.forEach(function (sol) { return sol.print(); });
|
||||
console.log(this.solutions.length);
|
||||
};
|
||||
SomaSolver.prototype.backtrackSolve = function (workingSolution, polycubes, depth) {
|
||||
if (depth === void 0) { depth = 0; }
|
||||
var nextCube = polycubes[0];
|
||||
var rots = depth === 0 ? [nextCube] : nextCube.getUniqueRotations();
|
||||
for (var i = 0; i < rots.length; i++) {
|
||||
var polyCubeDims = rots[i].getDims();
|
||||
for (var x = 0; x < this.dim - polyCubeDims[0] + 1; x++) {
|
||||
for (var y = 0; y < this.dim - polyCubeDims[1] + 1; y++) {
|
||||
for (var z = 0; z < this.dim - polyCubeDims[2] + 1; z++) {
|
||||
var successfulFusion = workingSolution.plus(rots[i], x, y, z);
|
||||
if (successfulFusion) {
|
||||
if (polycubes.length === 1) {
|
||||
console.log("soln", this.iterations++);
|
||||
this.solutions.push(successfulFusion);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
this.backtrackSolve(successfulFusion, polycubes.slice(1), depth + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
return SomaSolver;
|
||||
}());
|
||||
exports.default = SomaSolver;
|
||||
55
src/SomaSolver.ts
Normal file
55
src/SomaSolver.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import Polycube from "./Polycube";
|
||||
import VoxelSpace from "./VoxelSpace";
|
||||
|
||||
export default class SomaSolver {
|
||||
private solutionCube: VoxelSpace;
|
||||
private dim: number;
|
||||
private solutions: VoxelSpace[] = [];
|
||||
private iterations: number = 0;
|
||||
constructor(dimension: number) {
|
||||
if (dimension % 1 !== 0 || dimension < 0) {
|
||||
throw new Error("The argument 'dimension' must be a positive whole number");
|
||||
}
|
||||
this.dim = dimension;
|
||||
this.solutionCube = new VoxelSpace([dimension, dimension, dimension], Array(dimension**3).fill(0));
|
||||
}
|
||||
|
||||
solve(polycubes: Polycube[]) {
|
||||
if (polycubes.length === 0) {
|
||||
throw new Error("You must pass at least one polycube to solve the puzzle.");
|
||||
}
|
||||
let cumulativeSize = polycubes.reduce((prev, curr) => prev + curr.size(), 0);
|
||||
if (cumulativeSize !== this.dim**3) {
|
||||
throw new Error(`The polycubes passed do not add up to exactly enough units to form a cube of dimension ${this.dim}! Got: ${cumulativeSize}, need: ${this.dim**3}`);
|
||||
}
|
||||
this.iterations = 0;
|
||||
this.backtrackSolve(this.solutionCube, polycubes);
|
||||
this.solutions = VoxelSpace.filterUnique(this.solutions);
|
||||
this.solutions.forEach(sol => sol.print());
|
||||
console.log(this.solutions.length);
|
||||
}
|
||||
|
||||
private backtrackSolve(workingSolution: VoxelSpace, polycubes: Polycube[], depth = 0) {
|
||||
const nextCube = polycubes[0];
|
||||
const rots = depth === 0 ? [nextCube] : nextCube.getUniqueRotations();
|
||||
for (let i = 0; i < rots.length; i++) {
|
||||
const polyCubeDims = rots[i].getDims();
|
||||
for (let x = 0; x < this.dim - polyCubeDims[0] + 1; x++) {
|
||||
for (let y = 0; y < this.dim - polyCubeDims[1] + 1; y++) {
|
||||
for (let z = 0; z < this.dim - polyCubeDims[2] + 1; z++) {
|
||||
const successfulFusion = workingSolution.plus(rots[i], x, y, z);
|
||||
if (successfulFusion) {
|
||||
if (polycubes.length === 1) {
|
||||
console.log("soln", this.iterations++);
|
||||
this.solutions.push(successfulFusion);
|
||||
return;
|
||||
} else {
|
||||
this.backtrackSolve(successfulFusion, polycubes.slice(1), depth + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
239
src/VoxelSpace.js
Normal file
239
src/VoxelSpace.js
Normal file
@@ -0,0 +1,239 @@
|
||||
"use strict";
|
||||
var __spreadArrays = (this && this.__spreadArrays) || function () {
|
||||
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
|
||||
for (var r = Array(s), k = 0, i = 0; i < il; i++)
|
||||
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
|
||||
r[k] = a[j];
|
||||
return r;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var VoxelSpace = /** @class */ (function () {
|
||||
function VoxelSpace(dims, vals, cullEmpty) {
|
||||
if (vals.length !== dims[0] * dims[1] * dims[2]) {
|
||||
throw new Error("Vals don't fit in given dimensions.");
|
||||
}
|
||||
this.dims = dims;
|
||||
this.vals = vals;
|
||||
if (cullEmpty) {
|
||||
this.cullEmptySpace();
|
||||
}
|
||||
}
|
||||
VoxelSpace.prototype.cullEmptySpace = function () {
|
||||
var extrema = {
|
||||
xMax: -Infinity,
|
||||
xMin: Infinity,
|
||||
yMax: -Infinity,
|
||||
yMin: Infinity,
|
||||
zMax: -Infinity,
|
||||
zMin: Infinity,
|
||||
};
|
||||
var newVals = [];
|
||||
this.forEachCell(function (val, i, j, k) {
|
||||
if (val !== 0) {
|
||||
extrema.xMax = Math.max(extrema.xMax, i);
|
||||
extrema.xMin = Math.min(extrema.xMin, i);
|
||||
extrema.yMax = Math.max(extrema.yMax, j);
|
||||
extrema.yMin = Math.min(extrema.yMin, j);
|
||||
extrema.zMax = Math.max(extrema.zMax, k);
|
||||
extrema.zMin = Math.min(extrema.zMin, k);
|
||||
}
|
||||
});
|
||||
for (var i = extrema.xMin; i <= extrema.xMax; i++) {
|
||||
for (var j = extrema.yMin; j <= extrema.yMax; j++) {
|
||||
for (var k = extrema.zMin; k <= extrema.zMax; k++) {
|
||||
newVals.push(this.at(i, j, k));
|
||||
}
|
||||
}
|
||||
}
|
||||
this.dims[0] = extrema.xMax - extrema.xMin + 1;
|
||||
this.dims[1] = extrema.yMax - extrema.yMin + 1;
|
||||
this.dims[2] = extrema.zMax - extrema.zMin + 1;
|
||||
this.vals = newVals;
|
||||
};
|
||||
VoxelSpace.prototype.forEachCell = function (cb) {
|
||||
loopStart: for (var x = 0; x < this.dims[0]; x++) {
|
||||
for (var y = 0; y < this.dims[1]; y++) {
|
||||
for (var z = 0; z < this.dims[2]; z++) {
|
||||
if (cb(this.at(x, y, z), x, y, z) === 0) {
|
||||
break loopStart;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
VoxelSpace.prototype.print = function () {
|
||||
var accum = "";
|
||||
console.log("---");
|
||||
for (var i = 0; i < this.dims[0]; i++) {
|
||||
for (var j = 0; j < this.dims[1]; j++) {
|
||||
for (var k = 0; k < this.dims[2]; k++) {
|
||||
accum += this.at(i, j, k);
|
||||
}
|
||||
console.log(accum);
|
||||
accum = "";
|
||||
}
|
||||
if (i !== this.dims[0] - 1) {
|
||||
console.log("-");
|
||||
}
|
||||
}
|
||||
console.log("---");
|
||||
};
|
||||
VoxelSpace.prototype.getUniqueRotations = function () {
|
||||
var rotations = [];
|
||||
var refSpace = this.clone();
|
||||
VoxelSpace.pushNewUniqueSpaces(rotations, refSpace.getAxisSpins('x'));
|
||||
refSpace.rot90('y');
|
||||
VoxelSpace.pushNewUniqueSpaces(rotations, refSpace.getAxisSpins('x'));
|
||||
refSpace.rot90('y');
|
||||
VoxelSpace.pushNewUniqueSpaces(rotations, refSpace.getAxisSpins('x'));
|
||||
refSpace.rot90('y');
|
||||
VoxelSpace.pushNewUniqueSpaces(rotations, refSpace.getAxisSpins('x'));
|
||||
refSpace.rot90('z');
|
||||
VoxelSpace.pushNewUniqueSpaces(rotations, refSpace.getAxisSpins('x'));
|
||||
refSpace.rot90('z');
|
||||
refSpace.rot90('z');
|
||||
VoxelSpace.pushNewUniqueSpaces(rotations, refSpace.getAxisSpins('x'));
|
||||
return rotations;
|
||||
};
|
||||
VoxelSpace.filterUnique = function (spaces) {
|
||||
if (spaces.length === 0) {
|
||||
return [];
|
||||
}
|
||||
var uniqueSpaces = [spaces[0]];
|
||||
for (var _i = 0, spaces_1 = spaces; _i < spaces_1.length; _i++) {
|
||||
var space = spaces_1[_i];
|
||||
var foundMatch = false;
|
||||
for (var _a = 0, _b = space.getUniqueRotations(); _a < _b.length; _a++) {
|
||||
var rotation = _b[_a];
|
||||
var end = uniqueSpaces.length;
|
||||
for (var i = 0; i < end; i++) {
|
||||
if (rotation.matches(uniqueSpaces[i])) {
|
||||
foundMatch = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!foundMatch) {
|
||||
uniqueSpaces.push(space);
|
||||
}
|
||||
}
|
||||
return uniqueSpaces;
|
||||
};
|
||||
VoxelSpace.pushNewUniqueSpaces = function (existingSpaces, newSpaces) {
|
||||
for (var _i = 0, newSpaces_1 = newSpaces; _i < newSpaces_1.length; _i++) {
|
||||
var newSpace = newSpaces_1[_i];
|
||||
var matchFound = false;
|
||||
for (var _a = 0, existingSpaces_1 = existingSpaces; _a < existingSpaces_1.length; _a++) {
|
||||
var existingSpace = existingSpaces_1[_a];
|
||||
if (newSpace.matches(existingSpace)) {
|
||||
matchFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!matchFound) {
|
||||
existingSpaces.push(newSpace);
|
||||
}
|
||||
}
|
||||
};
|
||||
VoxelSpace.prototype.matches = function (space) {
|
||||
var otherDims = space.getDims();
|
||||
for (var i = 0; i < this.dims.length; i++) {
|
||||
if (otherDims[i] !== this.dims[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
var otherVals = space.getVals();
|
||||
for (var i = 0; i < this.vals.length; i++) {
|
||||
if (this.vals[i] !== otherVals[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
VoxelSpace.prototype.clone = function () {
|
||||
return new VoxelSpace(this.getDims(), this.getVals());
|
||||
};
|
||||
VoxelSpace.prototype.getAxisSpins = function (axis) {
|
||||
var rotations = [this.clone()];
|
||||
for (var i = 0; i < 3; i++) {
|
||||
rotations.push(rotations[i].rotated90(axis));
|
||||
}
|
||||
return rotations;
|
||||
};
|
||||
VoxelSpace.prototype.getDims = function () {
|
||||
return this.dims.slice();
|
||||
};
|
||||
VoxelSpace.prototype.getVals = function () {
|
||||
return this.vals.slice();
|
||||
};
|
||||
// [1, 0, 0] [x] [ x]
|
||||
// [0, 0, -1] * [y] = [-z]
|
||||
// [0, 1, 0] [z] [ y]
|
||||
VoxelSpace.prototype.newIndexRotX = function (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]
|
||||
VoxelSpace.prototype.newIndexRotY = function (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]
|
||||
VoxelSpace.prototype.newIndexRotZ = function (x, y, z) {
|
||||
return this.dims[0] * this.dims[2] * (this.dims[1] - 1 - y) + this.dims[2] * x + z;
|
||||
};
|
||||
VoxelSpace.prototype.at = function (x, y, z) {
|
||||
return this.vals[this.dims[1] * this.dims[2] * x + this.dims[2] * y + z];
|
||||
};
|
||||
VoxelSpace.prototype.set = function (x, y, z, val) {
|
||||
this.vals[this.dims[1] * this.dims[2] * x + this.dims[2] * y + z] = val;
|
||||
};
|
||||
VoxelSpace.prototype.rotated90 = function (dim) {
|
||||
var newVals = __spreadArrays(this.vals);
|
||||
var newDims;
|
||||
var 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(function (val, i, j, k) {
|
||||
newVals[rotIndex(i, j, k)] = val;
|
||||
});
|
||||
return new VoxelSpace(newDims, newVals);
|
||||
};
|
||||
VoxelSpace.prototype.rot90 = function (dim) {
|
||||
var rot = this.rotated90(dim);
|
||||
this.vals = rot.getVals();
|
||||
this.dims = rot.getDims();
|
||||
};
|
||||
VoxelSpace.prototype.plus = function (space, startX, startY, startZ) {
|
||||
var result = this.clone();
|
||||
var spaceDims = space.getDims();
|
||||
for (var i = 0; i < spaceDims[0]; i++) {
|
||||
for (var j = 0; j < spaceDims[1]; j++) {
|
||||
for (var k = 0; k < spaceDims[2]; k++) {
|
||||
var sourceVal = space.at(i, j, k);
|
||||
var targetEmpty = result.at(startX + i, startY + j, startZ + k) === 0;
|
||||
if (sourceVal !== 0 && targetEmpty) {
|
||||
result.set(startX + i, startY + j, startZ + k, sourceVal);
|
||||
}
|
||||
else if (sourceVal !== 0 && !targetEmpty) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
return VoxelSpace;
|
||||
}());
|
||||
exports.default = VoxelSpace;
|
||||
246
src/VoxelSpace.ts
Normal file
246
src/VoxelSpace.ts
Normal file
@@ -0,0 +1,246 @@
|
||||
import Polycube from "./Polycube";
|
||||
|
||||
export type DimensionDef = [number, number, number];
|
||||
|
||||
export default class VoxelSpace {
|
||||
protected vals: number[];
|
||||
protected dims: DimensionDef;
|
||||
constructor(dims: DimensionDef, vals: number[], cullEmpty?: boolean) {
|
||||
if (vals.length !== dims[0] * dims[1] * dims[2]) {
|
||||
throw new Error("Vals don't fit in given dimensions.");
|
||||
}
|
||||
this.dims = dims;
|
||||
this.vals = vals;
|
||||
if (cullEmpty) {
|
||||
this.cullEmptySpace();
|
||||
}
|
||||
}
|
||||
|
||||
private cullEmptySpace() {
|
||||
const extrema = {
|
||||
xMax: -Infinity,
|
||||
xMin: Infinity,
|
||||
yMax: -Infinity,
|
||||
yMin: Infinity,
|
||||
zMax: -Infinity,
|
||||
zMin: Infinity,
|
||||
};
|
||||
const newVals: number[] = [];
|
||||
this.forEachCell((val, i, j, k) => {
|
||||
if (val !== 0) {
|
||||
extrema.xMax = Math.max(extrema.xMax, i);
|
||||
extrema.xMin = Math.min(extrema.xMin, i);
|
||||
extrema.yMax = Math.max(extrema.yMax, j);
|
||||
extrema.yMin = Math.min(extrema.yMin, j);
|
||||
extrema.zMax = Math.max(extrema.zMax, k);
|
||||
extrema.zMin = Math.min(extrema.zMin, k);
|
||||
}
|
||||
});
|
||||
for (let i = extrema.xMin; i <= extrema.xMax; i++) {
|
||||
for (let j = extrema.yMin; j <= extrema.yMax; j++) {
|
||||
for (let k = extrema.zMin; k <= extrema.zMax; k++) {
|
||||
newVals.push(this.at(i, j, k));
|
||||
}
|
||||
}
|
||||
}
|
||||
this.dims[0] = extrema.xMax - extrema.xMin + 1;
|
||||
this.dims[1] = extrema.yMax - extrema.yMin + 1;
|
||||
this.dims[2] = extrema.zMax - extrema.zMin + 1;
|
||||
this.vals = newVals;
|
||||
}
|
||||
|
||||
forEachCell(cb: (val: number, x: number, y: number, z: number) => any) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
console.log(accum);
|
||||
accum = "";
|
||||
}
|
||||
if (i !== this.dims[0] - 1) {
|
||||
console.log("-");
|
||||
}
|
||||
}
|
||||
console.log("---");
|
||||
}
|
||||
|
||||
getUniqueRotations() {
|
||||
const rotations: VoxelSpace[] = [];
|
||||
const refSpace = this.clone();
|
||||
VoxelSpace.pushNewUniqueSpaces(rotations, refSpace.getAxisSpins('x'));
|
||||
refSpace.rot90('y');
|
||||
VoxelSpace.pushNewUniqueSpaces(rotations, refSpace.getAxisSpins('x'));
|
||||
refSpace.rot90('y');
|
||||
VoxelSpace.pushNewUniqueSpaces(rotations, refSpace.getAxisSpins('x'));
|
||||
refSpace.rot90('y');
|
||||
VoxelSpace.pushNewUniqueSpaces(rotations, refSpace.getAxisSpins('x'));
|
||||
refSpace.rot90('z');
|
||||
VoxelSpace.pushNewUniqueSpaces(rotations, refSpace.getAxisSpins('x'));
|
||||
refSpace.rot90('z');
|
||||
refSpace.rot90('z');
|
||||
VoxelSpace.pushNewUniqueSpaces(rotations, refSpace.getAxisSpins('x'));
|
||||
return rotations;
|
||||
}
|
||||
|
||||
static filterUnique<T extends Polycube | VoxelSpace>(spaces: T[]): T[] {
|
||||
if (spaces.length === 0) {
|
||||
return [];
|
||||
}
|
||||
const uniqueSpaces = [spaces[0]];
|
||||
for (const space of spaces) {
|
||||
let foundMatch = false;
|
||||
for (const rotation of space.getUniqueRotations()) {
|
||||
let end = uniqueSpaces.length;
|
||||
for (let i = 0; i < end; i++) {
|
||||
if (rotation.matches(uniqueSpaces[i])) {
|
||||
foundMatch = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!foundMatch) {
|
||||
uniqueSpaces.push(space);
|
||||
}
|
||||
}
|
||||
return uniqueSpaces;
|
||||
}
|
||||
|
||||
protected static pushNewUniqueSpaces(existingSpaces: VoxelSpace[], newSpaces: VoxelSpace[]) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
matches(space: VoxelSpace) {
|
||||
const otherDims = space.getDims();
|
||||
for (let i = 0; i < this.dims.length; i++) {
|
||||
if (otherDims[i] !== this.dims[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const otherVals = space.getVals();
|
||||
for (let i = 0; i < this.vals.length; i++) {
|
||||
if (this.vals[i] !== otherVals[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
clone() {
|
||||
return new VoxelSpace(this.getDims(), this.getVals());
|
||||
}
|
||||
|
||||
private getAxisSpins(axis: 'x' | 'y' | 'z'): VoxelSpace[] {
|
||||
const rotations = [this.clone()];
|
||||
for (let i = 0; i < 3; i++) {
|
||||
rotations.push(rotations[i].rotated90(axis));
|
||||
}
|
||||
return rotations;
|
||||
}
|
||||
|
||||
getDims(): DimensionDef {
|
||||
return this.dims.slice() as DimensionDef;
|
||||
}
|
||||
|
||||
getVals() {
|
||||
return this.vals.slice();
|
||||
}
|
||||
|
||||
// [1, 0, 0] [x] [ x]
|
||||
// [0, 0, -1] * [y] = [-z]
|
||||
// [0, 1, 0] [z] [ y]
|
||||
private newIndexRotX(x: number, y: number, z: number) {
|
||||
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]
|
||||
private newIndexRotY(x: number, y: number, z: number) {
|
||||
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]
|
||||
private newIndexRotZ(x: number, y: number, z: number) {
|
||||
return this.dims[0] * this.dims[2] * (this.dims[1] - 1 - y) + this.dims[2] * x + z;
|
||||
}
|
||||
|
||||
at(x: number, y: number, z: number) {
|
||||
return this.vals[this.dims[1] * this.dims[2] * x + this.dims[2] * y + z];
|
||||
}
|
||||
|
||||
set(x: number, y: number, z: number, val: number) {
|
||||
this.vals[this.dims[1] * this.dims[2] * x + this.dims[2] * y + z] = val;
|
||||
}
|
||||
|
||||
rotated90(dim: 'x' | 'y' | 'z') {
|
||||
const newVals = [...this.vals];
|
||||
let newDims: DimensionDef;
|
||||
let rotIndex: (i: number, j: number, k: number) => number;
|
||||
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) => {
|
||||
newVals[rotIndex(i, j, k)] = val;
|
||||
})
|
||||
return new VoxelSpace(newDims, newVals);
|
||||
}
|
||||
|
||||
rot90(dim: 'x' | 'y' | 'z') {
|
||||
const rot = this.rotated90(dim);
|
||||
this.vals = rot.getVals();
|
||||
this.dims = rot.getDims();
|
||||
}
|
||||
|
||||
plus(space: VoxelSpace, startX: number, startY: number, startZ: number): VoxelSpace | null {
|
||||
let result: VoxelSpace = this.clone();
|
||||
const spaceDims = space.getDims();
|
||||
for (let i = 0; i < spaceDims[0]; i++) {
|
||||
for (let j = 0; j < spaceDims[1]; j++) {
|
||||
for (let k = 0; k < spaceDims[2]; k++) {
|
||||
const sourceVal = space.at(i, j, k);
|
||||
const targetEmpty = result.at(startX + i, startY + j, startZ + k) === 0;
|
||||
if (sourceVal !== 0 && targetEmpty) {
|
||||
result.set(startX + i, startY + j, startZ + k, sourceVal);
|
||||
} else if (sourceVal !== 0 && !targetEmpty) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
139
src/main.js
Normal file
139
src/main.js
Normal file
@@ -0,0 +1,139 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var Polycube_1 = __importDefault(require("./Polycube"));
|
||||
var SomaSolver_1 = __importDefault(require("./SomaSolver"));
|
||||
// const testCube = new Cube([4, 2, 5], [
|
||||
// "000", "001", "002", "003", "004",
|
||||
// "010", "011", "012", "013", "014",
|
||||
// "100", "101", "102", "103", "104",
|
||||
// "110", "111", "112", "113", "114",
|
||||
// "200", "201", "202", "203", "204",
|
||||
// "210", "211", "212", "213", "214",
|
||||
// "300", "301", "302", "303", "304",
|
||||
// "310", "311", "312", "313", "314",
|
||||
// ]);
|
||||
// const somaCube = new Polycube([3, 3, 3], [
|
||||
// false, false, false,
|
||||
// false, false, false,
|
||||
// false, false, false,
|
||||
//
|
||||
// true, true, true,
|
||||
// true, true, true,
|
||||
// false, false, false,
|
||||
//
|
||||
// false, false, false,
|
||||
// false, false, false,
|
||||
// false, false, false,
|
||||
// ], 1);
|
||||
var unitCube1 = new Polycube_1.default([1, 1, 1], [true], 1);
|
||||
var unitCube2 = new Polycube_1.default([1, 1, 1], [true], 2);
|
||||
var unitCube3 = new Polycube_1.default([1, 1, 1], [true], 3);
|
||||
var unitCube4 = new Polycube_1.default([1, 1, 1], [true], 4);
|
||||
var unitCube5 = new Polycube_1.default([1, 1, 1], [true], 5);
|
||||
var unitCube6 = new Polycube_1.default([1, 1, 1], [true], 6);
|
||||
var unitCube7 = new Polycube_1.default([1, 1, 1], [true], 7);
|
||||
var unitCube8 = new Polycube_1.default([1, 1, 1], [true], 8);
|
||||
var unitCube9 = new Polycube_1.default([1, 1, 1], [true], 9);
|
||||
var unitCube10 = new Polycube_1.default([1, 1, 1], [true], 10);
|
||||
var unitCube11 = new Polycube_1.default([1, 1, 1], [true], 11);
|
||||
var unitCube12 = new Polycube_1.default([1, 1, 1], [true], 12);
|
||||
var unitCube13 = new Polycube_1.default([1, 1, 1], [true], 13);
|
||||
var unitCube14 = new Polycube_1.default([1, 1, 1], [true], 14);
|
||||
var unitCube15 = new Polycube_1.default([1, 1, 1], [true], 15);
|
||||
var unitCube16 = new Polycube_1.default([1, 1, 1], [true], 16);
|
||||
var unitCube17 = new Polycube_1.default([1, 1, 1], [true], 17);
|
||||
var unitCube18 = new Polycube_1.default([1, 1, 1], [true], 18);
|
||||
var unitCube19 = new Polycube_1.default([1, 1, 1], [true], 19);
|
||||
var unitCube20 = new Polycube_1.default([1, 1, 1], [true], 20);
|
||||
var unitCube21 = new Polycube_1.default([1, 1, 1], [true], 21);
|
||||
var unitCube22 = new Polycube_1.default([1, 1, 1], [true], 22);
|
||||
var unitCube23 = new Polycube_1.default([1, 1, 1], [true], 23);
|
||||
var unitCube24 = new Polycube_1.default([1, 1, 1], [true], 24);
|
||||
var unitCube25 = new Polycube_1.default([1, 1, 1], [true], 25);
|
||||
var unitCube26 = new Polycube_1.default([1, 1, 1], [true], 26);
|
||||
var unitCube27 = new Polycube_1.default([1, 1, 1], [true], 27);
|
||||
var tetromino1 = new Polycube_1.default([3, 3, 3], [
|
||||
true, true, true,
|
||||
false, true, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
], 1);
|
||||
var tetromino2 = new Polycube_1.default([3, 3, 3], [
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, true, false,
|
||||
false, true, false,
|
||||
false, true, false,
|
||||
false, true, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
], 2);
|
||||
var tetromino3 = new Polycube_1.default([3, 3, 3], [
|
||||
true, false, false,
|
||||
true, true, false,
|
||||
false, true, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
], 3);
|
||||
var tetromino4 = new Polycube_1.default([3, 3, 3], [
|
||||
true, true, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
true, false, false,
|
||||
true, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
], 4);
|
||||
var tetromino5 = new Polycube_1.default([3, 3, 3], [
|
||||
true, true, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, true, false,
|
||||
false, true, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
], 5);
|
||||
var tetromino6 = new Polycube_1.default([3, 3, 3], [
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, true, false,
|
||||
false, false, false,
|
||||
false, true, false,
|
||||
false, true, true,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
], 6);
|
||||
var triomino1 = new Polycube_1.default([3, 3, 3], [
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, true, false,
|
||||
false, false, false,
|
||||
false, true, false,
|
||||
false, true, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
], 7);
|
||||
// const cube = new VoxelSpace([3, 3, 3], Array(3**3).fill(0));
|
||||
// cube.plus(triomino1)?.plus(tetromino2, {x: 1, y: 0, z: 1})?.print();
|
||||
var solver = new SomaSolver_1.default(3);
|
||||
console.log("solving");
|
||||
solver.solve([triomino1, tetromino2, tetromino3, tetromino1, tetromino4, tetromino5, tetromino6]);
|
||||
158
src/main.ts
Normal file
158
src/main.ts
Normal file
@@ -0,0 +1,158 @@
|
||||
import Polycube from "./Polycube";
|
||||
import SomaSolver from "./SomaSolver";
|
||||
|
||||
// const testCube = new Cube([4, 2, 5], [
|
||||
// "000", "001", "002", "003", "004",
|
||||
// "010", "011", "012", "013", "014",
|
||||
// "100", "101", "102", "103", "104",
|
||||
// "110", "111", "112", "113", "114",
|
||||
// "200", "201", "202", "203", "204",
|
||||
// "210", "211", "212", "213", "214",
|
||||
// "300", "301", "302", "303", "304",
|
||||
// "310", "311", "312", "313", "314",
|
||||
// ]);
|
||||
// const somaCube = new Polycube([3, 3, 3], [
|
||||
// false, false, false,
|
||||
// false, false, false,
|
||||
// false, false, false,
|
||||
//
|
||||
// true, true, true,
|
||||
// true, true, true,
|
||||
// false, false, false,
|
||||
//
|
||||
// false, false, false,
|
||||
// false, false, false,
|
||||
// false, false, false,
|
||||
// ], 1);
|
||||
const unitCube1 = new Polycube([1, 1, 1], [true], 1);
|
||||
const unitCube2 = new Polycube([1, 1, 1], [true], 2);
|
||||
const unitCube3 = new Polycube([1, 1, 1], [true], 3);
|
||||
const unitCube4 = new Polycube([1, 1, 1], [true], 4);
|
||||
const unitCube5 = new Polycube([1, 1, 1], [true], 5);
|
||||
const unitCube6 = new Polycube([1, 1, 1], [true], 6);
|
||||
const unitCube7 = new Polycube([1, 1, 1], [true], 7);
|
||||
const unitCube8 = new Polycube([1, 1, 1], [true], 8);
|
||||
const unitCube9 = new Polycube([1, 1, 1], [true], 9);
|
||||
const unitCube10 = new Polycube([1, 1, 1], [true], 10);
|
||||
const unitCube11 = new Polycube([1, 1, 1], [true], 11);
|
||||
const unitCube12 = new Polycube([1, 1, 1], [true], 12);
|
||||
const unitCube13 = new Polycube([1, 1, 1], [true], 13);
|
||||
const unitCube14 = new Polycube([1, 1, 1], [true], 14);
|
||||
const unitCube15 = new Polycube([1, 1, 1], [true], 15);
|
||||
const unitCube16 = new Polycube([1, 1, 1], [true], 16);
|
||||
const unitCube17 = new Polycube([1, 1, 1], [true], 17);
|
||||
const unitCube18 = new Polycube([1, 1, 1], [true], 18);
|
||||
const unitCube19 = new Polycube([1, 1, 1], [true], 19);
|
||||
const unitCube20 = new Polycube([1, 1, 1], [true], 20);
|
||||
const unitCube21 = new Polycube([1, 1, 1], [true], 21);
|
||||
const unitCube22 = new Polycube([1, 1, 1], [true], 22);
|
||||
const unitCube23 = new Polycube([1, 1, 1], [true], 23);
|
||||
const unitCube24 = new Polycube([1, 1, 1], [true], 24);
|
||||
const unitCube25 = new Polycube([1, 1, 1], [true], 25);
|
||||
const unitCube26 = new Polycube([1, 1, 1], [true], 26);
|
||||
const unitCube27 = new Polycube([1, 1, 1], [true], 27);
|
||||
|
||||
const tetromino1 = new Polycube([3, 3, 3], [
|
||||
true, true, true,
|
||||
false, true, false,
|
||||
false, false, false,
|
||||
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
], 1);
|
||||
|
||||
const tetromino2 = new Polycube([3, 3, 3], [
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, true, false,
|
||||
|
||||
false, true, false,
|
||||
false, true, false,
|
||||
false, true, false,
|
||||
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
], 2);
|
||||
|
||||
const tetromino3 = new Polycube([3, 3, 3], [
|
||||
true, false, false,
|
||||
true, true, false,
|
||||
false, true, false,
|
||||
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
], 3);
|
||||
|
||||
const tetromino4 = new Polycube([3, 3, 3], [
|
||||
true, true, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
|
||||
true, false, false,
|
||||
true, false, false,
|
||||
false, false, false,
|
||||
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
], 4);
|
||||
|
||||
const tetromino5 = new Polycube([3, 3, 3], [
|
||||
true, true, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
|
||||
false, true, false,
|
||||
false, true, false,
|
||||
false, false, false,
|
||||
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
], 5);
|
||||
|
||||
const tetromino6 = new Polycube([3, 3, 3], [
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, true, false,
|
||||
|
||||
false, false, false,
|
||||
false, true, false,
|
||||
false, true, true,
|
||||
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
], 6);
|
||||
|
||||
const triomino1 = new Polycube([3, 3, 3], [
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, true, false,
|
||||
|
||||
false, false, false,
|
||||
false, true, false,
|
||||
false, true, false,
|
||||
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
], 7);
|
||||
|
||||
// const cube = new VoxelSpace([3, 3, 3], Array(3**3).fill(0));
|
||||
// cube.plus(triomino1)?.plus(tetromino2, {x: 1, y: 0, z: 1})?.print();
|
||||
|
||||
const solver = new SomaSolver(3);
|
||||
console.log("solving");
|
||||
solver.solve([triomino1, tetromino2, tetromino3, tetromino1, tetromino4, tetromino5, tetromino6]);
|
||||
13
src/test.html
Normal file
13
src/test.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Title</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script type="application/javascript" src="test.js"></script>
|
||||
<a onclick="start();">Start</a>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
499
src/test.js
Normal file
499
src/test.js
Normal file
@@ -0,0 +1,499 @@
|
||||
"use strict";
|
||||
var __extends = (this && this.__extends) || (function () {
|
||||
var extendStatics = function (d, b) {
|
||||
extendStatics = Object.setPrototypeOf ||
|
||||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
||||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
|
||||
return extendStatics(d, b);
|
||||
};
|
||||
return function (d, b) {
|
||||
extendStatics(d, b);
|
||||
function __() { this.constructor = d; }
|
||||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
||||
};
|
||||
})();
|
||||
var __spreadArrays = (this && this.__spreadArrays) || function () {
|
||||
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
|
||||
for (var r = Array(s), k = 0, i = 0; i < il; i++)
|
||||
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
|
||||
r[k] = a[j];
|
||||
return r;
|
||||
};
|
||||
var SomaSolver = /** @class */ (function () {
|
||||
function SomaSolver(dimension) {
|
||||
this.solutions = [];
|
||||
this.iterations = 0;
|
||||
if (dimension % 1 !== 0 || dimension < 0) {
|
||||
throw new Error("The argument 'dimension' must be a positive whole number");
|
||||
}
|
||||
this.dim = dimension;
|
||||
this.solutionCube = new VoxelSpace([dimension, dimension, dimension], Array(Math.pow(dimension, 3)).fill(0));
|
||||
}
|
||||
SomaSolver.prototype.solve = function (polycubes) {
|
||||
if (polycubes.length === 0) {
|
||||
throw new Error("You must pass at least one polycube to solve the puzzle.");
|
||||
}
|
||||
var cumulativeSize = polycubes.reduce(function (prev, curr) { return prev + curr.size(); }, 0);
|
||||
if (cumulativeSize !== Math.pow(this.dim, 3)) {
|
||||
throw new Error("The polycubes passed do not add up to exactly enough units to form a cube of dimension " + this.dim + "! Got: " + cumulativeSize + ", need: " + Math.pow(this.dim, 3));
|
||||
}
|
||||
this.iterations = 0;
|
||||
this.backtrackSolve(this.solutionCube, polycubes);
|
||||
this.solutions = VoxelSpace.filterUnique(this.solutions);
|
||||
this.solutions.forEach(function (sol) { return sol.print(); });
|
||||
console.log(this.solutions.length);
|
||||
};
|
||||
SomaSolver.prototype.backtrackSolve = function (workingSolution, polycubes, depth) {
|
||||
if (depth === void 0) { depth = 0; }
|
||||
var nextCube = polycubes[0];
|
||||
var rots = depth === 0 ? [nextCube] : nextCube.getUniqueRotations();
|
||||
for (var i = 0; i < rots.length; i++) {
|
||||
var polyCubeDims = rots[i].getDims();
|
||||
for (var x = 0; x < this.dim - polyCubeDims[0] + 1; x++) {
|
||||
for (var y = 0; y < this.dim - polyCubeDims[1] + 1; y++) {
|
||||
for (var z = 0; z < this.dim - polyCubeDims[2] + 1; z++) {
|
||||
var successfulFusion = workingSolution.plus(rots[i], x, y, z);
|
||||
if (successfulFusion) {
|
||||
if (polycubes.length === 1) {
|
||||
console.log("soln", this.iterations++);
|
||||
this.solutions.push(successfulFusion);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
this.backtrackSolve(successfulFusion, polycubes.slice(1), depth + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
return SomaSolver;
|
||||
}());
|
||||
var VoxelSpace = /** @class */ (function () {
|
||||
function VoxelSpace(dims, vals, cullEmpty) {
|
||||
if (vals.length !== dims[0] * dims[1] * dims[2]) {
|
||||
throw new Error("Vals don't fit in given dimensions.");
|
||||
}
|
||||
this.dims = dims;
|
||||
this.vals = vals;
|
||||
if (cullEmpty) {
|
||||
this.cullEmptySpace();
|
||||
}
|
||||
}
|
||||
VoxelSpace.prototype.cullEmptySpace = function () {
|
||||
var extrema = {
|
||||
xMax: -Infinity,
|
||||
xMin: Infinity,
|
||||
yMax: -Infinity,
|
||||
yMin: Infinity,
|
||||
zMax: -Infinity,
|
||||
zMin: Infinity,
|
||||
};
|
||||
var newVals = [];
|
||||
this.forEachCell(function (val, i, j, k) {
|
||||
if (val !== 0) {
|
||||
extrema.xMax = Math.max(extrema.xMax, i);
|
||||
extrema.xMin = Math.min(extrema.xMin, i);
|
||||
extrema.yMax = Math.max(extrema.yMax, j);
|
||||
extrema.yMin = Math.min(extrema.yMin, j);
|
||||
extrema.zMax = Math.max(extrema.zMax, k);
|
||||
extrema.zMin = Math.min(extrema.zMin, k);
|
||||
}
|
||||
});
|
||||
for (var i = extrema.xMin; i <= extrema.xMax; i++) {
|
||||
for (var j = extrema.yMin; j <= extrema.yMax; j++) {
|
||||
for (var k = extrema.zMin; k <= extrema.zMax; k++) {
|
||||
newVals.push(this.at(i, j, k));
|
||||
}
|
||||
}
|
||||
}
|
||||
this.dims[0] = extrema.xMax - extrema.xMin + 1;
|
||||
this.dims[1] = extrema.yMax - extrema.yMin + 1;
|
||||
this.dims[2] = extrema.zMax - extrema.zMin + 1;
|
||||
this.vals = newVals;
|
||||
};
|
||||
VoxelSpace.prototype.forEachCell = function (cb) {
|
||||
loopStart: for (var x = 0; x < this.dims[0]; x++) {
|
||||
for (var y = 0; y < this.dims[1]; y++) {
|
||||
for (var z = 0; z < this.dims[2]; z++) {
|
||||
if (cb(this.at(x, y, z), x, y, z) === 0) {
|
||||
break loopStart;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
VoxelSpace.prototype.print = function () {
|
||||
var accum = "";
|
||||
console.log("---");
|
||||
for (var i = 0; i < this.dims[0]; i++) {
|
||||
for (var j = 0; j < this.dims[1]; j++) {
|
||||
for (var k = 0; k < this.dims[2]; k++) {
|
||||
accum += this.at(i, j, k);
|
||||
}
|
||||
console.log(accum);
|
||||
accum = "";
|
||||
}
|
||||
if (i !== this.dims[0] - 1) {
|
||||
console.log("-");
|
||||
}
|
||||
}
|
||||
console.log("---");
|
||||
};
|
||||
VoxelSpace.prototype.getUniqueRotations = function () {
|
||||
var rotations = [];
|
||||
var refSpace = this.clone();
|
||||
VoxelSpace.pushNewUniqueSpaces(rotations, refSpace.getAxisSpins('x'));
|
||||
refSpace.rot90('y');
|
||||
VoxelSpace.pushNewUniqueSpaces(rotations, refSpace.getAxisSpins('x'));
|
||||
refSpace.rot90('y');
|
||||
VoxelSpace.pushNewUniqueSpaces(rotations, refSpace.getAxisSpins('x'));
|
||||
refSpace.rot90('y');
|
||||
VoxelSpace.pushNewUniqueSpaces(rotations, refSpace.getAxisSpins('x'));
|
||||
refSpace.rot90('z');
|
||||
VoxelSpace.pushNewUniqueSpaces(rotations, refSpace.getAxisSpins('x'));
|
||||
refSpace.rot90('z');
|
||||
refSpace.rot90('z');
|
||||
VoxelSpace.pushNewUniqueSpaces(rotations, refSpace.getAxisSpins('x'));
|
||||
return rotations;
|
||||
};
|
||||
VoxelSpace.filterUnique = function (spaces) {
|
||||
if (spaces.length === 0) {
|
||||
return [];
|
||||
}
|
||||
var uniqueSpaces = [spaces[0]];
|
||||
for (var _i = 0, spaces_1 = spaces; _i < spaces_1.length; _i++) {
|
||||
var space = spaces_1[_i];
|
||||
var foundMatch = false;
|
||||
for (var _a = 0, _b = space.getUniqueRotations(); _a < _b.length; _a++) {
|
||||
var rotation = _b[_a];
|
||||
var end = uniqueSpaces.length;
|
||||
for (var i = 0; i < end; i++) {
|
||||
if (rotation.matches(uniqueSpaces[i])) {
|
||||
foundMatch = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!foundMatch) {
|
||||
uniqueSpaces.push(space);
|
||||
}
|
||||
}
|
||||
return uniqueSpaces;
|
||||
};
|
||||
VoxelSpace.pushNewUniqueSpaces = function (existingSpaces, newSpaces) {
|
||||
for (var _i = 0, newSpaces_1 = newSpaces; _i < newSpaces_1.length; _i++) {
|
||||
var newSpace = newSpaces_1[_i];
|
||||
var matchFound = false;
|
||||
for (var _a = 0, existingSpaces_1 = existingSpaces; _a < existingSpaces_1.length; _a++) {
|
||||
var existingSpace = existingSpaces_1[_a];
|
||||
if (newSpace.matches(existingSpace)) {
|
||||
matchFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!matchFound) {
|
||||
existingSpaces.push(newSpace);
|
||||
}
|
||||
}
|
||||
};
|
||||
VoxelSpace.prototype.matches = function (space) {
|
||||
var otherDims = space.getDims();
|
||||
for (var i = 0; i < this.dims.length; i++) {
|
||||
if (otherDims[i] !== this.dims[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
var otherVals = space.getVals();
|
||||
for (var i = 0; i < this.vals.length; i++) {
|
||||
if (this.vals[i] !== otherVals[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
VoxelSpace.prototype.clone = function () {
|
||||
return new VoxelSpace(this.getDims(), this.getVals());
|
||||
};
|
||||
VoxelSpace.prototype.getAxisSpins = function (axis) {
|
||||
var rotations = [this.clone()];
|
||||
for (var i = 0; i < 3; i++) {
|
||||
rotations.push(rotations[i].rotated90(axis));
|
||||
}
|
||||
return rotations;
|
||||
};
|
||||
VoxelSpace.prototype.getDims = function () {
|
||||
return this.dims.slice();
|
||||
};
|
||||
VoxelSpace.prototype.getVals = function () {
|
||||
return this.vals.slice();
|
||||
};
|
||||
// [1, 0, 0] [x] [ x]
|
||||
// [0, 0, -1] * [y] = [-z]
|
||||
// [0, 1, 0] [z] [ y]
|
||||
VoxelSpace.prototype.newIndexRotX = function (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]
|
||||
VoxelSpace.prototype.newIndexRotY = function (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]
|
||||
VoxelSpace.prototype.newIndexRotZ = function (x, y, z) {
|
||||
return this.dims[0] * this.dims[2] * (this.dims[1] - 1 - y) + this.dims[2] * x + z;
|
||||
};
|
||||
VoxelSpace.prototype.at = function (x, y, z) {
|
||||
return this.vals[this.dims[1] * this.dims[2] * x + this.dims[2] * y + z];
|
||||
};
|
||||
VoxelSpace.prototype.set = function (x, y, z, val) {
|
||||
this.vals[this.dims[1] * this.dims[2] * x + this.dims[2] * y + z] = val;
|
||||
};
|
||||
VoxelSpace.prototype.rotated90 = function (dim) {
|
||||
var newVals = __spreadArrays(this.vals);
|
||||
var newDims;
|
||||
var 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(function (val, i, j, k) {
|
||||
newVals[rotIndex(i, j, k)] = val;
|
||||
});
|
||||
return new VoxelSpace(newDims, newVals);
|
||||
};
|
||||
VoxelSpace.prototype.rot90 = function (dim) {
|
||||
var rot = this.rotated90(dim);
|
||||
this.vals = rot.getVals();
|
||||
this.dims = rot.getDims();
|
||||
};
|
||||
VoxelSpace.prototype.plus = function (space, startX, startY, startZ) {
|
||||
var result = this.clone();
|
||||
var spaceDims = space.getDims();
|
||||
for (var i = 0; i < spaceDims[0]; i++) {
|
||||
for (var j = 0; j < spaceDims[1]; j++) {
|
||||
for (var k = 0; k < spaceDims[2]; k++) {
|
||||
var sourceVal = space.at(i, j, k);
|
||||
var targetEmpty = result.at(startX + i, startY + j, startZ + k) === 0;
|
||||
if (sourceVal !== 0 && targetEmpty) {
|
||||
result.set(startX + i, startY + j, startZ + k, sourceVal);
|
||||
}
|
||||
else if (sourceVal !== 0 && !targetEmpty) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
return VoxelSpace;
|
||||
}());
|
||||
var Polycube = /** @class */ (function (_super) {
|
||||
__extends(Polycube, _super);
|
||||
function Polycube(dims, vals, id) {
|
||||
var _this = _super.call(this, dims, vals.map(function (val) { return val ? id : 0; }), true) || this;
|
||||
_this.id = id;
|
||||
return _this;
|
||||
}
|
||||
Polycube.prototype.getId = function () {
|
||||
return this.id;
|
||||
};
|
||||
Polycube.prototype.print = function () {
|
||||
var accum = "";
|
||||
console.log("---");
|
||||
for (var i = 0; i < this.dims[0]; i++) {
|
||||
for (var j = 0; j < this.dims[1]; j++) {
|
||||
for (var k = 0; k < this.dims[2]; k++) {
|
||||
accum += this.at(i, j, k) === 0 ? "O" : "#";
|
||||
}
|
||||
console.log(accum);
|
||||
accum = "";
|
||||
}
|
||||
if (i !== this.dims[0] - 1) {
|
||||
console.log("-");
|
||||
}
|
||||
}
|
||||
console.log("---");
|
||||
};
|
||||
Polycube.prototype.matches = function (cube) {
|
||||
var otherDims = cube.getDims();
|
||||
for (var i = 0; i < this.dims.length; i++) {
|
||||
if (otherDims[i] !== this.dims[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
var otherVals = cube.getVals();
|
||||
for (var i = 0; i < this.vals.length; i++) {
|
||||
if (Number(this.vals[i]) !== Number(otherVals[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
Polycube.prototype.size = function () {
|
||||
var size = 0;
|
||||
this.forEachCell(function (val) {
|
||||
if (val) {
|
||||
size++;
|
||||
}
|
||||
});
|
||||
return size;
|
||||
};
|
||||
Polycube.prototype.rotated90 = function (dim) {
|
||||
var rotated = _super.prototype.rotated90.call(this, dim);
|
||||
return new Polycube(rotated.getDims(), rotated.getVals(), this.id);
|
||||
};
|
||||
Polycube.prototype.clone = function () {
|
||||
return new Polycube(this.getDims(), this.getVals(), this.id);
|
||||
};
|
||||
Polycube.prototype.getUniqueRotations = function () {
|
||||
var _this = this;
|
||||
return _super.prototype.getUniqueRotations.call(this).map(function (rot) { return new Polycube(rot.getDims(), rot.getVals(), _this.id); });
|
||||
};
|
||||
return Polycube;
|
||||
}(VoxelSpace));
|
||||
// const testCube = new Cube([4, 2, 5], [
|
||||
// "000", "001", "002", "003", "004",
|
||||
// "010", "011", "012", "013", "014",
|
||||
// "100", "101", "102", "103", "104",
|
||||
// "110", "111", "112", "113", "114",
|
||||
// "200", "201", "202", "203", "204",
|
||||
// "210", "211", "212", "213", "214",
|
||||
// "300", "301", "302", "303", "304",
|
||||
// "310", "311", "312", "313", "314",
|
||||
// ]);
|
||||
// const somaCube = new Polycube([3, 3, 3], [
|
||||
// false, false, false,
|
||||
// false, false, false,
|
||||
// false, false, false,
|
||||
//
|
||||
// true, true, true,
|
||||
// true, true, true,
|
||||
// false, false, false,
|
||||
//
|
||||
// false, false, false,
|
||||
// false, false, false,
|
||||
// false, false, false,
|
||||
// ], 1);
|
||||
var unitCube1 = new Polycube([1, 1, 1], [true], 1);
|
||||
var unitCube2 = new Polycube([1, 1, 1], [true], 2);
|
||||
var unitCube3 = new Polycube([1, 1, 1], [true], 3);
|
||||
var unitCube4 = new Polycube([1, 1, 1], [true], 4);
|
||||
var unitCube5 = new Polycube([1, 1, 1], [true], 5);
|
||||
var unitCube6 = new Polycube([1, 1, 1], [true], 6);
|
||||
var unitCube7 = new Polycube([1, 1, 1], [true], 7);
|
||||
var unitCube8 = new Polycube([1, 1, 1], [true], 8);
|
||||
var unitCube9 = new Polycube([1, 1, 1], [true], 9);
|
||||
var unitCube10 = new Polycube([1, 1, 1], [true], 10);
|
||||
var unitCube11 = new Polycube([1, 1, 1], [true], 11);
|
||||
var unitCube12 = new Polycube([1, 1, 1], [true], 12);
|
||||
var unitCube13 = new Polycube([1, 1, 1], [true], 13);
|
||||
var unitCube14 = new Polycube([1, 1, 1], [true], 14);
|
||||
var unitCube15 = new Polycube([1, 1, 1], [true], 15);
|
||||
var unitCube16 = new Polycube([1, 1, 1], [true], 16);
|
||||
var unitCube17 = new Polycube([1, 1, 1], [true], 17);
|
||||
var unitCube18 = new Polycube([1, 1, 1], [true], 18);
|
||||
var unitCube19 = new Polycube([1, 1, 1], [true], 19);
|
||||
var unitCube20 = new Polycube([1, 1, 1], [true], 20);
|
||||
var unitCube21 = new Polycube([1, 1, 1], [true], 21);
|
||||
var unitCube22 = new Polycube([1, 1, 1], [true], 22);
|
||||
var unitCube23 = new Polycube([1, 1, 1], [true], 23);
|
||||
var unitCube24 = new Polycube([1, 1, 1], [true], 24);
|
||||
var unitCube25 = new Polycube([1, 1, 1], [true], 25);
|
||||
var unitCube26 = new Polycube([1, 1, 1], [true], 26);
|
||||
var unitCube27 = new Polycube([1, 1, 1], [true], 27);
|
||||
var tetromino1 = new Polycube([3, 3, 3], [
|
||||
true, true, true,
|
||||
false, true, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
], 1);
|
||||
var tetromino2 = new Polycube([3, 3, 3], [
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, true, false,
|
||||
false, true, false,
|
||||
false, true, false,
|
||||
false, true, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
], 2);
|
||||
var tetromino3 = new Polycube([3, 3, 3], [
|
||||
true, false, false,
|
||||
true, true, false,
|
||||
false, true, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
], 3);
|
||||
var tetromino4 = new Polycube([3, 3, 3], [
|
||||
true, true, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
true, false, false,
|
||||
true, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
], 4);
|
||||
var tetromino5 = new Polycube([3, 3, 3], [
|
||||
true, true, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, true, false,
|
||||
false, true, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
], 5);
|
||||
var tetromino6 = new Polycube([3, 3, 3], [
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, true, false,
|
||||
false, false, false,
|
||||
false, true, false,
|
||||
false, true, true,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
], 6);
|
||||
var triomino1 = new Polycube([3, 3, 3], [
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, true, false,
|
||||
false, false, false,
|
||||
false, true, false,
|
||||
false, true, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
], 7);
|
||||
// const cube = new VoxelSpace([3, 3, 3], Array(3**3).fill(0));
|
||||
// cube.plus(triomino1)?.plus(tetromino2, {x: 1, y: 0, z: 1})?.print();
|
||||
function start() {
|
||||
var solver = new SomaSolver(3);
|
||||
console.log("solving");
|
||||
solver.solve([triomino1, tetromino2, tetromino3, tetromino1, tetromino4, tetromino5, tetromino6]);
|
||||
}
|
||||
524
src/test.ts
Normal file
524
src/test.ts
Normal file
@@ -0,0 +1,524 @@
|
||||
class SomaSolver {
|
||||
private solutionCube: VoxelSpace;
|
||||
private dim: number;
|
||||
private solutions: VoxelSpace[] = [];
|
||||
private iterations: number = 0;
|
||||
constructor(dimension: number) {
|
||||
if (dimension % 1 !== 0 || dimension < 0) {
|
||||
throw new Error("The argument 'dimension' must be a positive whole number");
|
||||
}
|
||||
this.dim = dimension;
|
||||
this.solutionCube = new VoxelSpace([dimension, dimension, dimension], Array(dimension**3).fill(0));
|
||||
}
|
||||
|
||||
solve(polycubes: Polycube[]) {
|
||||
if (polycubes.length === 0) {
|
||||
throw new Error("You must pass at least one polycube to solve the puzzle.");
|
||||
}
|
||||
let cumulativeSize = polycubes.reduce((prev, curr) => prev + curr.size(), 0);
|
||||
if (cumulativeSize !== this.dim**3) {
|
||||
throw new Error(`The polycubes passed do not add up to exactly enough units to form a cube of dimension ${this.dim}! Got: ${cumulativeSize}, need: ${this.dim**3}`);
|
||||
}
|
||||
this.iterations = 0;
|
||||
this.backtrackSolve(this.solutionCube, polycubes);
|
||||
this.solutions = VoxelSpace.filterUnique(this.solutions);
|
||||
this.solutions.forEach(sol => sol.print());
|
||||
console.log(this.solutions.length);
|
||||
}
|
||||
|
||||
private backtrackSolve(workingSolution: VoxelSpace, polycubes: Polycube[], depth = 0) {
|
||||
const nextCube = polycubes[0];
|
||||
const rots = depth === 0 ? [nextCube] : nextCube.getUniqueRotations();
|
||||
for (let i = 0; i < rots.length; i++) {
|
||||
const polyCubeDims = rots[i].getDims();
|
||||
for (let x = 0; x < this.dim - polyCubeDims[0] + 1; x++) {
|
||||
for (let y = 0; y < this.dim - polyCubeDims[1] + 1; y++) {
|
||||
for (let z = 0; z < this.dim - polyCubeDims[2] + 1; z++) {
|
||||
const successfulFusion = workingSolution.plus(rots[i], x, y, z);
|
||||
if (successfulFusion) {
|
||||
if (polycubes.length === 1) {
|
||||
console.log("soln", this.iterations++);
|
||||
this.solutions.push(successfulFusion);
|
||||
return;
|
||||
} else {
|
||||
this.backtrackSolve(successfulFusion, polycubes.slice(1), depth + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class VoxelSpace {
|
||||
protected vals: number[];
|
||||
protected dims: DimensionDef;
|
||||
constructor(dims: DimensionDef, vals: number[], cullEmpty?: boolean) {
|
||||
if (vals.length !== dims[0] * dims[1] * dims[2]) {
|
||||
throw new Error("Vals don't fit in given dimensions.");
|
||||
}
|
||||
this.dims = dims;
|
||||
this.vals = vals;
|
||||
if (cullEmpty) {
|
||||
this.cullEmptySpace();
|
||||
}
|
||||
}
|
||||
|
||||
private cullEmptySpace() {
|
||||
const extrema = {
|
||||
xMax: -Infinity,
|
||||
xMin: Infinity,
|
||||
yMax: -Infinity,
|
||||
yMin: Infinity,
|
||||
zMax: -Infinity,
|
||||
zMin: Infinity,
|
||||
};
|
||||
const newVals: number[] = [];
|
||||
this.forEachCell((val, i, j, k) => {
|
||||
if (val !== 0) {
|
||||
extrema.xMax = Math.max(extrema.xMax, i);
|
||||
extrema.xMin = Math.min(extrema.xMin, i);
|
||||
extrema.yMax = Math.max(extrema.yMax, j);
|
||||
extrema.yMin = Math.min(extrema.yMin, j);
|
||||
extrema.zMax = Math.max(extrema.zMax, k);
|
||||
extrema.zMin = Math.min(extrema.zMin, k);
|
||||
}
|
||||
});
|
||||
for (let i = extrema.xMin; i <= extrema.xMax; i++) {
|
||||
for (let j = extrema.yMin; j <= extrema.yMax; j++) {
|
||||
for (let k = extrema.zMin; k <= extrema.zMax; k++) {
|
||||
newVals.push(this.at(i, j, k));
|
||||
}
|
||||
}
|
||||
}
|
||||
this.dims[0] = extrema.xMax - extrema.xMin + 1;
|
||||
this.dims[1] = extrema.yMax - extrema.yMin + 1;
|
||||
this.dims[2] = extrema.zMax - extrema.zMin + 1;
|
||||
this.vals = newVals;
|
||||
}
|
||||
|
||||
forEachCell(cb: (val: number, x: number, y: number, z: number) => any) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
console.log(accum);
|
||||
accum = "";
|
||||
}
|
||||
if (i !== this.dims[0] - 1) {
|
||||
console.log("-");
|
||||
}
|
||||
}
|
||||
console.log("---");
|
||||
}
|
||||
|
||||
getUniqueRotations() {
|
||||
const rotations: VoxelSpace[] = [];
|
||||
const refSpace = this.clone();
|
||||
VoxelSpace.pushNewUniqueSpaces(rotations, refSpace.getAxisSpins('x'));
|
||||
refSpace.rot90('y');
|
||||
VoxelSpace.pushNewUniqueSpaces(rotations, refSpace.getAxisSpins('x'));
|
||||
refSpace.rot90('y');
|
||||
VoxelSpace.pushNewUniqueSpaces(rotations, refSpace.getAxisSpins('x'));
|
||||
refSpace.rot90('y');
|
||||
VoxelSpace.pushNewUniqueSpaces(rotations, refSpace.getAxisSpins('x'));
|
||||
refSpace.rot90('z');
|
||||
VoxelSpace.pushNewUniqueSpaces(rotations, refSpace.getAxisSpins('x'));
|
||||
refSpace.rot90('z');
|
||||
refSpace.rot90('z');
|
||||
VoxelSpace.pushNewUniqueSpaces(rotations, refSpace.getAxisSpins('x'));
|
||||
return rotations;
|
||||
}
|
||||
|
||||
static filterUnique<T extends Polycube | VoxelSpace>(spaces: T[]): T[] {
|
||||
if (spaces.length === 0) {
|
||||
return [];
|
||||
}
|
||||
const uniqueSpaces = [spaces[0]];
|
||||
for (const space of spaces) {
|
||||
let foundMatch = false;
|
||||
for (const rotation of space.getUniqueRotations()) {
|
||||
let end = uniqueSpaces.length;
|
||||
for (let i = 0; i < end; i++) {
|
||||
if (rotation.matches(uniqueSpaces[i])) {
|
||||
foundMatch = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!foundMatch) {
|
||||
uniqueSpaces.push(space);
|
||||
}
|
||||
}
|
||||
return uniqueSpaces;
|
||||
}
|
||||
|
||||
protected static pushNewUniqueSpaces(existingSpaces: VoxelSpace[], newSpaces: VoxelSpace[]) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
matches(space: VoxelSpace) {
|
||||
const otherDims = space.getDims();
|
||||
for (let i = 0; i < this.dims.length; i++) {
|
||||
if (otherDims[i] !== this.dims[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const otherVals = space.getVals();
|
||||
for (let i = 0; i < this.vals.length; i++) {
|
||||
if (this.vals[i] !== otherVals[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
clone() {
|
||||
return new VoxelSpace(this.getDims(), this.getVals());
|
||||
}
|
||||
|
||||
private getAxisSpins(axis: 'x' | 'y' | 'z'): VoxelSpace[] {
|
||||
const rotations = [this.clone()];
|
||||
for (let i = 0; i < 3; i++) {
|
||||
rotations.push(rotations[i].rotated90(axis));
|
||||
}
|
||||
return rotations;
|
||||
}
|
||||
|
||||
getDims(): DimensionDef {
|
||||
return this.dims.slice() as DimensionDef;
|
||||
}
|
||||
|
||||
getVals() {
|
||||
return this.vals.slice();
|
||||
}
|
||||
|
||||
// [1, 0, 0] [x] [ x]
|
||||
// [0, 0, -1] * [y] = [-z]
|
||||
// [0, 1, 0] [z] [ y]
|
||||
private newIndexRotX(x: number, y: number, z: number) {
|
||||
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]
|
||||
private newIndexRotY(x: number, y: number, z: number) {
|
||||
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]
|
||||
private newIndexRotZ(x: number, y: number, z: number) {
|
||||
return this.dims[0] * this.dims[2] * (this.dims[1] - 1 - y) + this.dims[2] * x + z;
|
||||
}
|
||||
|
||||
at(x: number, y: number, z: number) {
|
||||
return this.vals[this.dims[1] * this.dims[2] * x + this.dims[2] * y + z];
|
||||
}
|
||||
|
||||
set(x: number, y: number, z: number, val: number) {
|
||||
this.vals[this.dims[1] * this.dims[2] * x + this.dims[2] * y + z] = val;
|
||||
}
|
||||
|
||||
rotated90(dim: 'x' | 'y' | 'z') {
|
||||
const newVals = [...this.vals];
|
||||
let newDims: DimensionDef;
|
||||
let rotIndex: (i: number, j: number, k: number) => number;
|
||||
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) => {
|
||||
newVals[rotIndex(i, j, k)] = val;
|
||||
})
|
||||
return new VoxelSpace(newDims, newVals);
|
||||
}
|
||||
|
||||
rot90(dim: 'x' | 'y' | 'z') {
|
||||
const rot = this.rotated90(dim);
|
||||
this.vals = rot.getVals();
|
||||
this.dims = rot.getDims();
|
||||
}
|
||||
|
||||
plus(space: VoxelSpace, startX: number, startY: number, startZ: number): VoxelSpace | null {
|
||||
let result: VoxelSpace = this.clone();
|
||||
const spaceDims = space.getDims();
|
||||
for (let i = 0; i < spaceDims[0]; i++) {
|
||||
for (let j = 0; j < spaceDims[1]; j++) {
|
||||
for (let k = 0; k < spaceDims[2]; k++) {
|
||||
const sourceVal = space.at(i, j, k);
|
||||
const targetEmpty = result.at(startX + i, startY + j, startZ + k) === 0;
|
||||
if (sourceVal !== 0 && targetEmpty) {
|
||||
result.set(startX + i, startY + j, startZ + k, sourceVal);
|
||||
} else if (sourceVal !== 0 && !targetEmpty) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
class Polycube extends VoxelSpace {
|
||||
private id: number;
|
||||
constructor(dims: DimensionDef, vals: boolean[], id: number) {
|
||||
super(dims, vals.map(val => val ? id : 0), true);
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
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) === 0 ? "O" : "#";
|
||||
}
|
||||
console.log(accum);
|
||||
accum = "";
|
||||
}
|
||||
if (i !== this.dims[0] - 1) {
|
||||
console.log("-");
|
||||
}
|
||||
}
|
||||
console.log("---");
|
||||
}
|
||||
|
||||
matches(cube: VoxelSpace) {
|
||||
const otherDims = cube.getDims();
|
||||
for (let i = 0; i < this.dims.length; i++) {
|
||||
if (otherDims[i] !== this.dims[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const otherVals = cube.getVals();
|
||||
for (let i = 0; i < this.vals.length; i++) {
|
||||
if (Number(this.vals[i]) !== Number(otherVals[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
size() {
|
||||
let size = 0;
|
||||
this.forEachCell((val) => {
|
||||
if (val) {
|
||||
size++;
|
||||
}
|
||||
});
|
||||
return size;
|
||||
}
|
||||
|
||||
rotated90(dim: "x" | "y" | "z"): Polycube {
|
||||
const rotated = super.rotated90(dim);
|
||||
return new Polycube(rotated.getDims(), rotated.getVals() as unknown as boolean[], this.id);
|
||||
}
|
||||
|
||||
clone(): Polycube {
|
||||
return new Polycube(this.getDims(), this.getVals() as unknown as boolean[], this.id);
|
||||
}
|
||||
|
||||
getUniqueRotations(): Polycube[] {
|
||||
return super.getUniqueRotations().map(rot => new Polycube(rot.getDims(), rot.getVals() as unknown as boolean[], this.id));
|
||||
}
|
||||
}
|
||||
|
||||
type DimensionDef = [number, number, number];
|
||||
|
||||
// const testCube = new Cube([4, 2, 5], [
|
||||
// "000", "001", "002", "003", "004",
|
||||
// "010", "011", "012", "013", "014",
|
||||
// "100", "101", "102", "103", "104",
|
||||
// "110", "111", "112", "113", "114",
|
||||
// "200", "201", "202", "203", "204",
|
||||
// "210", "211", "212", "213", "214",
|
||||
// "300", "301", "302", "303", "304",
|
||||
// "310", "311", "312", "313", "314",
|
||||
// ]);
|
||||
// const somaCube = new Polycube([3, 3, 3], [
|
||||
// false, false, false,
|
||||
// false, false, false,
|
||||
// false, false, false,
|
||||
//
|
||||
// true, true, true,
|
||||
// true, true, true,
|
||||
// false, false, false,
|
||||
//
|
||||
// false, false, false,
|
||||
// false, false, false,
|
||||
// false, false, false,
|
||||
// ], 1);
|
||||
const unitCube1 = new Polycube([1, 1, 1], [true], 1);
|
||||
const unitCube2 = new Polycube([1, 1, 1], [true], 2);
|
||||
const unitCube3 = new Polycube([1, 1, 1], [true], 3);
|
||||
const unitCube4 = new Polycube([1, 1, 1], [true], 4);
|
||||
const unitCube5 = new Polycube([1, 1, 1], [true], 5);
|
||||
const unitCube6 = new Polycube([1, 1, 1], [true], 6);
|
||||
const unitCube7 = new Polycube([1, 1, 1], [true], 7);
|
||||
const unitCube8 = new Polycube([1, 1, 1], [true], 8);
|
||||
const unitCube9 = new Polycube([1, 1, 1], [true], 9);
|
||||
const unitCube10 = new Polycube([1, 1, 1], [true], 10);
|
||||
const unitCube11 = new Polycube([1, 1, 1], [true], 11);
|
||||
const unitCube12 = new Polycube([1, 1, 1], [true], 12);
|
||||
const unitCube13 = new Polycube([1, 1, 1], [true], 13);
|
||||
const unitCube14 = new Polycube([1, 1, 1], [true], 14);
|
||||
const unitCube15 = new Polycube([1, 1, 1], [true], 15);
|
||||
const unitCube16 = new Polycube([1, 1, 1], [true], 16);
|
||||
const unitCube17 = new Polycube([1, 1, 1], [true], 17);
|
||||
const unitCube18 = new Polycube([1, 1, 1], [true], 18);
|
||||
const unitCube19 = new Polycube([1, 1, 1], [true], 19);
|
||||
const unitCube20 = new Polycube([1, 1, 1], [true], 20);
|
||||
const unitCube21 = new Polycube([1, 1, 1], [true], 21);
|
||||
const unitCube22 = new Polycube([1, 1, 1], [true], 22);
|
||||
const unitCube23 = new Polycube([1, 1, 1], [true], 23);
|
||||
const unitCube24 = new Polycube([1, 1, 1], [true], 24);
|
||||
const unitCube25 = new Polycube([1, 1, 1], [true], 25);
|
||||
const unitCube26 = new Polycube([1, 1, 1], [true], 26);
|
||||
const unitCube27 = new Polycube([1, 1, 1], [true], 27);
|
||||
|
||||
const tetromino1 = new Polycube([3, 3, 3], [
|
||||
true, true, true,
|
||||
false, true, false,
|
||||
false, false, false,
|
||||
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
], 1);
|
||||
|
||||
const tetromino2 = new Polycube([3, 3, 3], [
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, true, false,
|
||||
|
||||
false, true, false,
|
||||
false, true, false,
|
||||
false, true, false,
|
||||
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
], 2);
|
||||
|
||||
const tetromino3 = new Polycube([3, 3, 3], [
|
||||
true, false, false,
|
||||
true, true, false,
|
||||
false, true, false,
|
||||
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
], 3);
|
||||
|
||||
const tetromino4 = new Polycube([3, 3, 3], [
|
||||
true, true, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
|
||||
true, false, false,
|
||||
true, false, false,
|
||||
false, false, false,
|
||||
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
], 4);
|
||||
|
||||
const tetromino5 = new Polycube([3, 3, 3], [
|
||||
true, true, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
|
||||
false, true, false,
|
||||
false, true, false,
|
||||
false, false, false,
|
||||
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
], 5);
|
||||
|
||||
const tetromino6 = new Polycube([3, 3, 3], [
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, true, false,
|
||||
|
||||
false, false, false,
|
||||
false, true, false,
|
||||
false, true, true,
|
||||
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
], 6);
|
||||
|
||||
const triomino1 = new Polycube([3, 3, 3], [
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, true, false,
|
||||
|
||||
false, false, false,
|
||||
false, true, false,
|
||||
false, true, false,
|
||||
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
false, false, false,
|
||||
], 7);
|
||||
|
||||
// const cube = new VoxelSpace([3, 3, 3], Array(3**3).fill(0));
|
||||
// cube.plus(triomino1)?.plus(tetromino2, {x: 1, y: 0, z: 1})?.print();
|
||||
|
||||
function start() {
|
||||
const solver = new SomaSolver(3);
|
||||
console.log("solving");
|
||||
solver.solve([triomino1, tetromino2, tetromino3, tetromino1, tetromino4, tetromino5, tetromino6]);
|
||||
}
|
||||
66
tsconfig.json
Normal file
66
tsconfig.json
Normal file
@@ -0,0 +1,66 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
/* Basic Options */
|
||||
// "incremental": true, /* Enable incremental compilation */
|
||||
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
|
||||
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
|
||||
"lib": ["dom", "ES6"], /* Specify library files to be included in the compilation. */
|
||||
// "allowJs": true, /* Allow javascript files to be compiled. */
|
||||
// "checkJs": true, /* Report errors in .js files. */
|
||||
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
|
||||
// "declaration": true, /* Generates corresponding '.d.ts' file. */
|
||||
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
|
||||
// "sourceMap": true, /* Generates corresponding '.map' file. */
|
||||
// "outFile": "./", /* Concatenate and emit output to single file. */
|
||||
// "outDir": "./", /* Redirect output structure to the directory. */
|
||||
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
|
||||
// "composite": true, /* Enable project compilation */
|
||||
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
|
||||
// "removeComments": true, /* Do not emit comments to output. */
|
||||
// "noEmit": true, /* Do not emit outputs. */
|
||||
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
|
||||
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
|
||||
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
|
||||
|
||||
/* Strict Type-Checking Options */
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
|
||||
// "strictNullChecks": true, /* Enable strict null checks. */
|
||||
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
|
||||
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
|
||||
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
|
||||
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
|
||||
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
|
||||
|
||||
/* Additional Checks */
|
||||
// "noUnusedLocals": true, /* Report errors on unused locals. */
|
||||
// "noUnusedParameters": true, /* Report errors on unused parameters. */
|
||||
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||
|
||||
/* Module Resolution Options */
|
||||
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
||||
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
|
||||
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||
// "types": [], /* Type declaration files to be included in compilation. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
||||
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
||||
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
|
||||
/* Source Map Options */
|
||||
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
|
||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
|
||||
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
|
||||
|
||||
/* Experimental Options */
|
||||
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
|
||||
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
|
||||
|
||||
/* Advanced Options */
|
||||
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user