@rednaxela101/cubing 0.63.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +120 -0
- package/alg/index.d.ts +1 -0
- package/bluetooth/index.d.ts +1 -0
- package/dist/bin/chunks/chunk-22VWCGBC.js +13 -0
- package/dist/bin/chunks/chunk-22VWCGBC.js.map +7 -0
- package/dist/bin/order.js +59 -0
- package/dist/bin/order.js.map +7 -0
- package/dist/bin/puzzle-geometry-bin.js +407 -0
- package/dist/bin/puzzle-geometry-bin.js.map +7 -0
- package/dist/bin/scramble.js +188 -0
- package/dist/bin/scramble.js.map +7 -0
- package/dist/bin/svg.js +56 -0
- package/dist/bin/svg.js.map +7 -0
- package/dist/lib/cubing/alg/index.d.ts +2 -0
- package/dist/lib/cubing/alg/index.js +43 -0
- package/dist/lib/cubing/alg/index.js.map +7 -0
- package/dist/lib/cubing/bluetooth/index.d.ts +189 -0
- package/dist/lib/cubing/bluetooth/index.js +2271 -0
- package/dist/lib/cubing/bluetooth/index.js.map +7 -0
- package/dist/lib/cubing/chunks/big-puzzle-orientation-BA6QHO5C.js +68 -0
- package/dist/lib/cubing/chunks/big-puzzle-orientation-BA6QHO5C.js.map +7 -0
- package/dist/lib/cubing/chunks/chunk-2VJB4TMH.js +1774 -0
- package/dist/lib/cubing/chunks/chunk-2VJB4TMH.js.map +7 -0
- package/dist/lib/cubing/chunks/chunk-52ZXJEUO.js +665 -0
- package/dist/lib/cubing/chunks/chunk-52ZXJEUO.js.map +7 -0
- package/dist/lib/cubing/chunks/chunk-56VZAEPF.js +274 -0
- package/dist/lib/cubing/chunks/chunk-56VZAEPF.js.map +7 -0
- package/dist/lib/cubing/chunks/chunk-7GUL3OBQ.js +9 -0
- package/dist/lib/cubing/chunks/chunk-7GUL3OBQ.js.map +7 -0
- package/dist/lib/cubing/chunks/chunk-7L7BJSIM.js +21 -0
- package/dist/lib/cubing/chunks/chunk-7L7BJSIM.js.map +7 -0
- package/dist/lib/cubing/chunks/chunk-AESPJKN6.js +265 -0
- package/dist/lib/cubing/chunks/chunk-AESPJKN6.js.map +7 -0
- package/dist/lib/cubing/chunks/chunk-C2Q347XS.js +89 -0
- package/dist/lib/cubing/chunks/chunk-C2Q347XS.js.map +7 -0
- package/dist/lib/cubing/chunks/chunk-CGBMJBU3.js +1195 -0
- package/dist/lib/cubing/chunks/chunk-CGBMJBU3.js.map +7 -0
- package/dist/lib/cubing/chunks/chunk-HZQONEMD.js +249 -0
- package/dist/lib/cubing/chunks/chunk-HZQONEMD.js.map +7 -0
- package/dist/lib/cubing/chunks/chunk-IAGCVPL3.js +1069 -0
- package/dist/lib/cubing/chunks/chunk-IAGCVPL3.js.map +7 -0
- package/dist/lib/cubing/chunks/chunk-NAPITA3L.js +1 -0
- package/dist/lib/cubing/chunks/chunk-NAPITA3L.js.map +7 -0
- package/dist/lib/cubing/chunks/chunk-XPDJUSZ7.js +1876 -0
- package/dist/lib/cubing/chunks/chunk-XPDJUSZ7.js.map +7 -0
- package/dist/lib/cubing/chunks/chunk-YSI6PF35.js +619 -0
- package/dist/lib/cubing/chunks/chunk-YSI6PF35.js.map +7 -0
- package/dist/lib/cubing/chunks/inside-LG2EAKCL.js +557 -0
- package/dist/lib/cubing/chunks/inside-LG2EAKCL.js.map +7 -0
- package/dist/lib/cubing/chunks/puzzles-dynamic-2x2x2-JYJJXB4V.js +142 -0
- package/dist/lib/cubing/chunks/puzzles-dynamic-2x2x2-JYJJXB4V.js.map +7 -0
- package/dist/lib/cubing/chunks/puzzles-dynamic-3x3x3-FYXD7SIU.js +365 -0
- package/dist/lib/cubing/chunks/puzzles-dynamic-3x3x3-FYXD7SIU.js.map +7 -0
- package/dist/lib/cubing/chunks/puzzles-dynamic-4x4x4-AIGABOAR.js +132 -0
- package/dist/lib/cubing/chunks/puzzles-dynamic-4x4x4-AIGABOAR.js.map +7 -0
- package/dist/lib/cubing/chunks/puzzles-dynamic-5x5x5-JUNRX27W.js +277 -0
- package/dist/lib/cubing/chunks/puzzles-dynamic-5x5x5-JUNRX27W.js.map +7 -0
- package/dist/lib/cubing/chunks/puzzles-dynamic-clock-USGQTDPO.js +836 -0
- package/dist/lib/cubing/chunks/puzzles-dynamic-clock-USGQTDPO.js.map +7 -0
- package/dist/lib/cubing/chunks/puzzles-dynamic-fto-WJHMLYCJ.js +100 -0
- package/dist/lib/cubing/chunks/puzzles-dynamic-fto-WJHMLYCJ.js.map +7 -0
- package/dist/lib/cubing/chunks/puzzles-dynamic-megaminx-2LVHIDL4.js +224 -0
- package/dist/lib/cubing/chunks/puzzles-dynamic-megaminx-2LVHIDL4.js.map +7 -0
- package/dist/lib/cubing/chunks/puzzles-dynamic-pyraminx-7UWX4HHI.js +93 -0
- package/dist/lib/cubing/chunks/puzzles-dynamic-pyraminx-7UWX4HHI.js.map +7 -0
- package/dist/lib/cubing/chunks/puzzles-dynamic-square1-PQEKBPFN.js +842 -0
- package/dist/lib/cubing/chunks/puzzles-dynamic-square1-PQEKBPFN.js.map +7 -0
- package/dist/lib/cubing/chunks/search-dynamic-sgs-2x2x2-XFKQ4VJS.js +125 -0
- package/dist/lib/cubing/chunks/search-dynamic-sgs-2x2x2-XFKQ4VJS.js.map +7 -0
- package/dist/lib/cubing/chunks/search-dynamic-sgs-megaminx-ZDOOLEDW.js +1564 -0
- package/dist/lib/cubing/chunks/search-dynamic-sgs-megaminx-ZDOOLEDW.js.map +7 -0
- package/dist/lib/cubing/chunks/search-dynamic-sgs-pyraminx-W6UJWBYY.js +108 -0
- package/dist/lib/cubing/chunks/search-dynamic-sgs-pyraminx-W6UJWBYY.js.map +7 -0
- package/dist/lib/cubing/chunks/search-dynamic-sgs-skewb-FXGKV4KD.js +125 -0
- package/dist/lib/cubing/chunks/search-dynamic-sgs-skewb-FXGKV4KD.js.map +7 -0
- package/dist/lib/cubing/chunks/search-dynamic-sgs-unofficial-YK6HA7BK.js +644 -0
- package/dist/lib/cubing/chunks/search-dynamic-sgs-unofficial-YK6HA7BK.js.map +7 -0
- package/dist/lib/cubing/chunks/search-dynamic-solve-3x3x3-DLMAGPXS.js +2549 -0
- package/dist/lib/cubing/chunks/search-dynamic-solve-3x3x3-DLMAGPXS.js.map +7 -0
- package/dist/lib/cubing/chunks/search-dynamic-solve-4x4x4-NONXQHOJ.js +2923 -0
- package/dist/lib/cubing/chunks/search-dynamic-solve-4x4x4-NONXQHOJ.js.map +7 -0
- package/dist/lib/cubing/chunks/search-dynamic-solve-fto-3GQ2HLSW.js +1405 -0
- package/dist/lib/cubing/chunks/search-dynamic-solve-fto-3GQ2HLSW.js.map +7 -0
- package/dist/lib/cubing/chunks/search-worker-entry.js +19 -0
- package/dist/lib/cubing/chunks/search-worker-entry.js.map +7 -0
- package/dist/lib/cubing/chunks/square1-stickerings-W2ZLZWIN.js +73 -0
- package/dist/lib/cubing/chunks/square1-stickerings-W2ZLZWIN.js.map +7 -0
- package/dist/lib/cubing/chunks/twips-2RNTWYNZ.js +433 -0
- package/dist/lib/cubing/chunks/twips-2RNTWYNZ.js.map +7 -0
- package/dist/lib/cubing/chunks/twips_wasm_bg-TJNYGA2C-23XTPLIK.js +10 -0
- package/dist/lib/cubing/chunks/twips_wasm_bg-TJNYGA2C-23XTPLIK.js.map +7 -0
- package/dist/lib/cubing/chunks/twisty-dynamic-3d-JO6OGKOF.js +2424 -0
- package/dist/lib/cubing/chunks/twisty-dynamic-3d-JO6OGKOF.js.map +7 -0
- package/dist/lib/cubing/index-BX6f56uo.d.ts +76 -0
- package/dist/lib/cubing/index-CcRu94bx.d.ts +2622 -0
- package/dist/lib/cubing/kpuzzle/index.d.ts +2 -0
- package/dist/lib/cubing/kpuzzle/index.js +12 -0
- package/dist/lib/cubing/kpuzzle/index.js.map +7 -0
- package/dist/lib/cubing/notation/index.d.ts +32 -0
- package/dist/lib/cubing/notation/index.js +20 -0
- package/dist/lib/cubing/notation/index.js.map +7 -0
- package/dist/lib/cubing/outside-BCzgSYQ_.d.ts +38 -0
- package/dist/lib/cubing/protocol/index.d.ts +28 -0
- package/dist/lib/cubing/protocol/index.js +20 -0
- package/dist/lib/cubing/protocol/index.js.map +7 -0
- package/dist/lib/cubing/puzzle-geometry/index.d.ts +2 -0
- package/dist/lib/cubing/puzzle-geometry/index.js +5024 -0
- package/dist/lib/cubing/puzzle-geometry/index.js.map +7 -0
- package/dist/lib/cubing/puzzles/index.d.ts +2 -0
- package/dist/lib/cubing/puzzles/index.js +23 -0
- package/dist/lib/cubing/puzzles/index.js.map +7 -0
- package/dist/lib/cubing/scramble/index.d.ts +2 -0
- package/dist/lib/cubing/scramble/index.js +16 -0
- package/dist/lib/cubing/scramble/index.js.map +7 -0
- package/dist/lib/cubing/search/index.d.ts +7 -0
- package/dist/lib/cubing/search/index.js +37 -0
- package/dist/lib/cubing/search/index.js.map +7 -0
- package/dist/lib/cubing/stream/index.d.ts +2 -0
- package/dist/lib/cubing/stream/index.js +111 -0
- package/dist/lib/cubing/stream/index.js.map +7 -0
- package/dist/lib/cubing/twisty/index.d.ts +2 -0
- package/dist/lib/cubing/twisty/index.js +6236 -0
- package/dist/lib/cubing/twisty/index.js.map +7 -0
- package/experimental-json-schema/kpuzzle/KPatternData.schema.json +34 -0
- package/experimental-json-schema/kpuzzle/KPuzzleDefinition.schema.json +97 -0
- package/experimental-json-schema/kpuzzle/KTransformationData.schema.json +27 -0
- package/kpuzzle/index.d.ts +1 -0
- package/notation/index.d.ts +1 -0
- package/package.json +146 -0
- package/protocol/index.d.ts +1 -0
- package/puzzle-geometry/index.d.ts +1 -0
- package/puzzles/index.d.ts +1 -0
- package/scramble/index.d.ts +1 -0
- package/search/index.d.ts +1 -0
- package/stream/index.d.ts +1 -0
- package/twisty/index.d.ts +1 -0
|
@@ -0,0 +1,5024 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Move,
|
|
3
|
+
QuantumMove
|
|
4
|
+
} from "../chunks/chunk-XPDJUSZ7.js";
|
|
5
|
+
|
|
6
|
+
// src/cubing/puzzle-geometry/colors.ts
|
|
7
|
+
function defaultPlatonicColorSchemes() {
|
|
8
|
+
return {
|
|
9
|
+
// the colors should use the same naming convention as the nets, above.
|
|
10
|
+
4: {
|
|
11
|
+
F: "#44ee00" /* Green */,
|
|
12
|
+
D: "#f4f400" /* Yellow */,
|
|
13
|
+
L: "#ff0000" /* Red */,
|
|
14
|
+
R: "#2266ff" /* Blue */
|
|
15
|
+
},
|
|
16
|
+
6: {
|
|
17
|
+
U: "#ffffff" /* White */,
|
|
18
|
+
F: "#44ee00" /* Green */,
|
|
19
|
+
R: "#ff0000" /* Red */,
|
|
20
|
+
D: "#f4f400" /* Yellow */,
|
|
21
|
+
B: "#2266ff" /* Blue */,
|
|
22
|
+
L: "#ff8000" /* Orange */
|
|
23
|
+
},
|
|
24
|
+
8: {
|
|
25
|
+
U: "#ffffff" /* White */,
|
|
26
|
+
F: "#44ee00" /* Green */,
|
|
27
|
+
R: "#ff0000" /* Red */,
|
|
28
|
+
D: "#f4f400" /* Yellow */,
|
|
29
|
+
BB: "#2266ff" /* Blue */,
|
|
30
|
+
L: "#8800dd" /* Purple */,
|
|
31
|
+
BL: "#ff8000" /* Orange */,
|
|
32
|
+
BR: "#888888" /* MediumGray */
|
|
33
|
+
},
|
|
34
|
+
12: {
|
|
35
|
+
U: "#ffffff" /* White */,
|
|
36
|
+
F: "#008800" /* DarkGreen */,
|
|
37
|
+
R: "#ff0000" /* Red */,
|
|
38
|
+
C: "#e8d0a0" /* Cream */,
|
|
39
|
+
A: "#3399ff" /* Aqua */,
|
|
40
|
+
L: "#8800dd" /* Purple */,
|
|
41
|
+
E: "#ff66cc" /* Pink */,
|
|
42
|
+
BF: "#99ff00" /* Lime */,
|
|
43
|
+
BR: "#0000ff" /* BoldBlue */,
|
|
44
|
+
BL: "#f4f400" /* Yellow */,
|
|
45
|
+
I: "#ff8000" /* Orange */,
|
|
46
|
+
D: "#888888" /* MediumGray */
|
|
47
|
+
},
|
|
48
|
+
20: {
|
|
49
|
+
R: "#f4f400" /* Yellow */,
|
|
50
|
+
C: "#d41f69" /* Cerise */,
|
|
51
|
+
F: "#008800" /* DarkGreen */,
|
|
52
|
+
E: "#5c5c5c" /* DarkGray */,
|
|
53
|
+
L: "#8800dd" /* Purple */,
|
|
54
|
+
U: "#ffffff" /* White */,
|
|
55
|
+
A: "#007a89" /* Teal */,
|
|
56
|
+
G: "#ff0000" /* Red */,
|
|
57
|
+
I: "#7d3b11" /* Brown */,
|
|
58
|
+
S: "#b9a1ff" /* Lavender */,
|
|
59
|
+
H: "#3399ff" /* Aqua */,
|
|
60
|
+
J: "#5ec4b6" /* SeaGreen */,
|
|
61
|
+
B: "#44ee00" /* Green */,
|
|
62
|
+
K: "#e8d0a0" /* Cream */,
|
|
63
|
+
D: "#aaaaaa" /* LightGray */,
|
|
64
|
+
M: "#ff66cc" /* Pink */,
|
|
65
|
+
O: "#292929" /* harcoal */,
|
|
66
|
+
P: "#ff8000" /* Orange */,
|
|
67
|
+
N: "#980000" /* Burgundy */,
|
|
68
|
+
Q: "#0000ff" /* BoldBlue */
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// src/cubing/puzzle-geometry/FaceNameSwizzler.ts
|
|
74
|
+
var FaceNameSwizzler = class {
|
|
75
|
+
constructor(facenames, gripnames_arg) {
|
|
76
|
+
this.facenames = facenames;
|
|
77
|
+
if (gripnames_arg) {
|
|
78
|
+
this.gripnames = gripnames_arg;
|
|
79
|
+
}
|
|
80
|
+
for (let i = 0; this.prefixFree && i < facenames.length; i++) {
|
|
81
|
+
for (let j = 0; this.prefixFree && j < facenames.length; j++) {
|
|
82
|
+
if (i !== j && facenames[i].startsWith(facenames[j])) {
|
|
83
|
+
this.prefixFree = false;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
facenames;
|
|
89
|
+
prefixFree = true;
|
|
90
|
+
gripnames = [];
|
|
91
|
+
setGripNames(names) {
|
|
92
|
+
this.gripnames = names;
|
|
93
|
+
}
|
|
94
|
+
// split a string into face names and return a list of
|
|
95
|
+
// indices.
|
|
96
|
+
splitByFaceNames(s) {
|
|
97
|
+
const r = [];
|
|
98
|
+
let at = 0;
|
|
99
|
+
while (at < s.length) {
|
|
100
|
+
if (at > 0 && at < s.length && s[at] === "_") {
|
|
101
|
+
at++;
|
|
102
|
+
}
|
|
103
|
+
let currentMatch = -1;
|
|
104
|
+
for (let i = 0; i < this.facenames.length; i++) {
|
|
105
|
+
if (s.substr(at).startsWith(this.facenames[i]) && (currentMatch < 0 || this.facenames[i].length > this.facenames[currentMatch].length)) {
|
|
106
|
+
currentMatch = i;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (currentMatch >= 0) {
|
|
110
|
+
r.push(currentMatch);
|
|
111
|
+
at += this.facenames[currentMatch].length;
|
|
112
|
+
} else {
|
|
113
|
+
throw new Error(`Could not split ${s} into face names.`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return r;
|
|
117
|
+
}
|
|
118
|
+
// cons a grip from an array of numbers.
|
|
119
|
+
joinByFaceIndices(list) {
|
|
120
|
+
let sep = "";
|
|
121
|
+
const r = [];
|
|
122
|
+
for (let i = 0; i < list.length; i++) {
|
|
123
|
+
r.push(sep);
|
|
124
|
+
r.push(this.facenames[list[i]]);
|
|
125
|
+
if (!this.prefixFree) {
|
|
126
|
+
sep = "_";
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return r.join("");
|
|
130
|
+
}
|
|
131
|
+
/*
|
|
132
|
+
* Try to match something the user gave us with some geometric
|
|
133
|
+
* feature. We used to have strict requirements:
|
|
134
|
+
*
|
|
135
|
+
* a) The set of face names are prefix free
|
|
136
|
+
* b) When specifying a corner, all coincident planes were
|
|
137
|
+
* specified
|
|
138
|
+
*
|
|
139
|
+
* But, to allow megaminx to have more reasonable and
|
|
140
|
+
* conventional names, and to permit shorter canonical
|
|
141
|
+
* names, we are relaxing these requirements and adding
|
|
142
|
+
* new syntax. Now:
|
|
143
|
+
*
|
|
144
|
+
* a) Face names need not be syntax free.
|
|
145
|
+
* b) When parsing a geometric name, we use greedy
|
|
146
|
+
* matching, so the longest name that matches the
|
|
147
|
+
* user string at the current position is the one
|
|
148
|
+
* assumed to match.
|
|
149
|
+
* c) Underscores are permitted to separate face names
|
|
150
|
+
* (both in user input and in geometric
|
|
151
|
+
* descriptions).
|
|
152
|
+
* d) Default names of corner moves where corners have
|
|
153
|
+
* more than three corners, need only include three
|
|
154
|
+
* of the corners.
|
|
155
|
+
*
|
|
156
|
+
* This code is not performance-sensitive so we can do it a
|
|
157
|
+
* slow and simple way.
|
|
158
|
+
*/
|
|
159
|
+
spinmatch(userinput, longname) {
|
|
160
|
+
if (userinput === longname) {
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
try {
|
|
164
|
+
const e1 = this.splitByFaceNames(userinput);
|
|
165
|
+
const e2 = this.splitByFaceNames(longname);
|
|
166
|
+
if (e1.length !== e2.length && e1.length < 3) {
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
for (let i = 0; i < e1.length; i++) {
|
|
170
|
+
for (let j = 0; j < i; j++) {
|
|
171
|
+
if (e1[i] === e1[j]) {
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
let found = false;
|
|
176
|
+
for (let j = 0; j < e2.length; j++) {
|
|
177
|
+
if (e1[i] === e2[j]) {
|
|
178
|
+
found = true;
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
if (!found) {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return true;
|
|
187
|
+
} catch {
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
/* same as above, but permit both to have v's on the end. */
|
|
192
|
+
spinmatchv(userinput, longname) {
|
|
193
|
+
if (userinput.endsWith("v") && longname.endsWith("v")) {
|
|
194
|
+
return this.spinmatch(
|
|
195
|
+
userinput.slice(0, userinput.length - 1),
|
|
196
|
+
longname.slice(0, longname.length - 1)
|
|
197
|
+
);
|
|
198
|
+
} else {
|
|
199
|
+
return this.spinmatch(userinput, longname);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
unswizzle(s) {
|
|
203
|
+
if ((s.endsWith("v") || s.endsWith("w")) && s[0] <= "Z") {
|
|
204
|
+
s = s.slice(0, s.length - 1);
|
|
205
|
+
}
|
|
206
|
+
const upperCaseGrip = s.toUpperCase();
|
|
207
|
+
for (let i = 0; i < this.gripnames.length; i++) {
|
|
208
|
+
const g = this.gripnames[i];
|
|
209
|
+
if (this.spinmatch(upperCaseGrip, g)) {
|
|
210
|
+
return g;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return s;
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
// src/cubing/puzzle-geometry/notation-mapping/FaceRenamingMapper.ts
|
|
218
|
+
var FaceRenamingMapper = class {
|
|
219
|
+
constructor(internalNames, externalNames) {
|
|
220
|
+
this.internalNames = internalNames;
|
|
221
|
+
this.externalNames = externalNames;
|
|
222
|
+
}
|
|
223
|
+
internalNames;
|
|
224
|
+
externalNames;
|
|
225
|
+
// TODO: consider putting a cache in front of this
|
|
226
|
+
convertString(grip, a, b) {
|
|
227
|
+
let suffix = "";
|
|
228
|
+
if ((grip.endsWith("v") || grip.endsWith("v")) && grip <= "_") {
|
|
229
|
+
suffix = grip.slice(grip.length - 1);
|
|
230
|
+
grip = grip.slice(0, grip.length - 1);
|
|
231
|
+
}
|
|
232
|
+
const upper = grip.toUpperCase();
|
|
233
|
+
let isLowerCase = false;
|
|
234
|
+
if (grip !== upper) {
|
|
235
|
+
isLowerCase = true;
|
|
236
|
+
grip = upper;
|
|
237
|
+
}
|
|
238
|
+
grip = b.joinByFaceIndices(a.splitByFaceNames(grip));
|
|
239
|
+
if (isLowerCase) {
|
|
240
|
+
grip = grip.toLowerCase();
|
|
241
|
+
}
|
|
242
|
+
return grip + suffix;
|
|
243
|
+
}
|
|
244
|
+
convert(move, a, b) {
|
|
245
|
+
const grip = move.family;
|
|
246
|
+
const ngrip = this.convertString(grip, a, b);
|
|
247
|
+
if (grip === ngrip) {
|
|
248
|
+
return move;
|
|
249
|
+
} else {
|
|
250
|
+
return new Move(
|
|
251
|
+
new QuantumMove(ngrip, move.innerLayer, move.outerLayer),
|
|
252
|
+
move.amount
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
notationToInternal(move) {
|
|
257
|
+
const r = this.convert(move, this.externalNames, this.internalNames);
|
|
258
|
+
return r;
|
|
259
|
+
}
|
|
260
|
+
notationToExternal(move) {
|
|
261
|
+
return this.convert(move, this.internalNames, this.externalNames);
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
// src/cubing/puzzle-geometry/notation-mapping/FTONotationMapper.ts
|
|
266
|
+
var FTONotationMapper = class {
|
|
267
|
+
constructor(child, sw) {
|
|
268
|
+
this.child = child;
|
|
269
|
+
this.sw = sw;
|
|
270
|
+
}
|
|
271
|
+
child;
|
|
272
|
+
sw;
|
|
273
|
+
notationToInternal(move) {
|
|
274
|
+
if (move.family === "T" && move.innerLayer === void 0 && move.outerLayer === void 0) {
|
|
275
|
+
return new Move(
|
|
276
|
+
new QuantumMove("FLRv", move.innerLayer, move.outerLayer),
|
|
277
|
+
move.amount
|
|
278
|
+
);
|
|
279
|
+
} else {
|
|
280
|
+
const r = this.child.notationToInternal(move);
|
|
281
|
+
return r;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
// we never rewrite click moves to these moves.
|
|
285
|
+
notationToExternal(move) {
|
|
286
|
+
let fam = move.family;
|
|
287
|
+
if (fam.length > 0 && fam[fam.length - 1] === "v") {
|
|
288
|
+
fam = fam.substring(0, fam.length - 1);
|
|
289
|
+
}
|
|
290
|
+
if (this.sw.spinmatch(fam, "FLUR")) {
|
|
291
|
+
return new Move(
|
|
292
|
+
new QuantumMove("T", move.innerLayer, move.outerLayer),
|
|
293
|
+
move.amount
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
return this.child.notationToExternal(move);
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
// src/cubing/puzzle-geometry/notation-mapping/MegaminxScramblingNotationMapper.ts
|
|
301
|
+
var MegaminxScramblingNotationMapper = class {
|
|
302
|
+
constructor(child) {
|
|
303
|
+
this.child = child;
|
|
304
|
+
}
|
|
305
|
+
child;
|
|
306
|
+
notationToInternal(move) {
|
|
307
|
+
if (move.innerLayer === void 0 && move.outerLayer === void 0) {
|
|
308
|
+
if (Math.abs(move.amount) === 1) {
|
|
309
|
+
if (move.family === "R++") {
|
|
310
|
+
return new Move(new QuantumMove("L", 3, 2), -2 * move.amount);
|
|
311
|
+
} else if (move.family === "R--") {
|
|
312
|
+
return new Move(new QuantumMove("L", 3, 2), 2 * move.amount);
|
|
313
|
+
} else if (move.family === "D++") {
|
|
314
|
+
return new Move(new QuantumMove("U", 3, 2), -2 * move.amount);
|
|
315
|
+
} else if (move.family === "D--") {
|
|
316
|
+
return new Move(new QuantumMove("U", 3, 2), 2 * move.amount);
|
|
317
|
+
}
|
|
318
|
+
if (move.family === "R_PLUSPLUS_") {
|
|
319
|
+
return new Move(new QuantumMove("L", 3, 2), -2 * move.amount);
|
|
320
|
+
} else if (move.family === "D_PLUSPLUS_") {
|
|
321
|
+
return new Move(new QuantumMove("U", 3, 2), -2 * move.amount);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
if (move.family === "y") {
|
|
325
|
+
return new Move("Uv", move.amount);
|
|
326
|
+
}
|
|
327
|
+
if (move.family === "x" && Math.abs(move.amount) === 2) {
|
|
328
|
+
return new Move("ERv", move.amount / 2);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return this.child.notationToInternal(move);
|
|
332
|
+
}
|
|
333
|
+
// we never rewrite click moves to these moves.
|
|
334
|
+
notationToExternal(move) {
|
|
335
|
+
if (move.family === "ERv" && Math.abs(move.amount) === 1) {
|
|
336
|
+
return new Move(
|
|
337
|
+
new QuantumMove("x", move.innerLayer, move.outerLayer),
|
|
338
|
+
move.amount * 2
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
if (move.family === "ILv" && Math.abs(move.amount) === 1) {
|
|
342
|
+
return new Move(
|
|
343
|
+
new QuantumMove("x", move.innerLayer, move.outerLayer),
|
|
344
|
+
-move.amount * 2
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
if (move.family === "Uv") {
|
|
348
|
+
return new Move(
|
|
349
|
+
new QuantumMove("y", move.innerLayer, move.outerLayer),
|
|
350
|
+
move.amount
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
if (move.family === "Dv") {
|
|
354
|
+
return new Move("y", -move.amount);
|
|
355
|
+
}
|
|
356
|
+
return this.child.notationToExternal(move);
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
// src/cubing/puzzle-geometry/notation-mapping/NullMapper.ts
|
|
361
|
+
var NullMapper = class {
|
|
362
|
+
notationToInternal(move) {
|
|
363
|
+
return move;
|
|
364
|
+
}
|
|
365
|
+
notationToExternal(move) {
|
|
366
|
+
return move;
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
// src/cubing/puzzle-geometry/notation-mapping/NxNxNCubeMapper.ts
|
|
371
|
+
var NxNxNCubeMapper = class {
|
|
372
|
+
constructor(slices) {
|
|
373
|
+
this.slices = slices;
|
|
374
|
+
}
|
|
375
|
+
slices;
|
|
376
|
+
notationToInternal(move) {
|
|
377
|
+
const grip = move.family;
|
|
378
|
+
if (!(move.innerLayer || move.outerLayer)) {
|
|
379
|
+
if (grip === "x") {
|
|
380
|
+
move = new Move("Rv", move.amount);
|
|
381
|
+
} else if (grip === "y") {
|
|
382
|
+
move = new Move("Uv", move.amount);
|
|
383
|
+
} else if (grip === "z") {
|
|
384
|
+
move = new Move("Fv", move.amount);
|
|
385
|
+
}
|
|
386
|
+
if ((this.slices & 1) === 1) {
|
|
387
|
+
if (grip === "E") {
|
|
388
|
+
move = new Move(
|
|
389
|
+
new QuantumMove("D", (this.slices + 1) / 2),
|
|
390
|
+
move.amount
|
|
391
|
+
);
|
|
392
|
+
} else if (grip === "M") {
|
|
393
|
+
move = new Move(
|
|
394
|
+
new QuantumMove("L", (this.slices + 1) / 2),
|
|
395
|
+
move.amount
|
|
396
|
+
);
|
|
397
|
+
} else if (grip === "S") {
|
|
398
|
+
move = new Move(
|
|
399
|
+
new QuantumMove("F", (this.slices + 1) / 2),
|
|
400
|
+
move.amount
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
if (this.slices > 2) {
|
|
405
|
+
if (grip === "e") {
|
|
406
|
+
move = new Move(
|
|
407
|
+
new QuantumMove("D", this.slices - 1, 2),
|
|
408
|
+
move.amount
|
|
409
|
+
);
|
|
410
|
+
} else if (grip === "m") {
|
|
411
|
+
move = new Move(
|
|
412
|
+
new QuantumMove("L", this.slices - 1, 2),
|
|
413
|
+
move.amount
|
|
414
|
+
);
|
|
415
|
+
} else if (grip === "s") {
|
|
416
|
+
move = new Move(
|
|
417
|
+
new QuantumMove("F", this.slices - 1, 2),
|
|
418
|
+
move.amount
|
|
419
|
+
);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
return move;
|
|
424
|
+
}
|
|
425
|
+
// do we want to map slice moves to E/M/S instead of 2U/etc.?
|
|
426
|
+
notationToExternal(move) {
|
|
427
|
+
const grip = move.family;
|
|
428
|
+
if (!(move.innerLayer || move.outerLayer)) {
|
|
429
|
+
if (grip === "Rv") {
|
|
430
|
+
return new Move("x", move.amount);
|
|
431
|
+
} else if (grip === "Uv") {
|
|
432
|
+
return new Move("y", move.amount);
|
|
433
|
+
} else if (grip === "Fv") {
|
|
434
|
+
return new Move("z", move.amount);
|
|
435
|
+
} else if (grip === "Lv") {
|
|
436
|
+
return new Move("x", -move.amount);
|
|
437
|
+
} else if (grip === "Dv") {
|
|
438
|
+
return new Move("y", -move.amount);
|
|
439
|
+
} else if (grip === "Bv") {
|
|
440
|
+
return new Move("z", -move.amount);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
return move;
|
|
444
|
+
}
|
|
445
|
+
};
|
|
446
|
+
|
|
447
|
+
// src/cubing/puzzle-geometry/notation-mapping/PyraminxNotationMapper.ts
|
|
448
|
+
var pyraminxFamilyMap = {
|
|
449
|
+
U: "frl",
|
|
450
|
+
L: "fld",
|
|
451
|
+
R: "fdr",
|
|
452
|
+
B: "dlr",
|
|
453
|
+
u: "FRL",
|
|
454
|
+
l: "FLD",
|
|
455
|
+
r: "FDR",
|
|
456
|
+
b: "DLR",
|
|
457
|
+
Uv: "FRLv",
|
|
458
|
+
Lv: "FLDv",
|
|
459
|
+
Rv: "FDRv",
|
|
460
|
+
Bv: "DLRv",
|
|
461
|
+
D: "D",
|
|
462
|
+
F: "F",
|
|
463
|
+
BL: "L",
|
|
464
|
+
BR: "R"
|
|
465
|
+
};
|
|
466
|
+
var tetraminxFamilyMap = {
|
|
467
|
+
U: "FRL",
|
|
468
|
+
L: "FLD",
|
|
469
|
+
R: "FDR",
|
|
470
|
+
B: "DLR",
|
|
471
|
+
u: "frl",
|
|
472
|
+
l: "fld",
|
|
473
|
+
r: "fdr",
|
|
474
|
+
b: "dlr",
|
|
475
|
+
Uv: "FRLv",
|
|
476
|
+
Lv: "FLDv",
|
|
477
|
+
Rv: "FDRv",
|
|
478
|
+
Bv: "DLRv",
|
|
479
|
+
D: "D",
|
|
480
|
+
F: "F",
|
|
481
|
+
BL: "L",
|
|
482
|
+
BR: "R",
|
|
483
|
+
d: "d",
|
|
484
|
+
f: "f",
|
|
485
|
+
bl: "l",
|
|
486
|
+
br: "r"
|
|
487
|
+
};
|
|
488
|
+
var pyraminxFamilyMapWCA = {
|
|
489
|
+
U: "FRL",
|
|
490
|
+
L: "FLD",
|
|
491
|
+
R: "FDR",
|
|
492
|
+
B: "DLR"
|
|
493
|
+
};
|
|
494
|
+
var pyraminxExternalQuantumY = new QuantumMove("y");
|
|
495
|
+
var pyraminxInternalQuantumY = new QuantumMove("Dv");
|
|
496
|
+
var PyraminxNotationMapper = class {
|
|
497
|
+
constructor(child) {
|
|
498
|
+
this.child = child;
|
|
499
|
+
}
|
|
500
|
+
child;
|
|
501
|
+
wcaHack = false;
|
|
502
|
+
map = pyraminxFamilyMap;
|
|
503
|
+
notationToInternal(move) {
|
|
504
|
+
if (this.wcaHack && move.innerLayer === 2 && move.outerLayer === null) {
|
|
505
|
+
const newFamilyWCA = pyraminxFamilyMapWCA[move.family];
|
|
506
|
+
if (newFamilyWCA) {
|
|
507
|
+
return new Move(
|
|
508
|
+
new QuantumMove(newFamilyWCA, move.innerLayer, move.outerLayer),
|
|
509
|
+
move.amount
|
|
510
|
+
);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
const newFamily = this.map[move.family];
|
|
514
|
+
if (newFamily) {
|
|
515
|
+
return new Move(
|
|
516
|
+
new QuantumMove(newFamily, move.innerLayer, move.outerLayer),
|
|
517
|
+
move.amount
|
|
518
|
+
);
|
|
519
|
+
} else if (pyraminxExternalQuantumY.isIdentical(move.quantum)) {
|
|
520
|
+
return new Move(pyraminxInternalQuantumY, -move.amount);
|
|
521
|
+
} else {
|
|
522
|
+
return null;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
// we never rewrite click moves to these moves.
|
|
526
|
+
notationToExternal(move) {
|
|
527
|
+
if (this.wcaHack && move.innerLayer === 2 && move.outerLayer === null) {
|
|
528
|
+
for (const [external, internal] of Object.entries(pyraminxFamilyMapWCA)) {
|
|
529
|
+
if (this.child.spinmatch(move.family, internal)) {
|
|
530
|
+
return new Move(
|
|
531
|
+
new QuantumMove(external, move.innerLayer, move.outerLayer),
|
|
532
|
+
move.amount
|
|
533
|
+
);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
for (const [external, internal] of Object.entries(this.map)) {
|
|
538
|
+
if (this.child.spinmatch(move.family, internal)) {
|
|
539
|
+
return new Move(
|
|
540
|
+
new QuantumMove(external, move.innerLayer, move.outerLayer),
|
|
541
|
+
move.amount
|
|
542
|
+
);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
if (pyraminxInternalQuantumY.isIdentical(move.quantum)) {
|
|
546
|
+
return new Move(pyraminxExternalQuantumY, -move.amount);
|
|
547
|
+
} else {
|
|
548
|
+
return null;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
};
|
|
552
|
+
var TetraminxNotationMapper = class extends PyraminxNotationMapper {
|
|
553
|
+
wcaHack = true;
|
|
554
|
+
constructor(child) {
|
|
555
|
+
super(child);
|
|
556
|
+
this.map = tetraminxFamilyMap;
|
|
557
|
+
}
|
|
558
|
+
};
|
|
559
|
+
|
|
560
|
+
// src/cubing/puzzle-geometry/notation-mapping/SkewbNotationMapper.ts
|
|
561
|
+
var skewbFamilyMap = {
|
|
562
|
+
U: "UBL",
|
|
563
|
+
UL: "ULF",
|
|
564
|
+
F: "UFR",
|
|
565
|
+
UR: "URB",
|
|
566
|
+
B: "DBL",
|
|
567
|
+
D: "DFR",
|
|
568
|
+
L: "DLF",
|
|
569
|
+
R: "DRB",
|
|
570
|
+
Uv: "UBLv",
|
|
571
|
+
ULv: "ULFv",
|
|
572
|
+
Fv: "UFRv",
|
|
573
|
+
URv: "URBv",
|
|
574
|
+
Bv: "DBLv",
|
|
575
|
+
Dv: "DFRv",
|
|
576
|
+
Lv: "DLFv",
|
|
577
|
+
Rv: "DRBv"
|
|
578
|
+
};
|
|
579
|
+
var skewbExternalQuantumX = new QuantumMove("x");
|
|
580
|
+
var skewbInternalQuantumX = new QuantumMove("Rv");
|
|
581
|
+
var skewbInternalQuantumXPrime = new QuantumMove("Lv");
|
|
582
|
+
var skewbExternalQuantumY = new QuantumMove("y");
|
|
583
|
+
var skewbInternalQuantumY = new QuantumMove("Uv");
|
|
584
|
+
var skewbInternalQuantumYPrime = new QuantumMove("Dv");
|
|
585
|
+
var skewbExternalQuantumZ = new QuantumMove("z");
|
|
586
|
+
var skewbInternalQuantumZ = new QuantumMove("Fv");
|
|
587
|
+
var skewbInternalQuantumZPrime = new QuantumMove("Bv");
|
|
588
|
+
var SkewbNotationMapper = class {
|
|
589
|
+
constructor(child) {
|
|
590
|
+
this.child = child;
|
|
591
|
+
}
|
|
592
|
+
child;
|
|
593
|
+
notationToInternal(move) {
|
|
594
|
+
if (move.innerLayer || move.outerLayer) {
|
|
595
|
+
return null;
|
|
596
|
+
}
|
|
597
|
+
const newFamily = skewbFamilyMap[move.family];
|
|
598
|
+
if (newFamily) {
|
|
599
|
+
return new Move(
|
|
600
|
+
new QuantumMove(newFamily, move.outerLayer, move.innerLayer),
|
|
601
|
+
move.amount
|
|
602
|
+
);
|
|
603
|
+
}
|
|
604
|
+
if (skewbExternalQuantumX.isIdentical(move.quantum)) {
|
|
605
|
+
return new Move(skewbInternalQuantumX, move.amount);
|
|
606
|
+
}
|
|
607
|
+
if (skewbExternalQuantumY.isIdentical(move.quantum)) {
|
|
608
|
+
return new Move(skewbInternalQuantumY, move.amount);
|
|
609
|
+
}
|
|
610
|
+
if (skewbExternalQuantumZ.isIdentical(move.quantum)) {
|
|
611
|
+
return new Move(skewbInternalQuantumZ, move.amount);
|
|
612
|
+
}
|
|
613
|
+
return null;
|
|
614
|
+
}
|
|
615
|
+
// we never rewrite click moves to these moves.
|
|
616
|
+
notationToExternal(move) {
|
|
617
|
+
for (const [external, internal] of Object.entries(skewbFamilyMap)) {
|
|
618
|
+
if (this.child.spinmatchv(move.family, internal)) {
|
|
619
|
+
return new Move(
|
|
620
|
+
new QuantumMove(external, move.innerLayer, move.outerLayer),
|
|
621
|
+
move.amount
|
|
622
|
+
);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
if (skewbInternalQuantumX.isIdentical(move.quantum)) {
|
|
626
|
+
return new Move(skewbExternalQuantumX, move.amount);
|
|
627
|
+
}
|
|
628
|
+
if (skewbInternalQuantumXPrime.isIdentical(move.quantum)) {
|
|
629
|
+
return new Move(skewbExternalQuantumX, -move.amount);
|
|
630
|
+
}
|
|
631
|
+
if (skewbInternalQuantumY.isIdentical(move.quantum)) {
|
|
632
|
+
return new Move(skewbExternalQuantumY, move.amount);
|
|
633
|
+
}
|
|
634
|
+
if (skewbInternalQuantumYPrime.isIdentical(move.quantum)) {
|
|
635
|
+
return new Move(skewbExternalQuantumY, -move.amount);
|
|
636
|
+
}
|
|
637
|
+
if (skewbInternalQuantumZ.isIdentical(move.quantum)) {
|
|
638
|
+
return new Move(skewbExternalQuantumZ, move.amount);
|
|
639
|
+
}
|
|
640
|
+
if (skewbInternalQuantumZPrime.isIdentical(move.quantum)) {
|
|
641
|
+
return new Move(skewbExternalQuantumZ, -move.amount);
|
|
642
|
+
}
|
|
643
|
+
return null;
|
|
644
|
+
}
|
|
645
|
+
};
|
|
646
|
+
|
|
647
|
+
// src/cubing/puzzle-geometry/notation-mapping/NotationMapper.ts
|
|
648
|
+
function remapKPuzzleDefinition(internalDefinition, notationMapper) {
|
|
649
|
+
const externalDefinition = {
|
|
650
|
+
...internalDefinition,
|
|
651
|
+
moves: {}
|
|
652
|
+
};
|
|
653
|
+
for (const [internalMoveName, transformationData] of Object.entries(
|
|
654
|
+
internalDefinition.moves
|
|
655
|
+
)) {
|
|
656
|
+
let prefix = internalMoveName;
|
|
657
|
+
let suffix = "";
|
|
658
|
+
if (["v", "w"].includes(internalMoveName.at(-1))) {
|
|
659
|
+
prefix = internalMoveName.slice(0, -1);
|
|
660
|
+
suffix = internalMoveName.slice(-1);
|
|
661
|
+
}
|
|
662
|
+
const externalPrefix = notationMapper.notationToExternal(
|
|
663
|
+
Move.fromString(prefix)
|
|
664
|
+
);
|
|
665
|
+
if (!externalPrefix) {
|
|
666
|
+
continue;
|
|
667
|
+
}
|
|
668
|
+
const externalMoveName = externalPrefix + suffix;
|
|
669
|
+
if (!externalMoveName) {
|
|
670
|
+
throw new Error(
|
|
671
|
+
`Missing external move name for: ${internalMoveName.toString()}`
|
|
672
|
+
);
|
|
673
|
+
}
|
|
674
|
+
externalDefinition.moves[externalMoveName.toString()] = transformationData;
|
|
675
|
+
}
|
|
676
|
+
return externalDefinition;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
// src/cubing/puzzle-geometry/Options.ts
|
|
680
|
+
var PuzzleGeometryFullOptions = class {
|
|
681
|
+
verbosity = 0;
|
|
682
|
+
// verbosity (console.log)
|
|
683
|
+
allMoves = false;
|
|
684
|
+
// generate all slice moves in ksolve
|
|
685
|
+
outerBlockMoves = false;
|
|
686
|
+
// generate outer block moves
|
|
687
|
+
vertexMoves = false;
|
|
688
|
+
// generate vertex moves
|
|
689
|
+
addRotations = false;
|
|
690
|
+
// add symmetry information to ksolve output
|
|
691
|
+
moveList = null;
|
|
692
|
+
// move list to generate
|
|
693
|
+
fixedOrientation = false;
|
|
694
|
+
// eliminate any orientations
|
|
695
|
+
fixedPieceType = null;
|
|
696
|
+
// fix a piece?
|
|
697
|
+
orientCenters = false;
|
|
698
|
+
// orient centers?
|
|
699
|
+
// TODO: Group these into a single object?
|
|
700
|
+
includeCornerOrbits = true;
|
|
701
|
+
// include corner orbits
|
|
702
|
+
includeCenterOrbits = true;
|
|
703
|
+
// include center orbits
|
|
704
|
+
includeEdgeOrbits = true;
|
|
705
|
+
// include edge orbits
|
|
706
|
+
// Overrides the previous options.
|
|
707
|
+
excludeOrbits = [];
|
|
708
|
+
// exclude these orbits
|
|
709
|
+
optimizeOrbits = false;
|
|
710
|
+
// optimize PermOri
|
|
711
|
+
grayCorners = false;
|
|
712
|
+
// make corner sets gray
|
|
713
|
+
grayCenters = false;
|
|
714
|
+
// make center sets gray
|
|
715
|
+
grayEdges = false;
|
|
716
|
+
// make edge sets gray
|
|
717
|
+
puzzleOrientation = null;
|
|
718
|
+
// single puzzle orientation from options
|
|
719
|
+
puzzleOrientations = null;
|
|
720
|
+
// puzzle orientation override object from options // TODO: is this needed?
|
|
721
|
+
scrambleAmount = 0;
|
|
722
|
+
// scramble?
|
|
723
|
+
constructor(options = {}) {
|
|
724
|
+
const optionsWithoutUndefined = Object.fromEntries(
|
|
725
|
+
Object.entries(options).filter(
|
|
726
|
+
([_, value]) => typeof value !== "undefined"
|
|
727
|
+
)
|
|
728
|
+
);
|
|
729
|
+
Object.assign(this, optionsWithoutUndefined);
|
|
730
|
+
}
|
|
731
|
+
};
|
|
732
|
+
|
|
733
|
+
// src/cubing/puzzle-geometry/Perm.ts
|
|
734
|
+
var zeroCache = [];
|
|
735
|
+
var iotaCache = [];
|
|
736
|
+
function zeros(n) {
|
|
737
|
+
if (!zeroCache[n]) {
|
|
738
|
+
const c = Array(n);
|
|
739
|
+
for (let i = 0; i < n; i++) {
|
|
740
|
+
c[i] = 0;
|
|
741
|
+
}
|
|
742
|
+
zeroCache[n] = c;
|
|
743
|
+
}
|
|
744
|
+
return zeroCache[n];
|
|
745
|
+
}
|
|
746
|
+
function iota(n) {
|
|
747
|
+
if (!iotaCache[n]) {
|
|
748
|
+
const c = Array(n);
|
|
749
|
+
for (let i = 0; i < n; i++) {
|
|
750
|
+
c[i] = i;
|
|
751
|
+
}
|
|
752
|
+
iotaCache[n] = c;
|
|
753
|
+
}
|
|
754
|
+
return iotaCache[n];
|
|
755
|
+
}
|
|
756
|
+
function identity(n) {
|
|
757
|
+
return new Perm(iota(n));
|
|
758
|
+
}
|
|
759
|
+
function factorial(a) {
|
|
760
|
+
let r = BigInt(1);
|
|
761
|
+
while (a > 1) {
|
|
762
|
+
r *= BigInt(a);
|
|
763
|
+
a--;
|
|
764
|
+
}
|
|
765
|
+
return r;
|
|
766
|
+
}
|
|
767
|
+
function gcd(a, b) {
|
|
768
|
+
if (a > b) {
|
|
769
|
+
const t = a;
|
|
770
|
+
a = b;
|
|
771
|
+
b = t;
|
|
772
|
+
}
|
|
773
|
+
while (a > 0) {
|
|
774
|
+
const m = b % a;
|
|
775
|
+
b = a;
|
|
776
|
+
a = m;
|
|
777
|
+
}
|
|
778
|
+
return b;
|
|
779
|
+
}
|
|
780
|
+
function lcm(a, b) {
|
|
781
|
+
return a / gcd(a, b) * b;
|
|
782
|
+
}
|
|
783
|
+
var Perm = class _Perm {
|
|
784
|
+
n;
|
|
785
|
+
// length
|
|
786
|
+
p;
|
|
787
|
+
// The permutation itself
|
|
788
|
+
constructor(a) {
|
|
789
|
+
this.n = a.length;
|
|
790
|
+
this.p = a;
|
|
791
|
+
}
|
|
792
|
+
toString() {
|
|
793
|
+
return `Perm[${this.p.join(" ")}]`;
|
|
794
|
+
}
|
|
795
|
+
mul(p2) {
|
|
796
|
+
const c = Array(this.n);
|
|
797
|
+
for (let i = 0; i < this.n; i++) {
|
|
798
|
+
c[i] = p2.p[this.p[i]];
|
|
799
|
+
}
|
|
800
|
+
return new _Perm(c);
|
|
801
|
+
}
|
|
802
|
+
rmul(p2) {
|
|
803
|
+
const c = Array(this.n);
|
|
804
|
+
for (let i = 0; i < this.n; i++) {
|
|
805
|
+
c[i] = this.p[p2.p[i]];
|
|
806
|
+
}
|
|
807
|
+
return new _Perm(c);
|
|
808
|
+
}
|
|
809
|
+
inv() {
|
|
810
|
+
const c = Array(this.n);
|
|
811
|
+
for (let i = 0; i < this.n; i++) {
|
|
812
|
+
c[this.p[i]] = i;
|
|
813
|
+
}
|
|
814
|
+
return new _Perm(c);
|
|
815
|
+
}
|
|
816
|
+
compareTo(p2) {
|
|
817
|
+
for (let i = 0; i < this.n; i++) {
|
|
818
|
+
if (this.p[i] !== p2.p[i]) {
|
|
819
|
+
return this.p[i] - p2.p[i];
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
return 0;
|
|
823
|
+
}
|
|
824
|
+
toGap() {
|
|
825
|
+
const cyc = [];
|
|
826
|
+
const seen = new Array(this.n);
|
|
827
|
+
for (let i = 0; i < this.p.length; i++) {
|
|
828
|
+
if (seen[i] || this.p[i] === i) {
|
|
829
|
+
continue;
|
|
830
|
+
}
|
|
831
|
+
const incyc = [];
|
|
832
|
+
for (let j = this.p[i]; !seen[j]; j = this.p[j]) {
|
|
833
|
+
incyc.push(1 + j);
|
|
834
|
+
seen[j] = true;
|
|
835
|
+
}
|
|
836
|
+
cyc.push(`(${incyc.reverse().join(",")})`);
|
|
837
|
+
}
|
|
838
|
+
return cyc.join("");
|
|
839
|
+
}
|
|
840
|
+
toMathematica() {
|
|
841
|
+
const cyc = [];
|
|
842
|
+
const seen = new Array(this.n);
|
|
843
|
+
for (let i = 0; i < this.p.length; i++) {
|
|
844
|
+
if (seen[i] || this.p[i] === i) {
|
|
845
|
+
continue;
|
|
846
|
+
}
|
|
847
|
+
const incyc = [];
|
|
848
|
+
for (let j = this.p[i]; !seen[j]; j = this.p[j]) {
|
|
849
|
+
incyc.push(1 + j);
|
|
850
|
+
seen[j] = true;
|
|
851
|
+
}
|
|
852
|
+
cyc.push(`{${incyc.reverse().join(",")}}`);
|
|
853
|
+
}
|
|
854
|
+
return `Cycles[{${cyc.join(",")}}]`;
|
|
855
|
+
}
|
|
856
|
+
order() {
|
|
857
|
+
let r = 1;
|
|
858
|
+
const seen = new Array(this.n);
|
|
859
|
+
for (let i = 0; i < this.p.length; i++) {
|
|
860
|
+
if (seen[i] || this.p[i] === i) {
|
|
861
|
+
continue;
|
|
862
|
+
}
|
|
863
|
+
let cs = 0;
|
|
864
|
+
for (let j = i; !seen[j]; j = this.p[j]) {
|
|
865
|
+
cs++;
|
|
866
|
+
seen[j] = true;
|
|
867
|
+
}
|
|
868
|
+
r = lcm(r, cs);
|
|
869
|
+
}
|
|
870
|
+
return r;
|
|
871
|
+
}
|
|
872
|
+
};
|
|
873
|
+
|
|
874
|
+
// src/cubing/puzzle-geometry/PermOriSet.ts
|
|
875
|
+
var PGOrbitDef = class {
|
|
876
|
+
constructor(size, mod) {
|
|
877
|
+
this.size = size;
|
|
878
|
+
this.mod = mod;
|
|
879
|
+
}
|
|
880
|
+
size;
|
|
881
|
+
mod;
|
|
882
|
+
reassemblySize() {
|
|
883
|
+
return factorial(this.size) * BigInt(this.mod) ** BigInt(this.size);
|
|
884
|
+
}
|
|
885
|
+
};
|
|
886
|
+
var lastGlobalDefinitionCounter = 0;
|
|
887
|
+
function externalName(mapper, moveString) {
|
|
888
|
+
const mv = Move.fromString(moveString);
|
|
889
|
+
const mv2 = mapper.notationToExternal(mv);
|
|
890
|
+
if (mv2 === null || mv === mv2) {
|
|
891
|
+
return moveString;
|
|
892
|
+
}
|
|
893
|
+
return mv2.toString();
|
|
894
|
+
}
|
|
895
|
+
var PGOrbitsDef = class _PGOrbitsDef {
|
|
896
|
+
constructor(orbitnames, orbitdefs, solved, movenames, moveops, isRotation, forcenames) {
|
|
897
|
+
this.orbitnames = orbitnames;
|
|
898
|
+
this.orbitdefs = orbitdefs;
|
|
899
|
+
this.solved = solved;
|
|
900
|
+
this.movenames = movenames;
|
|
901
|
+
this.moveops = moveops;
|
|
902
|
+
this.isRotation = isRotation;
|
|
903
|
+
this.forcenames = forcenames;
|
|
904
|
+
}
|
|
905
|
+
orbitnames;
|
|
906
|
+
orbitdefs;
|
|
907
|
+
solved;
|
|
908
|
+
movenames;
|
|
909
|
+
moveops;
|
|
910
|
+
isRotation;
|
|
911
|
+
forcenames;
|
|
912
|
+
toKTransformationData(t) {
|
|
913
|
+
const ktransformationData = {};
|
|
914
|
+
for (let i = 0; i < this.orbitnames.length; i++) {
|
|
915
|
+
ktransformationData[this.orbitnames[i]] = t.orbits[i].toKTransformationOrbitData();
|
|
916
|
+
}
|
|
917
|
+
return ktransformationData;
|
|
918
|
+
}
|
|
919
|
+
toKPatternData(t) {
|
|
920
|
+
const kpatternData = {};
|
|
921
|
+
for (let i = 0; i < this.orbitnames.length; i++) {
|
|
922
|
+
kpatternData[this.orbitnames[i]] = t.orbits[i].toKPatternOrbitData();
|
|
923
|
+
}
|
|
924
|
+
return kpatternData;
|
|
925
|
+
}
|
|
926
|
+
// TODO: remove this
|
|
927
|
+
static transformToKTransformationData(orbitnames, t) {
|
|
928
|
+
const mp = {};
|
|
929
|
+
for (let j = 0; j < orbitnames.length; j++) {
|
|
930
|
+
mp[orbitnames[j]] = t.orbits[j].toKTransformationOrbitData();
|
|
931
|
+
}
|
|
932
|
+
return mp;
|
|
933
|
+
}
|
|
934
|
+
describeSet(s, r, mapper) {
|
|
935
|
+
const n = this.orbitdefs[s].size;
|
|
936
|
+
const m = new Array(n);
|
|
937
|
+
for (let i = 0; i < n; i++) {
|
|
938
|
+
m[i] = [];
|
|
939
|
+
}
|
|
940
|
+
for (let i = 0; i < this.movenames.length; i++) {
|
|
941
|
+
if (this.isRotation[i]) {
|
|
942
|
+
continue;
|
|
943
|
+
}
|
|
944
|
+
let mvname = this.movenames[i];
|
|
945
|
+
if (!this.forcenames[i]) {
|
|
946
|
+
mvname = externalName(mapper, mvname);
|
|
947
|
+
if (mvname[mvname.length - 1] === "'") {
|
|
948
|
+
mvname = mvname.substring(0, mvname.length - 1);
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
const pd = this.moveops[i].orbits[s];
|
|
952
|
+
for (let j = 0; j < n; j++) {
|
|
953
|
+
if (pd.perm[j] !== j || pd.ori[j] !== 0) {
|
|
954
|
+
m[j].push(mvname);
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
for (let j = 0; j < n; j++) {
|
|
959
|
+
r.push(`# ${j + 1} ${m[j].join(" ")}`);
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
toKsolve(name, mapper = new NullMapper()) {
|
|
963
|
+
const result = [];
|
|
964
|
+
result.push(`Name ${name}`);
|
|
965
|
+
result.push("");
|
|
966
|
+
for (let i = 0; i < this.orbitnames.length; i++) {
|
|
967
|
+
result.push(
|
|
968
|
+
`Set ${this.orbitnames[i]} ${this.orbitdefs[i].size} ${this.orbitdefs[i].mod}`
|
|
969
|
+
);
|
|
970
|
+
this.describeSet(i, result, mapper);
|
|
971
|
+
}
|
|
972
|
+
result.push("");
|
|
973
|
+
result.push("Solved");
|
|
974
|
+
for (let i = 0; i < this.orbitnames.length; i++) {
|
|
975
|
+
this.solved.orbits[i].appendDefinition(
|
|
976
|
+
result,
|
|
977
|
+
this.orbitnames[i],
|
|
978
|
+
false,
|
|
979
|
+
false
|
|
980
|
+
);
|
|
981
|
+
}
|
|
982
|
+
result.push("End");
|
|
983
|
+
for (let i = 0; i < this.movenames.length; i++) {
|
|
984
|
+
result.push("");
|
|
985
|
+
let name2 = this.movenames[i];
|
|
986
|
+
if (!this.forcenames[i]) {
|
|
987
|
+
name2 = externalName(mapper, this.movenames[i]);
|
|
988
|
+
}
|
|
989
|
+
let doinv = false;
|
|
990
|
+
if (name2[name2.length - 1] === "'") {
|
|
991
|
+
doinv = true;
|
|
992
|
+
name2 = name2.substring(0, name2.length - 1);
|
|
993
|
+
}
|
|
994
|
+
result.push(`Move ${name2}`);
|
|
995
|
+
for (let j = 0; j < this.orbitnames.length; j++) {
|
|
996
|
+
if (doinv) {
|
|
997
|
+
this.moveops[i].orbits[j].inv().appendDefinition(result, this.orbitnames[j], true);
|
|
998
|
+
} else {
|
|
999
|
+
this.moveops[i].orbits[j].appendDefinition(
|
|
1000
|
+
result,
|
|
1001
|
+
this.orbitnames[j],
|
|
1002
|
+
true
|
|
1003
|
+
);
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
result.push("End");
|
|
1007
|
+
}
|
|
1008
|
+
return result;
|
|
1009
|
+
}
|
|
1010
|
+
// TODO: return type.
|
|
1011
|
+
toKPuzzleDefinition(includemoves) {
|
|
1012
|
+
const orbits = [];
|
|
1013
|
+
const defaultPatternData = {};
|
|
1014
|
+
for (let i = 0; i < this.orbitnames.length; i++) {
|
|
1015
|
+
orbits.push({
|
|
1016
|
+
orbitName: this.orbitnames[i],
|
|
1017
|
+
numPieces: this.orbitdefs[i].size,
|
|
1018
|
+
numOrientations: this.orbitdefs[i].mod
|
|
1019
|
+
});
|
|
1020
|
+
const defaultPatternFrom = this.solved.orbits[i].toKTransformationOrbitData();
|
|
1021
|
+
defaultPatternData[this.orbitnames[i]] = {
|
|
1022
|
+
pieces: defaultPatternFrom.permutation,
|
|
1023
|
+
orientation: defaultPatternFrom.orientationDelta
|
|
1024
|
+
};
|
|
1025
|
+
}
|
|
1026
|
+
const moves = {};
|
|
1027
|
+
if (includemoves) {
|
|
1028
|
+
for (let i = 0; i < this.movenames.length; i++) {
|
|
1029
|
+
moves[this.movenames[i]] = this.toKTransformationData(this.moveops[i]);
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
return {
|
|
1033
|
+
name: `PG3D #${++lastGlobalDefinitionCounter}`,
|
|
1034
|
+
orbits,
|
|
1035
|
+
defaultPattern: defaultPatternData,
|
|
1036
|
+
moves
|
|
1037
|
+
};
|
|
1038
|
+
}
|
|
1039
|
+
optimize() {
|
|
1040
|
+
const neworbitnames = [];
|
|
1041
|
+
const neworbitdefs = [];
|
|
1042
|
+
const newsolved = [];
|
|
1043
|
+
const newmoveops = [];
|
|
1044
|
+
for (let j = 0; j < this.moveops.length; j++) {
|
|
1045
|
+
newmoveops.push([]);
|
|
1046
|
+
}
|
|
1047
|
+
for (let i = 0; i < this.orbitdefs.length; i++) {
|
|
1048
|
+
const om = this.orbitdefs[i].mod;
|
|
1049
|
+
const n = this.orbitdefs[i].size;
|
|
1050
|
+
const du = new DisjointUnion(n);
|
|
1051
|
+
const changed = new Array(this.orbitdefs[i].size);
|
|
1052
|
+
for (let k = 0; k < n; k++) {
|
|
1053
|
+
changed[k] = false;
|
|
1054
|
+
}
|
|
1055
|
+
for (let j = 0; j < this.moveops.length; j++) {
|
|
1056
|
+
for (let k = 0; k < n; k++) {
|
|
1057
|
+
if (this.moveops[j].orbits[i].perm[k] !== k || this.moveops[j].orbits[i].ori[k] !== 0) {
|
|
1058
|
+
if (!this.isRotation[j]) {
|
|
1059
|
+
changed[k] = true;
|
|
1060
|
+
}
|
|
1061
|
+
du.union(k, this.moveops[j].orbits[i].perm[k]);
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
let keepori = true;
|
|
1066
|
+
if (om > 1) {
|
|
1067
|
+
keepori = false;
|
|
1068
|
+
const duo = new DisjointUnion(this.orbitdefs[i].size * om);
|
|
1069
|
+
for (let j = 0; j < this.moveops.length; j++) {
|
|
1070
|
+
for (let k = 0; k < n; k++) {
|
|
1071
|
+
if (this.moveops[j].orbits[i].perm[k] !== k || this.moveops[j].orbits[i].ori[k] !== 0) {
|
|
1072
|
+
for (let o = 0; o < om; o++) {
|
|
1073
|
+
duo.union(
|
|
1074
|
+
k * om + o,
|
|
1075
|
+
this.moveops[j].orbits[i].perm[k] * om + (o + this.moveops[j].orbits[i].ori[k]) % om
|
|
1076
|
+
);
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
for (let j = 0; !keepori && j < n; j++) {
|
|
1082
|
+
for (let o = 1; o < om; o++) {
|
|
1083
|
+
if (duo.find(j * om) === duo.find(j * om + o)) {
|
|
1084
|
+
keepori = true;
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
for (let j = 0; !keepori && j < n; j++) {
|
|
1089
|
+
for (let k = 0; k < j; k++) {
|
|
1090
|
+
if (this.solved.orbits[i].perm[j] === this.solved.orbits[i].perm[k]) {
|
|
1091
|
+
keepori = true;
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
let nontriv = -1;
|
|
1097
|
+
let multiple = false;
|
|
1098
|
+
for (let j = 0; j < this.orbitdefs[i].size; j++) {
|
|
1099
|
+
if (changed[j]) {
|
|
1100
|
+
const h = du.find(j);
|
|
1101
|
+
if (nontriv < 0) {
|
|
1102
|
+
nontriv = h;
|
|
1103
|
+
} else if (nontriv !== h) {
|
|
1104
|
+
multiple = true;
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
for (let j = 0; j < this.orbitdefs[i].size; j++) {
|
|
1109
|
+
if (!changed[j]) {
|
|
1110
|
+
continue;
|
|
1111
|
+
}
|
|
1112
|
+
const h = du.find(j);
|
|
1113
|
+
if (h !== j) {
|
|
1114
|
+
continue;
|
|
1115
|
+
}
|
|
1116
|
+
const no = [];
|
|
1117
|
+
const on = [];
|
|
1118
|
+
let nv = 0;
|
|
1119
|
+
for (let k = 0; k < this.orbitdefs[i].size; k++) {
|
|
1120
|
+
if (du.find(k) === j) {
|
|
1121
|
+
no[nv] = k;
|
|
1122
|
+
on[k] = nv;
|
|
1123
|
+
nv++;
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
if (multiple) {
|
|
1127
|
+
neworbitnames.push(`${this.orbitnames[i]}_p${j}`);
|
|
1128
|
+
} else {
|
|
1129
|
+
neworbitnames.push(this.orbitnames[i]);
|
|
1130
|
+
}
|
|
1131
|
+
if (keepori) {
|
|
1132
|
+
neworbitdefs.push(new PGOrbitDef(nv, this.orbitdefs[i].mod));
|
|
1133
|
+
newsolved.push(this.solved.orbits[i].remapVS(no, nv));
|
|
1134
|
+
for (let k = 0; k < this.moveops.length; k++) {
|
|
1135
|
+
newmoveops[k].push(this.moveops[k].orbits[i].remap(no, on, nv));
|
|
1136
|
+
}
|
|
1137
|
+
} else {
|
|
1138
|
+
neworbitdefs.push(new PGOrbitDef(nv, 1));
|
|
1139
|
+
newsolved.push(this.solved.orbits[i].remapVS(no, nv).killOri());
|
|
1140
|
+
for (let k = 0; k < this.moveops.length; k++) {
|
|
1141
|
+
newmoveops[k].push(
|
|
1142
|
+
this.moveops[k].orbits[i].remap(no, on, nv).killOri()
|
|
1143
|
+
);
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
return new _PGOrbitsDef(
|
|
1149
|
+
neworbitnames,
|
|
1150
|
+
neworbitdefs,
|
|
1151
|
+
new VisibleState(newsolved),
|
|
1152
|
+
this.movenames,
|
|
1153
|
+
newmoveops.map((_) => new PGTransform(_)),
|
|
1154
|
+
this.isRotation,
|
|
1155
|
+
this.forcenames
|
|
1156
|
+
);
|
|
1157
|
+
}
|
|
1158
|
+
// replace the solved state with a new scrambled state.
|
|
1159
|
+
scramble(n) {
|
|
1160
|
+
this.solved = this.solved.mul(this.getScrambleTransformation(n));
|
|
1161
|
+
}
|
|
1162
|
+
// generate a new "random" position based on an entropy pool
|
|
1163
|
+
// this should be significantly faster and more random than just
|
|
1164
|
+
// doing a large number of random moves, especially on big puzzles.
|
|
1165
|
+
getScrambleTransformation(n) {
|
|
1166
|
+
if (n < 100) {
|
|
1167
|
+
n = 100;
|
|
1168
|
+
}
|
|
1169
|
+
const pool = [];
|
|
1170
|
+
for (let i = 0; i < this.moveops.length; i++) {
|
|
1171
|
+
pool[i] = this.moveops[i];
|
|
1172
|
+
}
|
|
1173
|
+
for (let i = 0; i < pool.length; i++) {
|
|
1174
|
+
const j = Math.floor(Math.random() * pool.length);
|
|
1175
|
+
const t = pool[i];
|
|
1176
|
+
pool[i] = pool[j];
|
|
1177
|
+
pool[j] = t;
|
|
1178
|
+
}
|
|
1179
|
+
if (n < pool.length) {
|
|
1180
|
+
n = pool.length;
|
|
1181
|
+
}
|
|
1182
|
+
for (let i = 0; i < n; i++) {
|
|
1183
|
+
const ri = Math.floor(Math.random() * pool.length);
|
|
1184
|
+
const rj = Math.floor(Math.random() * pool.length);
|
|
1185
|
+
const rm = Math.floor(Math.random() * this.moveops.length);
|
|
1186
|
+
pool[ri] = pool[ri].mul(pool[rj]).mul(this.moveops[rm]);
|
|
1187
|
+
if (Math.random() < 0.1) {
|
|
1188
|
+
pool[ri] = pool[ri].mul(this.moveops[rm]);
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
let s = pool[0];
|
|
1192
|
+
for (let i = 1; i < pool.length; i++) {
|
|
1193
|
+
s = s.mul(pool[i]);
|
|
1194
|
+
}
|
|
1195
|
+
return s;
|
|
1196
|
+
}
|
|
1197
|
+
reassemblySize() {
|
|
1198
|
+
let n = BigInt(1);
|
|
1199
|
+
for (let i = 0; i < this.orbitdefs.length; i++) {
|
|
1200
|
+
n *= this.orbitdefs[i].reassemblySize();
|
|
1201
|
+
}
|
|
1202
|
+
return n;
|
|
1203
|
+
}
|
|
1204
|
+
};
|
|
1205
|
+
var PGOrbit = class _PGOrbit {
|
|
1206
|
+
constructor(perm, ori, orimod) {
|
|
1207
|
+
this.perm = perm;
|
|
1208
|
+
this.ori = ori;
|
|
1209
|
+
this.orimod = orimod;
|
|
1210
|
+
}
|
|
1211
|
+
perm;
|
|
1212
|
+
ori;
|
|
1213
|
+
orimod;
|
|
1214
|
+
static ktransformationCache = [];
|
|
1215
|
+
static e(n, mod) {
|
|
1216
|
+
return new _PGOrbit(iota(n), zeros(n), mod);
|
|
1217
|
+
}
|
|
1218
|
+
mul(b) {
|
|
1219
|
+
const n = this.perm.length;
|
|
1220
|
+
const newPerm = new Array(n);
|
|
1221
|
+
if (this.orimod === 1) {
|
|
1222
|
+
for (let i = 0; i < n; i++) {
|
|
1223
|
+
newPerm[i] = this.perm[b.perm[i]];
|
|
1224
|
+
}
|
|
1225
|
+
return new _PGOrbit(newPerm, this.ori, this.orimod);
|
|
1226
|
+
} else {
|
|
1227
|
+
const newOri = new Array(n);
|
|
1228
|
+
for (let i = 0; i < n; i++) {
|
|
1229
|
+
newPerm[i] = this.perm[b.perm[i]];
|
|
1230
|
+
newOri[i] = (this.ori[b.perm[i]] + b.ori[i]) % this.orimod;
|
|
1231
|
+
}
|
|
1232
|
+
return new _PGOrbit(newPerm, newOri, this.orimod);
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
inv() {
|
|
1236
|
+
const n = this.perm.length;
|
|
1237
|
+
const newPerm = new Array(n);
|
|
1238
|
+
const newOri = new Array(n);
|
|
1239
|
+
for (let i = 0; i < n; i++) {
|
|
1240
|
+
newPerm[this.perm[i]] = i;
|
|
1241
|
+
newOri[this.perm[i]] = (this.orimod - this.ori[i]) % this.orimod;
|
|
1242
|
+
}
|
|
1243
|
+
return new _PGOrbit(newPerm, newOri, this.orimod);
|
|
1244
|
+
}
|
|
1245
|
+
equal(b) {
|
|
1246
|
+
const n = this.perm.length;
|
|
1247
|
+
for (let i = 0; i < n; i++) {
|
|
1248
|
+
if (this.perm[i] !== b.perm[i] || this.ori[i] !== b.ori[i]) {
|
|
1249
|
+
return false;
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
return true;
|
|
1253
|
+
}
|
|
1254
|
+
// in-place mutator
|
|
1255
|
+
killOri() {
|
|
1256
|
+
const n = this.perm.length;
|
|
1257
|
+
for (let i = 0; i < n; i++) {
|
|
1258
|
+
this.ori[i] = 0;
|
|
1259
|
+
}
|
|
1260
|
+
this.orimod = 1;
|
|
1261
|
+
return this;
|
|
1262
|
+
}
|
|
1263
|
+
toPerm() {
|
|
1264
|
+
const o = this.orimod;
|
|
1265
|
+
if (o === 1) {
|
|
1266
|
+
return new Perm(this.perm);
|
|
1267
|
+
}
|
|
1268
|
+
const n = this.perm.length;
|
|
1269
|
+
const newPerm = new Array(n * o);
|
|
1270
|
+
for (let i = 0; i < n; i++) {
|
|
1271
|
+
for (let j = 0; j < o; j++) {
|
|
1272
|
+
newPerm[i * o + j] = o * this.perm[i] + (this.ori[i] + j) % o;
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
return new Perm(newPerm);
|
|
1276
|
+
}
|
|
1277
|
+
// returns tuple of sets of identical pieces in this orbit
|
|
1278
|
+
identicalPieces() {
|
|
1279
|
+
const done = [];
|
|
1280
|
+
const n = this.perm.length;
|
|
1281
|
+
const r = [];
|
|
1282
|
+
for (let i = 0; i < n; i++) {
|
|
1283
|
+
const v = this.perm[i];
|
|
1284
|
+
if (done[v] === void 0) {
|
|
1285
|
+
const s = [i];
|
|
1286
|
+
done[v] = true;
|
|
1287
|
+
for (let j = i + 1; j < n; j++) {
|
|
1288
|
+
if (this.perm[j] === v) {
|
|
1289
|
+
s.push(j);
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
r.push(s);
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
return r;
|
|
1296
|
+
}
|
|
1297
|
+
order() {
|
|
1298
|
+
return this.toPerm().order();
|
|
1299
|
+
}
|
|
1300
|
+
isIdentity() {
|
|
1301
|
+
const n = this.perm.length;
|
|
1302
|
+
if (this.perm === iota(n) && this.ori === zeros(n)) {
|
|
1303
|
+
return true;
|
|
1304
|
+
}
|
|
1305
|
+
for (let i = 0; i < n; i++) {
|
|
1306
|
+
if (this.perm[i] !== i || this.ori[i] !== 0) {
|
|
1307
|
+
return false;
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
return true;
|
|
1311
|
+
}
|
|
1312
|
+
zeroOris() {
|
|
1313
|
+
const n = this.perm.length;
|
|
1314
|
+
if (this.ori === zeros(n)) {
|
|
1315
|
+
return true;
|
|
1316
|
+
}
|
|
1317
|
+
for (let i = 0; i < n; i++) {
|
|
1318
|
+
if (this.ori[i] !== 0) {
|
|
1319
|
+
return false;
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
return true;
|
|
1323
|
+
}
|
|
1324
|
+
remap(no, on, nv) {
|
|
1325
|
+
const newPerm = new Array(nv);
|
|
1326
|
+
const newOri = new Array(nv);
|
|
1327
|
+
for (let i = 0; i < nv; i++) {
|
|
1328
|
+
newPerm[i] = on[this.perm[no[i]]];
|
|
1329
|
+
newOri[i] = this.ori[no[i]];
|
|
1330
|
+
}
|
|
1331
|
+
return new _PGOrbit(newPerm, newOri, this.orimod);
|
|
1332
|
+
}
|
|
1333
|
+
remapVS(no, nv) {
|
|
1334
|
+
const newPerm = new Array(nv);
|
|
1335
|
+
const newOri = new Array(nv);
|
|
1336
|
+
let nextNew = 0;
|
|
1337
|
+
const reassign = [];
|
|
1338
|
+
for (let i = 0; i < nv; i++) {
|
|
1339
|
+
const ov = this.perm[no[i]];
|
|
1340
|
+
if (reassign[ov] === void 0) {
|
|
1341
|
+
reassign[ov] = nextNew++;
|
|
1342
|
+
}
|
|
1343
|
+
newPerm[i] = reassign[ov];
|
|
1344
|
+
newOri[i] = this.ori[no[i]];
|
|
1345
|
+
}
|
|
1346
|
+
return new _PGOrbit(newPerm, newOri, this.orimod);
|
|
1347
|
+
}
|
|
1348
|
+
appendDefinition(result, name, useVS, concise = true) {
|
|
1349
|
+
if (concise && this.isIdentity()) {
|
|
1350
|
+
return;
|
|
1351
|
+
}
|
|
1352
|
+
result.push(name);
|
|
1353
|
+
result.push(this.perm.map((_) => _ + 1).join(" "));
|
|
1354
|
+
if (!this.zeroOris()) {
|
|
1355
|
+
if (useVS) {
|
|
1356
|
+
const newori = new Array(this.ori.length);
|
|
1357
|
+
for (let i = 0; i < newori.length; i++) {
|
|
1358
|
+
newori[this.perm[i]] = this.ori[i];
|
|
1359
|
+
}
|
|
1360
|
+
result.push(newori.join(" "));
|
|
1361
|
+
} else {
|
|
1362
|
+
result.push(this.ori.join(" "));
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
toKTransformationOrbitData() {
|
|
1367
|
+
const n = this.perm.length;
|
|
1368
|
+
if (this.isIdentity()) {
|
|
1369
|
+
if (!_PGOrbit.ktransformationCache[n]) {
|
|
1370
|
+
_PGOrbit.ktransformationCache[n] = {
|
|
1371
|
+
permutation: iota(n),
|
|
1372
|
+
orientationDelta: zeros(n)
|
|
1373
|
+
};
|
|
1374
|
+
}
|
|
1375
|
+
return _PGOrbit.ktransformationCache[n];
|
|
1376
|
+
} else {
|
|
1377
|
+
return { permutation: this.perm, orientationDelta: this.ori };
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
toKPatternOrbitData() {
|
|
1381
|
+
const n = this.perm.length;
|
|
1382
|
+
return {
|
|
1383
|
+
pieces: this.perm,
|
|
1384
|
+
orientation: this.ori,
|
|
1385
|
+
orientationMod: zeros(n)
|
|
1386
|
+
};
|
|
1387
|
+
}
|
|
1388
|
+
};
|
|
1389
|
+
var PGTransformBase = class {
|
|
1390
|
+
constructor(orbits) {
|
|
1391
|
+
this.orbits = orbits;
|
|
1392
|
+
}
|
|
1393
|
+
orbits;
|
|
1394
|
+
internalMul(b) {
|
|
1395
|
+
const newOrbits = [];
|
|
1396
|
+
for (let i = 0; i < this.orbits.length; i++) {
|
|
1397
|
+
newOrbits.push(this.orbits[i].mul(b.orbits[i]));
|
|
1398
|
+
}
|
|
1399
|
+
return newOrbits;
|
|
1400
|
+
}
|
|
1401
|
+
internalInv() {
|
|
1402
|
+
const newOrbits = [];
|
|
1403
|
+
for (const orbit of this.orbits) {
|
|
1404
|
+
newOrbits.push(orbit.inv());
|
|
1405
|
+
}
|
|
1406
|
+
return newOrbits;
|
|
1407
|
+
}
|
|
1408
|
+
equal(b) {
|
|
1409
|
+
for (let i = 0; i < this.orbits.length; i++) {
|
|
1410
|
+
if (!this.orbits[i].equal(b.orbits[i])) {
|
|
1411
|
+
return false;
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
return true;
|
|
1415
|
+
}
|
|
1416
|
+
killOri() {
|
|
1417
|
+
for (const orbit of this.orbits) {
|
|
1418
|
+
orbit.killOri();
|
|
1419
|
+
}
|
|
1420
|
+
return this;
|
|
1421
|
+
}
|
|
1422
|
+
toPerm() {
|
|
1423
|
+
const perms = [];
|
|
1424
|
+
let n = 0;
|
|
1425
|
+
for (const orbit of this.orbits) {
|
|
1426
|
+
const p = orbit.toPerm();
|
|
1427
|
+
perms.push(p);
|
|
1428
|
+
n += p.n;
|
|
1429
|
+
}
|
|
1430
|
+
const newPerm = new Array(n);
|
|
1431
|
+
n = 0;
|
|
1432
|
+
for (const p of perms) {
|
|
1433
|
+
for (let j = 0; j < p.n; j++) {
|
|
1434
|
+
newPerm[n + j] = n + p.p[j];
|
|
1435
|
+
}
|
|
1436
|
+
n += p.n;
|
|
1437
|
+
}
|
|
1438
|
+
return new Perm(newPerm);
|
|
1439
|
+
}
|
|
1440
|
+
identicalPieces() {
|
|
1441
|
+
const r = [];
|
|
1442
|
+
let n = 0;
|
|
1443
|
+
for (const orbit of this.orbits) {
|
|
1444
|
+
const o = orbit.orimod;
|
|
1445
|
+
const s = orbit.identicalPieces();
|
|
1446
|
+
for (let j = 0; j < s.length; j++) {
|
|
1447
|
+
r.push(s[j].map((_) => _ * o + n));
|
|
1448
|
+
}
|
|
1449
|
+
n += o * orbit.perm.length;
|
|
1450
|
+
}
|
|
1451
|
+
return r;
|
|
1452
|
+
}
|
|
1453
|
+
order() {
|
|
1454
|
+
let r = 1;
|
|
1455
|
+
for (const orbit of this.orbits) {
|
|
1456
|
+
r = lcm(r, orbit.order());
|
|
1457
|
+
}
|
|
1458
|
+
return r;
|
|
1459
|
+
}
|
|
1460
|
+
};
|
|
1461
|
+
var PGTransform = class _PGTransform extends PGTransformBase {
|
|
1462
|
+
mul(b) {
|
|
1463
|
+
return new _PGTransform(this.internalMul(b));
|
|
1464
|
+
}
|
|
1465
|
+
mulScalar(n) {
|
|
1466
|
+
if (n === 0) {
|
|
1467
|
+
return this.e();
|
|
1468
|
+
}
|
|
1469
|
+
let t = this;
|
|
1470
|
+
if (n < 0) {
|
|
1471
|
+
t = t.inv();
|
|
1472
|
+
n = -n;
|
|
1473
|
+
}
|
|
1474
|
+
while ((n & 1) === 0) {
|
|
1475
|
+
t = t.mul(t);
|
|
1476
|
+
n >>= 1;
|
|
1477
|
+
}
|
|
1478
|
+
if (n === 1) {
|
|
1479
|
+
return t;
|
|
1480
|
+
}
|
|
1481
|
+
let s = t;
|
|
1482
|
+
let r = this.e();
|
|
1483
|
+
while (n > 0) {
|
|
1484
|
+
if (n & 1) {
|
|
1485
|
+
r = r.mul(s);
|
|
1486
|
+
}
|
|
1487
|
+
if (n > 1) {
|
|
1488
|
+
s = s.mul(s);
|
|
1489
|
+
}
|
|
1490
|
+
n >>= 1;
|
|
1491
|
+
}
|
|
1492
|
+
return r;
|
|
1493
|
+
}
|
|
1494
|
+
inv() {
|
|
1495
|
+
return new _PGTransform(this.internalInv());
|
|
1496
|
+
}
|
|
1497
|
+
e() {
|
|
1498
|
+
return new _PGTransform(
|
|
1499
|
+
this.orbits.map((_) => PGOrbit.e(_.perm.length, _.orimod))
|
|
1500
|
+
);
|
|
1501
|
+
}
|
|
1502
|
+
};
|
|
1503
|
+
var VisibleState = class _VisibleState extends PGTransformBase {
|
|
1504
|
+
mul(b) {
|
|
1505
|
+
return new _VisibleState(this.internalMul(b));
|
|
1506
|
+
}
|
|
1507
|
+
};
|
|
1508
|
+
var DisjointUnion = class {
|
|
1509
|
+
constructor(n) {
|
|
1510
|
+
this.n = n;
|
|
1511
|
+
this.heads = new Array(n);
|
|
1512
|
+
for (let i = 0; i < n; i++) {
|
|
1513
|
+
this.heads[i] = i;
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
n;
|
|
1517
|
+
heads;
|
|
1518
|
+
find(v) {
|
|
1519
|
+
let h = this.heads[v];
|
|
1520
|
+
if (this.heads[h] === h) {
|
|
1521
|
+
return h;
|
|
1522
|
+
}
|
|
1523
|
+
h = this.find(this.heads[h]);
|
|
1524
|
+
this.heads[v] = h;
|
|
1525
|
+
return h;
|
|
1526
|
+
}
|
|
1527
|
+
union(a, b) {
|
|
1528
|
+
const ah = this.find(a);
|
|
1529
|
+
const bh = this.find(b);
|
|
1530
|
+
if (ah < bh) {
|
|
1531
|
+
this.heads[bh] = ah;
|
|
1532
|
+
} else if (ah > bh) {
|
|
1533
|
+
this.heads[ah] = bh;
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
};
|
|
1537
|
+
function showcanon(g, disp) {
|
|
1538
|
+
const n = g.moveops.length;
|
|
1539
|
+
if (n > 30) {
|
|
1540
|
+
throw new Error("Canon info too big for bitmask");
|
|
1541
|
+
}
|
|
1542
|
+
const orders = [];
|
|
1543
|
+
const commutes = [];
|
|
1544
|
+
for (let i = 0; i < n; i++) {
|
|
1545
|
+
const permA = g.moveops[i];
|
|
1546
|
+
orders.push(permA.order());
|
|
1547
|
+
let bits = 0;
|
|
1548
|
+
for (let j = 0; j < n; j++) {
|
|
1549
|
+
if (j === i) {
|
|
1550
|
+
continue;
|
|
1551
|
+
}
|
|
1552
|
+
const permB = g.moveops[j];
|
|
1553
|
+
if (permA.mul(permB).equal(permB.mul(permA))) {
|
|
1554
|
+
bits |= 1 << j;
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
commutes.push(bits);
|
|
1558
|
+
}
|
|
1559
|
+
let curlev = {};
|
|
1560
|
+
curlev[0] = 1;
|
|
1561
|
+
for (let d = 0; d < 100; d++) {
|
|
1562
|
+
let sum = 0;
|
|
1563
|
+
const nextlev = {};
|
|
1564
|
+
let uniq = 0;
|
|
1565
|
+
for (const sti in curlev) {
|
|
1566
|
+
const st = +sti;
|
|
1567
|
+
const cnt = curlev[st];
|
|
1568
|
+
sum += cnt;
|
|
1569
|
+
uniq++;
|
|
1570
|
+
for (let mv = 0; mv < orders.length; mv++) {
|
|
1571
|
+
if ((st >> mv & 1) === 0 && (st & commutes[mv] & (1 << mv) - 1) === 0) {
|
|
1572
|
+
const nst = st & commutes[mv] | 1 << mv;
|
|
1573
|
+
if (nextlev[nst] === void 0) {
|
|
1574
|
+
nextlev[nst] = 0;
|
|
1575
|
+
}
|
|
1576
|
+
nextlev[nst] += (orders[mv] - 1) * cnt;
|
|
1577
|
+
}
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1580
|
+
disp(`${d}: canonseq ${sum} states ${uniq}`);
|
|
1581
|
+
curlev = nextlev;
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
// src/cubing/puzzle-geometry/Quat.ts
|
|
1586
|
+
var eps = 1e-9;
|
|
1587
|
+
function centermassface(face) {
|
|
1588
|
+
let s = new Quat(0, 0, 0, 0);
|
|
1589
|
+
for (let i = 0; i < face.length; i++) {
|
|
1590
|
+
s = s.sum(face[i]);
|
|
1591
|
+
}
|
|
1592
|
+
return s.smul(1 / face.length);
|
|
1593
|
+
}
|
|
1594
|
+
function solvethreeplanes(p1, p2, p3, planes) {
|
|
1595
|
+
const p = planes[p1].intersect3(planes[p2], planes[p3]);
|
|
1596
|
+
if (!p) {
|
|
1597
|
+
return p;
|
|
1598
|
+
}
|
|
1599
|
+
for (let i = 0; i < planes.length; i++) {
|
|
1600
|
+
if (i !== p1 && i !== p2 && i !== p3) {
|
|
1601
|
+
const dt = planes[i].b * p.b + planes[i].c * p.c + planes[i].d * p.d;
|
|
1602
|
+
if (planes[i].a > 0 && dt > planes[i].a || planes[i].a < 0 && dt < planes[i].a) {
|
|
1603
|
+
return false;
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
return p;
|
|
1608
|
+
}
|
|
1609
|
+
var Quat = class _Quat {
|
|
1610
|
+
constructor(a, b, c, d) {
|
|
1611
|
+
this.a = a;
|
|
1612
|
+
this.b = b;
|
|
1613
|
+
this.c = c;
|
|
1614
|
+
this.d = d;
|
|
1615
|
+
}
|
|
1616
|
+
a;
|
|
1617
|
+
b;
|
|
1618
|
+
c;
|
|
1619
|
+
d;
|
|
1620
|
+
mul(q) {
|
|
1621
|
+
return new _Quat(
|
|
1622
|
+
this.a * q.a - this.b * q.b - this.c * q.c - this.d * q.d,
|
|
1623
|
+
this.a * q.b + this.b * q.a + this.c * q.d - this.d * q.c,
|
|
1624
|
+
this.a * q.c - this.b * q.d + this.c * q.a + this.d * q.b,
|
|
1625
|
+
this.a * q.d + this.b * q.c - this.c * q.b + this.d * q.a
|
|
1626
|
+
);
|
|
1627
|
+
}
|
|
1628
|
+
toString() {
|
|
1629
|
+
return `Q[${this.a},${this.b},${this.c},${this.d}]`;
|
|
1630
|
+
}
|
|
1631
|
+
dist(q) {
|
|
1632
|
+
return Math.hypot(this.a - q.a, this.b - q.b, this.c - q.c, this.d - q.d);
|
|
1633
|
+
}
|
|
1634
|
+
len() {
|
|
1635
|
+
return Math.hypot(this.a, this.b, this.c, this.d);
|
|
1636
|
+
}
|
|
1637
|
+
cross(q) {
|
|
1638
|
+
return new _Quat(
|
|
1639
|
+
0,
|
|
1640
|
+
this.c * q.d - this.d * q.c,
|
|
1641
|
+
this.d * q.b - this.b * q.d,
|
|
1642
|
+
this.b * q.c - this.c * q.b
|
|
1643
|
+
);
|
|
1644
|
+
}
|
|
1645
|
+
dot(q) {
|
|
1646
|
+
return this.b * q.b + this.c * q.c + this.d * q.d;
|
|
1647
|
+
}
|
|
1648
|
+
normalize() {
|
|
1649
|
+
const d = Math.sqrt(this.dot(this));
|
|
1650
|
+
return new _Quat(this.a / d, this.b / d, this.c / d, this.d / d);
|
|
1651
|
+
}
|
|
1652
|
+
makenormal() {
|
|
1653
|
+
return new _Quat(0, this.b, this.c, this.d).normalize();
|
|
1654
|
+
}
|
|
1655
|
+
normalizeplane() {
|
|
1656
|
+
const d = Math.hypot(this.b, this.c, this.d);
|
|
1657
|
+
return new _Quat(this.a / d, this.b / d, this.c / d, this.d / d);
|
|
1658
|
+
}
|
|
1659
|
+
smul(m) {
|
|
1660
|
+
return new _Quat(this.a * m, this.b * m, this.c * m, this.d * m);
|
|
1661
|
+
}
|
|
1662
|
+
sum(q) {
|
|
1663
|
+
return new _Quat(this.a + q.a, this.b + q.b, this.c + q.c, this.d + q.d);
|
|
1664
|
+
}
|
|
1665
|
+
sub(q) {
|
|
1666
|
+
return new _Quat(this.a - q.a, this.b - q.b, this.c - q.c, this.d - q.d);
|
|
1667
|
+
}
|
|
1668
|
+
angle() {
|
|
1669
|
+
return 2 * Math.acos(this.a);
|
|
1670
|
+
}
|
|
1671
|
+
invrot() {
|
|
1672
|
+
return new _Quat(this.a, -this.b, -this.c, -this.d);
|
|
1673
|
+
}
|
|
1674
|
+
det3x3(a00, a01, a02, a10, a11, a12, a20, a21, a22) {
|
|
1675
|
+
return a00 * (a11 * a22 - a12 * a21) + a01 * (a12 * a20 - a10 * a22) + a02 * (a10 * a21 - a11 * a20);
|
|
1676
|
+
}
|
|
1677
|
+
rotateplane(q) {
|
|
1678
|
+
const t = q.mul(new _Quat(0, this.b, this.c, this.d)).mul(q.invrot());
|
|
1679
|
+
t.a = this.a;
|
|
1680
|
+
return t;
|
|
1681
|
+
}
|
|
1682
|
+
// return any vector orthogonal to the given one. Find the smallest
|
|
1683
|
+
// component (in absolute value) and return the cross product of that
|
|
1684
|
+
// axis with the given vector.
|
|
1685
|
+
orthogonal() {
|
|
1686
|
+
const ab = Math.abs(this.b);
|
|
1687
|
+
const ac = Math.abs(this.c);
|
|
1688
|
+
const ad = Math.abs(this.d);
|
|
1689
|
+
if (ab < ac && ab < ad) {
|
|
1690
|
+
return this.cross(new _Quat(0, 1, 0, 0)).normalize();
|
|
1691
|
+
} else if (ac < ab && ac < ad) {
|
|
1692
|
+
return this.cross(new _Quat(0, 0, 1, 0)).normalize();
|
|
1693
|
+
} else {
|
|
1694
|
+
return this.cross(new _Quat(0, 0, 0, 1)).normalize();
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1697
|
+
// return the Quaternion that will rotate the this vector
|
|
1698
|
+
// to the b vector through rotatepoint.
|
|
1699
|
+
pointrotation(b) {
|
|
1700
|
+
const a = this.normalize();
|
|
1701
|
+
b = b.normalize();
|
|
1702
|
+
if (a.sub(b).len() < eps) {
|
|
1703
|
+
return new _Quat(1, 0, 0, 0);
|
|
1704
|
+
}
|
|
1705
|
+
let h = a.sum(b);
|
|
1706
|
+
if (h.len() < eps) {
|
|
1707
|
+
h = h.orthogonal();
|
|
1708
|
+
} else {
|
|
1709
|
+
h = h.normalize();
|
|
1710
|
+
}
|
|
1711
|
+
const r = a.cross(h);
|
|
1712
|
+
r.a = a.dot(h);
|
|
1713
|
+
return r;
|
|
1714
|
+
}
|
|
1715
|
+
// given two vectors, return the portion of the first that
|
|
1716
|
+
// is not in the direction of the second.
|
|
1717
|
+
unproject(b) {
|
|
1718
|
+
return this.sum(b.smul(-this.dot(b) / (this.len() * b.len())));
|
|
1719
|
+
}
|
|
1720
|
+
rotatepoint(q) {
|
|
1721
|
+
return q.mul(this).mul(q.invrot());
|
|
1722
|
+
}
|
|
1723
|
+
rotateface(face) {
|
|
1724
|
+
return face.map((_) => _.rotatepoint(this));
|
|
1725
|
+
}
|
|
1726
|
+
intersect3(p2, p3) {
|
|
1727
|
+
const det = this.det3x3(
|
|
1728
|
+
this.b,
|
|
1729
|
+
this.c,
|
|
1730
|
+
this.d,
|
|
1731
|
+
p2.b,
|
|
1732
|
+
p2.c,
|
|
1733
|
+
p2.d,
|
|
1734
|
+
p3.b,
|
|
1735
|
+
p3.c,
|
|
1736
|
+
p3.d
|
|
1737
|
+
);
|
|
1738
|
+
if (Math.abs(det) < eps) {
|
|
1739
|
+
return false;
|
|
1740
|
+
}
|
|
1741
|
+
return new _Quat(
|
|
1742
|
+
0,
|
|
1743
|
+
this.det3x3(this.a, this.c, this.d, p2.a, p2.c, p2.d, p3.a, p3.c, p3.d) / det,
|
|
1744
|
+
this.det3x3(this.b, this.a, this.d, p2.b, p2.a, p2.d, p3.b, p3.a, p3.d) / det,
|
|
1745
|
+
this.det3x3(this.b, this.c, this.a, p2.b, p2.c, p2.a, p3.b, p3.c, p3.a) / det
|
|
1746
|
+
);
|
|
1747
|
+
}
|
|
1748
|
+
side(x) {
|
|
1749
|
+
if (x > eps) {
|
|
1750
|
+
return 1;
|
|
1751
|
+
}
|
|
1752
|
+
if (x < -eps) {
|
|
1753
|
+
return -1;
|
|
1754
|
+
}
|
|
1755
|
+
return 0;
|
|
1756
|
+
}
|
|
1757
|
+
/**
|
|
1758
|
+
* Cuts a face by this plane, or returns null if there
|
|
1759
|
+
* is no intersection.
|
|
1760
|
+
* @param face The face to cut.
|
|
1761
|
+
*/
|
|
1762
|
+
cutface(face) {
|
|
1763
|
+
const d = this.a;
|
|
1764
|
+
let seen = 0;
|
|
1765
|
+
let r = null;
|
|
1766
|
+
for (let i = 0; i < face.length; i++) {
|
|
1767
|
+
seen |= 1 << this.side(face[i].dot(this) - d) + 1;
|
|
1768
|
+
}
|
|
1769
|
+
if ((seen & 5) === 5) {
|
|
1770
|
+
r = [];
|
|
1771
|
+
const inout = face.map((_) => this.side(_.dot(this) - d));
|
|
1772
|
+
for (let s = -1; s <= 1; s += 2) {
|
|
1773
|
+
const nface = [];
|
|
1774
|
+
for (let k = 0; k < face.length; k++) {
|
|
1775
|
+
if (inout[k] === s || inout[k] === 0) {
|
|
1776
|
+
nface.push(face[k]);
|
|
1777
|
+
}
|
|
1778
|
+
const kk = (k + 1) % face.length;
|
|
1779
|
+
if (inout[k] + inout[kk] === 0 && inout[k] !== 0) {
|
|
1780
|
+
const vk = face[k].dot(this) - d;
|
|
1781
|
+
const vkk = face[kk].dot(this) - d;
|
|
1782
|
+
const r2 = vk / (vk - vkk);
|
|
1783
|
+
const pt = face[k].smul(1 - r2).sum(face[kk].smul(r2));
|
|
1784
|
+
nface.push(pt);
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
r.push(nface);
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1790
|
+
return r;
|
|
1791
|
+
}
|
|
1792
|
+
cutfaces(faces) {
|
|
1793
|
+
const nfaces = [];
|
|
1794
|
+
for (let j = 0; j < faces.length; j++) {
|
|
1795
|
+
const face = faces[j];
|
|
1796
|
+
const t = this.cutface(face);
|
|
1797
|
+
if (t) {
|
|
1798
|
+
nfaces.push(t[0]);
|
|
1799
|
+
nfaces.push(t[1]);
|
|
1800
|
+
} else {
|
|
1801
|
+
nfaces.push(face);
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1804
|
+
return nfaces;
|
|
1805
|
+
}
|
|
1806
|
+
faceside(face) {
|
|
1807
|
+
const d = this.a;
|
|
1808
|
+
for (let i = 0; i < face.length; i++) {
|
|
1809
|
+
const s = this.side(face[i].dot(this) - d);
|
|
1810
|
+
if (s !== 0) {
|
|
1811
|
+
return s;
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
throw new Error("Could not determine side of plane in faceside");
|
|
1815
|
+
}
|
|
1816
|
+
sameplane(p) {
|
|
1817
|
+
const a = this.normalize();
|
|
1818
|
+
const b = p.normalize();
|
|
1819
|
+
return a.dist(b) < eps || a.dist(b.smul(-1)) < eps;
|
|
1820
|
+
}
|
|
1821
|
+
makecut(r) {
|
|
1822
|
+
return new _Quat(r, this.b, this.c, this.d);
|
|
1823
|
+
}
|
|
1824
|
+
};
|
|
1825
|
+
|
|
1826
|
+
// src/cubing/puzzle-geometry/PlatonicGenerator.ts
|
|
1827
|
+
var eps2 = 1e-9;
|
|
1828
|
+
function cube() {
|
|
1829
|
+
const s5 = Math.sqrt(0.5);
|
|
1830
|
+
return [new Quat(s5, s5, 0, 0), new Quat(s5, 0, s5, 0)];
|
|
1831
|
+
}
|
|
1832
|
+
function tetrahedron() {
|
|
1833
|
+
return [new Quat(0.5, 0.5, 0.5, 0.5), new Quat(0.5, 0.5, 0.5, -0.5)];
|
|
1834
|
+
}
|
|
1835
|
+
function dodecahedron() {
|
|
1836
|
+
const d36 = 2 * Math.PI / 10;
|
|
1837
|
+
let dx = 0.5 + 0.3 * Math.sqrt(5);
|
|
1838
|
+
let dy = 0.5 + 0.1 * Math.sqrt(5);
|
|
1839
|
+
const dd = Math.sqrt(dx * dx + dy * dy);
|
|
1840
|
+
dx /= dd;
|
|
1841
|
+
dy /= dd;
|
|
1842
|
+
return [
|
|
1843
|
+
new Quat(Math.cos(d36), dx * Math.sin(d36), dy * Math.sin(d36), 0),
|
|
1844
|
+
new Quat(0.5, 0.5, 0.5, 0.5)
|
|
1845
|
+
];
|
|
1846
|
+
}
|
|
1847
|
+
function icosahedron() {
|
|
1848
|
+
let dx = 1 / 6 + Math.sqrt(5) / 6;
|
|
1849
|
+
let dy = 2 / 3 + Math.sqrt(5) / 3;
|
|
1850
|
+
const dd = Math.sqrt(dx * dx + dy * dy);
|
|
1851
|
+
dx /= dd;
|
|
1852
|
+
dy /= dd;
|
|
1853
|
+
const ang = 2 * Math.PI / 6;
|
|
1854
|
+
return [
|
|
1855
|
+
new Quat(Math.cos(ang), dx * Math.sin(ang), dy * Math.sin(ang), 0),
|
|
1856
|
+
new Quat(Math.cos(ang), -dx * Math.sin(ang), dy * Math.sin(ang), 0)
|
|
1857
|
+
];
|
|
1858
|
+
}
|
|
1859
|
+
function octahedron() {
|
|
1860
|
+
const s5 = Math.sqrt(0.5);
|
|
1861
|
+
return [new Quat(0.5, 0.5, 0.5, 0.5), new Quat(s5, 0, 0, s5)];
|
|
1862
|
+
}
|
|
1863
|
+
function closure(g) {
|
|
1864
|
+
const q = [new Quat(1, 0, 0, 0)];
|
|
1865
|
+
for (let i = 0; i < q.length; i++) {
|
|
1866
|
+
for (let j = 0; j < g.length; j++) {
|
|
1867
|
+
const ns = g[j].mul(q[i]);
|
|
1868
|
+
const negns = ns.smul(-1);
|
|
1869
|
+
let wasseen = false;
|
|
1870
|
+
for (let k = 0; k < q.length; k++) {
|
|
1871
|
+
if (ns.dist(q[k]) < eps2 || negns.dist(q[k]) < eps2) {
|
|
1872
|
+
wasseen = true;
|
|
1873
|
+
break;
|
|
1874
|
+
}
|
|
1875
|
+
}
|
|
1876
|
+
if (!wasseen) {
|
|
1877
|
+
q.push(ns);
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
}
|
|
1881
|
+
return q;
|
|
1882
|
+
}
|
|
1883
|
+
function uniqueplanes(p, g) {
|
|
1884
|
+
const planes = [];
|
|
1885
|
+
const planerot = [];
|
|
1886
|
+
for (let i = 0; i < g.length; i++) {
|
|
1887
|
+
const p2 = p.rotateplane(g[i]);
|
|
1888
|
+
let wasseen = false;
|
|
1889
|
+
for (let j = 0; j < planes.length; j++) {
|
|
1890
|
+
if (p2.dist(planes[j]) < eps2) {
|
|
1891
|
+
wasseen = true;
|
|
1892
|
+
break;
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
1895
|
+
if (!wasseen) {
|
|
1896
|
+
planes.push(p2);
|
|
1897
|
+
planerot.push(g[i]);
|
|
1898
|
+
}
|
|
1899
|
+
}
|
|
1900
|
+
return planerot;
|
|
1901
|
+
}
|
|
1902
|
+
function getface(planes) {
|
|
1903
|
+
const face = [];
|
|
1904
|
+
for (let i = 1; i < planes.length; i++) {
|
|
1905
|
+
for (let j = i + 1; j < planes.length; j++) {
|
|
1906
|
+
const p = solvethreeplanes(0, i, j, planes);
|
|
1907
|
+
if (p) {
|
|
1908
|
+
let wasseen = false;
|
|
1909
|
+
for (let k = 0; k < face.length; k++) {
|
|
1910
|
+
if (p.dist(face[k]) < eps2) {
|
|
1911
|
+
wasseen = true;
|
|
1912
|
+
break;
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
if (!wasseen) {
|
|
1916
|
+
face.push(p);
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
for (; ; ) {
|
|
1922
|
+
let changed = false;
|
|
1923
|
+
for (let i = 0; i < face.length; i++) {
|
|
1924
|
+
const j = (i + 1) % face.length;
|
|
1925
|
+
if (planes[0].dot(face[i].cross(face[j])) < 0) {
|
|
1926
|
+
const t = face[i];
|
|
1927
|
+
face[i] = face[j];
|
|
1928
|
+
face[j] = t;
|
|
1929
|
+
changed = true;
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1932
|
+
if (!changed) {
|
|
1933
|
+
break;
|
|
1934
|
+
}
|
|
1935
|
+
}
|
|
1936
|
+
return face;
|
|
1937
|
+
}
|
|
1938
|
+
|
|
1939
|
+
// src/cubing/puzzle-geometry/pgPuzzles.ts
|
|
1940
|
+
var pgPuzzle = {
|
|
1941
|
+
"2x2x2": "c f 0",
|
|
1942
|
+
"3x3x3": "c f 0.333333333333333",
|
|
1943
|
+
"4x4x4": "c f 0.5 f 0",
|
|
1944
|
+
"5x5x5": "c f 0.6 f 0.2",
|
|
1945
|
+
"6x6x6": "c f 0.666666666666667 f 0.333333333333333 f 0",
|
|
1946
|
+
"7x7x7": "c f 0.714285714285714 f 0.428571428571429 f 0.142857142857143",
|
|
1947
|
+
"8x8x8": "c f 0.75 f 0.5 f 0.25 f 0",
|
|
1948
|
+
"9x9x9": "c f 0.777777777777778 f 0.555555555555556 f 0.333333333333333 f 0.111111111111111",
|
|
1949
|
+
"10x10x10": "c f 0.8 f 0.6 f 0.4 f 0.2 f 0",
|
|
1950
|
+
"11x11x11": "c f 0.818181818181818 f 0.636363636363636 f 0.454545454545455 f 0.272727272727273 f 0.0909090909090909",
|
|
1951
|
+
"12x12x12": "c f 0.833333333333333 f 0.666666666666667 f 0.5 f 0.333333333333333 f 0.166666666666667 f 0",
|
|
1952
|
+
"13x13x13": "c f 0.846153846153846 f 0.692307692307692 f 0.538461538461538 f 0.384615384615385 f 0.230769230769231 f 0.0769230769230769",
|
|
1953
|
+
"20x20x20": "c f 0 f .1 f .2 f .3 f .4 f .5 f .6 f .7 f .8 f .9",
|
|
1954
|
+
"30x30x30": "c f 0 f .066667 f .133333 f .2 f .266667 f .333333 f .4 f .466667 f .533333 f .6 f .666667 f .733333 f .8 f .866667 f .933333",
|
|
1955
|
+
"40x40x40": "c f 0 f .05 f .1 f .15 f .2 f .25 f .3 f .35 f .4 f .45 f .5 f .55 f .6 f .65 f .7 f .75 f .8 f .85 f .9 f .95",
|
|
1956
|
+
skewb: "c v 0",
|
|
1957
|
+
"master skewb": "c v 0.275",
|
|
1958
|
+
"professor skewb": "c v 0 v 0.38",
|
|
1959
|
+
"compy cube": "c v 0.915641442663986",
|
|
1960
|
+
helicopter: "c e 0.707106781186547",
|
|
1961
|
+
"curvy copter": "c e 0.83",
|
|
1962
|
+
dino: "c v 0.577350269189626",
|
|
1963
|
+
"little chop": "c e 0",
|
|
1964
|
+
pyramorphix: "t e 0",
|
|
1965
|
+
mastermorphix: "t e 0.346184634065199",
|
|
1966
|
+
pyraminx: "t v 0.333333333333333 v 1.66666666666667",
|
|
1967
|
+
tetraminx: "t v 0.333333333333333",
|
|
1968
|
+
"master pyraminx": "t v 0 v 1 v 2",
|
|
1969
|
+
"master tetraminx": "t v 0 v 1",
|
|
1970
|
+
"professor pyraminx": "t v -0.2 v 0.6 v 1.4 v 2.2",
|
|
1971
|
+
"professor tetraminx": "t v -0.2 v 0.6 v 1.4",
|
|
1972
|
+
"royal pyraminx": "t v -0.333333333333333 v 0.333333333333333 v 1 v 1.66666666666667 v 2.33333333333333",
|
|
1973
|
+
"royal tetraminx": "t v -0.333333333333333 v 0.333333333333333 v 1 v 1.66666666666667",
|
|
1974
|
+
"emperor pyraminx": "t v -0.428571428571429 v 0.142857142857143 v 0.714285714285714 v 1.28571428571429 v 1.85714285714286 v 2.42857142857143",
|
|
1975
|
+
"emperor tetraminx": "t v -0.428571428571429 v 0.142857142857143 v 0.714285714285714 v 1.28571428571429 v 1.85714285714286",
|
|
1976
|
+
"Jing pyraminx": "t f 0",
|
|
1977
|
+
"master pyramorphix": "t e 0.866025403784437",
|
|
1978
|
+
megaminx: "d f 0.7",
|
|
1979
|
+
gigaminx: "d f 0.64 f 0.82",
|
|
1980
|
+
teraminx: "d f 0.64 f 0.76 f 0.88",
|
|
1981
|
+
petaminx: "d f 0.64 f 0.73 f 0.82 f 0.91",
|
|
1982
|
+
examinx: "d f 0.64 f 0.712 f 0.784 f 0.856 f 0.928",
|
|
1983
|
+
zetaminx: "d f 0.64 f 0.7 f 0.76 f 0.82 f 0.88 f 0.94",
|
|
1984
|
+
yottaminx: "d f 0.64 f 0.6914 f 0.7429 f 0.7943 f 0.8457 f 0.8971 f 0.9486",
|
|
1985
|
+
pentultimate: "d f 0",
|
|
1986
|
+
"master pentultimate": "d f 0.1",
|
|
1987
|
+
"elite pentultimate": "d f 0 f 0.145905",
|
|
1988
|
+
// exact value for starminx is sqrt(5(5-2 sqrt(5))/3)
|
|
1989
|
+
starminx: "d v 0.937962370425399",
|
|
1990
|
+
"starminx 2": "d f 0.23606797749979",
|
|
1991
|
+
"pyraminx crystal": "d f 0.447213595499989",
|
|
1992
|
+
chopasaurus: "d v 0",
|
|
1993
|
+
"big chop": "d e 0",
|
|
1994
|
+
"skewb diamond": "o f 0",
|
|
1995
|
+
FTO: "o f 0.333333333333333",
|
|
1996
|
+
"master FTO": "o f 0.5 f 0",
|
|
1997
|
+
"Christopher's jewel": "o v 0.577350269189626",
|
|
1998
|
+
octastar: "o e 0",
|
|
1999
|
+
"Trajber's octahedron": "o v 0.433012701892219",
|
|
2000
|
+
"radio chop": "i f 0",
|
|
2001
|
+
icosamate: "i v 0",
|
|
2002
|
+
"Regular Astrominx": "i v 0.18759247376021",
|
|
2003
|
+
"Regular Astrominx + Big Chop": "i v 0.18759247376021 e 0",
|
|
2004
|
+
Redicosahedron: "i v 0.794654472291766",
|
|
2005
|
+
"Redicosahedron with centers": "i v 0.84",
|
|
2006
|
+
Icosaminx: "i v 0.73",
|
|
2007
|
+
"Eitan's star": "i f 0.61803398874989",
|
|
2008
|
+
"2x2x2 + dino": "c f 0 v 0.577350269189626",
|
|
2009
|
+
"2x2x2 + little chop": "c f 0 e 0",
|
|
2010
|
+
"dino + little chop": "c v 0.577350269189626 e 0",
|
|
2011
|
+
"2x2x2 + dino + little chop": "c f 0 v 0.577350269189626 e 0",
|
|
2012
|
+
"megaminx + chopasaurus": "d f 0.61803398875 v 0",
|
|
2013
|
+
"starminx combo": "d f 0.23606797749979 v 0.937962370425399"
|
|
2014
|
+
};
|
|
2015
|
+
|
|
2016
|
+
// src/cubing/puzzle-geometry/SchreierSims.ts
|
|
2017
|
+
var FactoredNumber = class {
|
|
2018
|
+
mult;
|
|
2019
|
+
constructor() {
|
|
2020
|
+
this.mult = [];
|
|
2021
|
+
}
|
|
2022
|
+
multiply(n) {
|
|
2023
|
+
for (let f = 2; f * f <= n; f++) {
|
|
2024
|
+
while (n % f === 0) {
|
|
2025
|
+
if (void 0 !== this.mult[f]) {
|
|
2026
|
+
this.mult[f]++;
|
|
2027
|
+
} else {
|
|
2028
|
+
this.mult[f] = 1;
|
|
2029
|
+
}
|
|
2030
|
+
n /= f;
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
if (n > 1) {
|
|
2034
|
+
if (void 0 !== this.mult[n]) {
|
|
2035
|
+
this.mult[n]++;
|
|
2036
|
+
} else {
|
|
2037
|
+
this.mult[n] = 1;
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
toString() {
|
|
2042
|
+
let r = "";
|
|
2043
|
+
for (let i = 0; i < this.mult.length; i++) {
|
|
2044
|
+
if (void 0 !== this.mult[i]) {
|
|
2045
|
+
if (r !== "") {
|
|
2046
|
+
r += "*";
|
|
2047
|
+
}
|
|
2048
|
+
r += i;
|
|
2049
|
+
if (this.mult[i] > 1) {
|
|
2050
|
+
r += `^${this.mult[i]}`;
|
|
2051
|
+
}
|
|
2052
|
+
}
|
|
2053
|
+
}
|
|
2054
|
+
return r;
|
|
2055
|
+
}
|
|
2056
|
+
};
|
|
2057
|
+
function schreierSims(g, disp) {
|
|
2058
|
+
const n = g[0].p.length;
|
|
2059
|
+
const e = identity(n);
|
|
2060
|
+
let sgs = [];
|
|
2061
|
+
let sgsi = [];
|
|
2062
|
+
let sgslen = [];
|
|
2063
|
+
let Tk = [];
|
|
2064
|
+
let Tklen = [];
|
|
2065
|
+
function resolve(p) {
|
|
2066
|
+
for (let i = p.p.length - 1; i >= 0; i--) {
|
|
2067
|
+
const j = p.p[i];
|
|
2068
|
+
if (j !== i) {
|
|
2069
|
+
if (!sgs[i][j]) {
|
|
2070
|
+
return false;
|
|
2071
|
+
}
|
|
2072
|
+
p = p.mul(sgsi[i][j]);
|
|
2073
|
+
}
|
|
2074
|
+
}
|
|
2075
|
+
return true;
|
|
2076
|
+
}
|
|
2077
|
+
function knutha(k, p, len) {
|
|
2078
|
+
Tk[k].push(p);
|
|
2079
|
+
Tklen[k].push(len);
|
|
2080
|
+
for (let i = 0; i < sgs[k].length; i++) {
|
|
2081
|
+
if (sgs[k][i]) {
|
|
2082
|
+
knuthb(k, sgs[k][i].mul(p), len + sgslen[k][i]);
|
|
2083
|
+
}
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
function knuthb(k, p, len) {
|
|
2087
|
+
const j = p.p[k];
|
|
2088
|
+
if (!sgs[k][j]) {
|
|
2089
|
+
sgs[k][j] = p;
|
|
2090
|
+
sgsi[k][j] = p.inv();
|
|
2091
|
+
sgslen[k][j] = len;
|
|
2092
|
+
for (let i = 0; i < Tk[k].length; i++) {
|
|
2093
|
+
knuthb(k, p.mul(Tk[k][i]), len + Tklen[k][i]);
|
|
2094
|
+
}
|
|
2095
|
+
return;
|
|
2096
|
+
}
|
|
2097
|
+
const p2 = p.mul(sgsi[k][j]);
|
|
2098
|
+
if (!resolve(p2)) {
|
|
2099
|
+
knutha(k - 1, p2, len + sgslen[k][j]);
|
|
2100
|
+
}
|
|
2101
|
+
}
|
|
2102
|
+
function getsgs() {
|
|
2103
|
+
sgs = [];
|
|
2104
|
+
sgsi = [];
|
|
2105
|
+
Tk = [];
|
|
2106
|
+
sgslen = [];
|
|
2107
|
+
Tklen = [];
|
|
2108
|
+
for (let i = 0; i < n; i++) {
|
|
2109
|
+
sgs.push([]);
|
|
2110
|
+
sgsi.push([]);
|
|
2111
|
+
sgslen.push([]);
|
|
2112
|
+
Tk.push([]);
|
|
2113
|
+
Tklen.push([]);
|
|
2114
|
+
sgs[i][i] = e;
|
|
2115
|
+
sgsi[i][i] = e;
|
|
2116
|
+
sgslen[i][i] = 0;
|
|
2117
|
+
}
|
|
2118
|
+
let none = 0;
|
|
2119
|
+
let sz = BigInt(1);
|
|
2120
|
+
for (let i = 0; i < g.length; i++) {
|
|
2121
|
+
knutha(n - 1, g[i], 1);
|
|
2122
|
+
sz = BigInt(1);
|
|
2123
|
+
let tks = 0;
|
|
2124
|
+
let sollen = 0;
|
|
2125
|
+
const avgs = [];
|
|
2126
|
+
const mults = new FactoredNumber();
|
|
2127
|
+
for (let j = 0; j < n; j++) {
|
|
2128
|
+
let cnt = 0;
|
|
2129
|
+
let lensum = 0;
|
|
2130
|
+
for (let k = 0; k < n; k++) {
|
|
2131
|
+
if (sgs[j][k]) {
|
|
2132
|
+
cnt++;
|
|
2133
|
+
lensum += sgslen[j][k];
|
|
2134
|
+
if (j !== k) {
|
|
2135
|
+
none++;
|
|
2136
|
+
}
|
|
2137
|
+
}
|
|
2138
|
+
}
|
|
2139
|
+
tks += Tk[j].length;
|
|
2140
|
+
sz *= BigInt(cnt);
|
|
2141
|
+
if (cnt > 1) {
|
|
2142
|
+
mults.multiply(cnt);
|
|
2143
|
+
}
|
|
2144
|
+
const avg = lensum / cnt;
|
|
2145
|
+
avgs.push(avg);
|
|
2146
|
+
sollen += avg;
|
|
2147
|
+
}
|
|
2148
|
+
disp(
|
|
2149
|
+
`${i}: sz ${sz} T ${tks} sol ${sollen} none ${none} mults ${mults.toString()}`
|
|
2150
|
+
);
|
|
2151
|
+
}
|
|
2152
|
+
return sz;
|
|
2153
|
+
}
|
|
2154
|
+
return getsgs();
|
|
2155
|
+
}
|
|
2156
|
+
|
|
2157
|
+
// src/cubing/puzzle-geometry/PuzzleGeometry.ts
|
|
2158
|
+
function tstart(s) {
|
|
2159
|
+
return s;
|
|
2160
|
+
}
|
|
2161
|
+
function tend(_) {
|
|
2162
|
+
}
|
|
2163
|
+
var Face = class _Face {
|
|
2164
|
+
coords;
|
|
2165
|
+
length;
|
|
2166
|
+
constructor(q) {
|
|
2167
|
+
this.coords = new Array(q.length * 3);
|
|
2168
|
+
for (let i = 0; i < q.length; i++) {
|
|
2169
|
+
this.coords[3 * i] = q[i].b;
|
|
2170
|
+
this.coords[3 * i + 1] = q[i].c;
|
|
2171
|
+
this.coords[3 * i + 2] = q[i].d;
|
|
2172
|
+
}
|
|
2173
|
+
this.length = q.length;
|
|
2174
|
+
}
|
|
2175
|
+
get(off) {
|
|
2176
|
+
return new Quat(
|
|
2177
|
+
0,
|
|
2178
|
+
this.coords[3 * off],
|
|
2179
|
+
this.coords[3 * off + 1],
|
|
2180
|
+
this.coords[3 * off + 2]
|
|
2181
|
+
);
|
|
2182
|
+
}
|
|
2183
|
+
centermass() {
|
|
2184
|
+
let sx = 0;
|
|
2185
|
+
let sy = 0;
|
|
2186
|
+
let sz = 0;
|
|
2187
|
+
for (let i = 0; i < this.length; i++) {
|
|
2188
|
+
sx += this.coords[3 * i];
|
|
2189
|
+
sy += this.coords[3 * i + 1];
|
|
2190
|
+
sz += this.coords[3 * i + 2];
|
|
2191
|
+
}
|
|
2192
|
+
return new Quat(0, sx / this.length, sy / this.length, sz / this.length);
|
|
2193
|
+
}
|
|
2194
|
+
rotate(q) {
|
|
2195
|
+
const a = [];
|
|
2196
|
+
for (let i = 0; i < this.length; i++) {
|
|
2197
|
+
a.push(this.get(i).rotatepoint(q));
|
|
2198
|
+
}
|
|
2199
|
+
return new _Face(a);
|
|
2200
|
+
}
|
|
2201
|
+
rotateforward() {
|
|
2202
|
+
const a = [];
|
|
2203
|
+
for (let i = 1; i < this.length; i++) {
|
|
2204
|
+
a.push(this.get(i));
|
|
2205
|
+
}
|
|
2206
|
+
a.push(this.get(0));
|
|
2207
|
+
return new _Face(a);
|
|
2208
|
+
}
|
|
2209
|
+
};
|
|
2210
|
+
var FaceTree = class _FaceTree {
|
|
2211
|
+
constructor(face, left, right) {
|
|
2212
|
+
this.face = face;
|
|
2213
|
+
this.left = left;
|
|
2214
|
+
this.right = right;
|
|
2215
|
+
}
|
|
2216
|
+
face;
|
|
2217
|
+
left;
|
|
2218
|
+
right;
|
|
2219
|
+
split(q) {
|
|
2220
|
+
const t = q.cutface(this.face);
|
|
2221
|
+
if (t !== null) {
|
|
2222
|
+
if (this.left === void 0) {
|
|
2223
|
+
this.left = new _FaceTree(t[0]);
|
|
2224
|
+
this.right = new _FaceTree(t[1]);
|
|
2225
|
+
} else {
|
|
2226
|
+
this.left = this.left?.split(q);
|
|
2227
|
+
this.right = this.right?.split(q);
|
|
2228
|
+
}
|
|
2229
|
+
}
|
|
2230
|
+
return this;
|
|
2231
|
+
}
|
|
2232
|
+
collect(arr, leftfirst) {
|
|
2233
|
+
if (this.left === void 0) {
|
|
2234
|
+
arr.push(new Face(this.face));
|
|
2235
|
+
} else if (leftfirst) {
|
|
2236
|
+
this.left?.collect(arr, false);
|
|
2237
|
+
this.right?.collect(arr, true);
|
|
2238
|
+
} else {
|
|
2239
|
+
this.right?.collect(arr, false);
|
|
2240
|
+
this.left?.collect(arr, true);
|
|
2241
|
+
}
|
|
2242
|
+
return arr;
|
|
2243
|
+
}
|
|
2244
|
+
};
|
|
2245
|
+
function expandfaces(rots, faces) {
|
|
2246
|
+
const nfaces = [];
|
|
2247
|
+
for (const rot of rots) {
|
|
2248
|
+
for (const face of faces) {
|
|
2249
|
+
nfaces.push(face.rotate(rot));
|
|
2250
|
+
}
|
|
2251
|
+
}
|
|
2252
|
+
return nfaces;
|
|
2253
|
+
}
|
|
2254
|
+
var eps3 = 1e-9;
|
|
2255
|
+
var copyright = "PuzzleGeometry 0.1 Copyright 2018 Tomas Rokicki.";
|
|
2256
|
+
var permissivieMoveParsing = false;
|
|
2257
|
+
function defaultnets() {
|
|
2258
|
+
return {
|
|
2259
|
+
// four faces: tetrahedron
|
|
2260
|
+
4: [["F", "D", "L", "R"]],
|
|
2261
|
+
// six faces: cube
|
|
2262
|
+
6: [
|
|
2263
|
+
["F", "D", "L", "U", "R"],
|
|
2264
|
+
["R", "F", "", "B", ""]
|
|
2265
|
+
],
|
|
2266
|
+
// eight faces: octahedron
|
|
2267
|
+
8: [
|
|
2268
|
+
["F", "D", "L", "R"],
|
|
2269
|
+
["D", "F", "BR", ""],
|
|
2270
|
+
["BR", "D", "", "BB"],
|
|
2271
|
+
["BB", "BR", "U", "BL"]
|
|
2272
|
+
],
|
|
2273
|
+
// twelve faces: dodecahedron; U/F/R/F/BL/BR from megaminx
|
|
2274
|
+
12: [
|
|
2275
|
+
["U", "F", "", "", "", ""],
|
|
2276
|
+
["F", "U", "R", "C", "A", "L"],
|
|
2277
|
+
["R", "F", "", "", "E", ""],
|
|
2278
|
+
["E", "R", "", "BF", "", ""],
|
|
2279
|
+
["BF", "E", "BR", "BL", "I", "D"]
|
|
2280
|
+
],
|
|
2281
|
+
// twenty faces: icosahedron
|
|
2282
|
+
20: [
|
|
2283
|
+
["R", "C", "F", "E"],
|
|
2284
|
+
["F", "R", "L", "U"],
|
|
2285
|
+
["L", "F", "A", ""],
|
|
2286
|
+
["E", "R", "G", "I"],
|
|
2287
|
+
["I", "E", "S", "H"],
|
|
2288
|
+
["S", "I", "J", "B"],
|
|
2289
|
+
["B", "S", "K", "D"],
|
|
2290
|
+
["K", "B", "M", "O"],
|
|
2291
|
+
["O", "K", "P", "N"],
|
|
2292
|
+
["P", "O", "Q", ""]
|
|
2293
|
+
]
|
|
2294
|
+
};
|
|
2295
|
+
}
|
|
2296
|
+
var orientationDefaults = {
|
|
2297
|
+
4: {
|
|
2298
|
+
v: ["DFR", "DLF", "DRL", "FLR"],
|
|
2299
|
+
e: ["FR", "LF", "DF", "DL", "RD", "RL"],
|
|
2300
|
+
c: ["DF", "FD", "RL", "LR"]
|
|
2301
|
+
},
|
|
2302
|
+
6: {
|
|
2303
|
+
v: ["URF", "UBR", "ULB", "UFL", "DFR", "DRB", "DBL", "DLF"],
|
|
2304
|
+
e: ["UF", "UR", "UB", "UL", "DF", "DR", "DB", "DL", "FR", "FL", "BR", "BL"],
|
|
2305
|
+
c: ["UB", "LU", "FU", "RU", "BU", "DF"]
|
|
2306
|
+
},
|
|
2307
|
+
8: {
|
|
2308
|
+
v: ["UBBBRR", "URFL", "ULBLBB", "DBRBBBL", "DBLLF", "DFRBR"],
|
|
2309
|
+
e: [
|
|
2310
|
+
"UL",
|
|
2311
|
+
"UBB",
|
|
2312
|
+
"UR",
|
|
2313
|
+
"BRD",
|
|
2314
|
+
"BLD",
|
|
2315
|
+
"FD",
|
|
2316
|
+
"BRR",
|
|
2317
|
+
"FR",
|
|
2318
|
+
"FL",
|
|
2319
|
+
"BLL",
|
|
2320
|
+
"BLBB",
|
|
2321
|
+
"BRBB"
|
|
2322
|
+
],
|
|
2323
|
+
c: ["BBU", "LU", "RU", "BRD", "FD", "BLD", "DF", "UBB"]
|
|
2324
|
+
},
|
|
2325
|
+
12: {
|
|
2326
|
+
v: [
|
|
2327
|
+
"URF",
|
|
2328
|
+
"UFL",
|
|
2329
|
+
"ULBL",
|
|
2330
|
+
"UBLBR",
|
|
2331
|
+
"UBRR",
|
|
2332
|
+
"DEBF",
|
|
2333
|
+
"DBFI",
|
|
2334
|
+
"DIA",
|
|
2335
|
+
"DAC",
|
|
2336
|
+
"DCE",
|
|
2337
|
+
"LAI",
|
|
2338
|
+
"ALF",
|
|
2339
|
+
"FCA",
|
|
2340
|
+
"CFR",
|
|
2341
|
+
"REC",
|
|
2342
|
+
"ERBR",
|
|
2343
|
+
"BRBFE",
|
|
2344
|
+
"BFBRBL",
|
|
2345
|
+
"BLIBF",
|
|
2346
|
+
"IBLL"
|
|
2347
|
+
],
|
|
2348
|
+
e: [
|
|
2349
|
+
"UF",
|
|
2350
|
+
"UR",
|
|
2351
|
+
"UBR",
|
|
2352
|
+
"UBL",
|
|
2353
|
+
"UL",
|
|
2354
|
+
"ER",
|
|
2355
|
+
"EBR",
|
|
2356
|
+
"EBF",
|
|
2357
|
+
"ED",
|
|
2358
|
+
"EC",
|
|
2359
|
+
"IBF",
|
|
2360
|
+
"IBL",
|
|
2361
|
+
"IL",
|
|
2362
|
+
"IA",
|
|
2363
|
+
"ID",
|
|
2364
|
+
"AC",
|
|
2365
|
+
"CF",
|
|
2366
|
+
"FA",
|
|
2367
|
+
"BFBR",
|
|
2368
|
+
"BRBL",
|
|
2369
|
+
"BLBF",
|
|
2370
|
+
"CD",
|
|
2371
|
+
"AD",
|
|
2372
|
+
"AL",
|
|
2373
|
+
"FL",
|
|
2374
|
+
"FR",
|
|
2375
|
+
"CR",
|
|
2376
|
+
"BFD",
|
|
2377
|
+
"BRR",
|
|
2378
|
+
"BLL"
|
|
2379
|
+
],
|
|
2380
|
+
c: [
|
|
2381
|
+
"UF",
|
|
2382
|
+
"FU",
|
|
2383
|
+
"DBF",
|
|
2384
|
+
"BFD",
|
|
2385
|
+
"AD",
|
|
2386
|
+
"CD",
|
|
2387
|
+
"BRU",
|
|
2388
|
+
"BLU",
|
|
2389
|
+
"LA",
|
|
2390
|
+
"RA",
|
|
2391
|
+
"EBR",
|
|
2392
|
+
"IBL"
|
|
2393
|
+
]
|
|
2394
|
+
},
|
|
2395
|
+
20: {
|
|
2396
|
+
v: [
|
|
2397
|
+
"FLPQU",
|
|
2398
|
+
"FUGER",
|
|
2399
|
+
"FRCAL",
|
|
2400
|
+
"HCREI",
|
|
2401
|
+
"ISBDH",
|
|
2402
|
+
"JSIEG",
|
|
2403
|
+
"BSJMK",
|
|
2404
|
+
"MQPOK",
|
|
2405
|
+
"ONDBK",
|
|
2406
|
+
"NOPLA",
|
|
2407
|
+
"UQMJG",
|
|
2408
|
+
"DNACH"
|
|
2409
|
+
],
|
|
2410
|
+
e: [
|
|
2411
|
+
"FU",
|
|
2412
|
+
"FL",
|
|
2413
|
+
"FR",
|
|
2414
|
+
"EG",
|
|
2415
|
+
"ER",
|
|
2416
|
+
"EI",
|
|
2417
|
+
"SJ",
|
|
2418
|
+
"SI",
|
|
2419
|
+
"SB",
|
|
2420
|
+
"KM",
|
|
2421
|
+
"KB",
|
|
2422
|
+
"KO",
|
|
2423
|
+
"PQ",
|
|
2424
|
+
"PO",
|
|
2425
|
+
"PL",
|
|
2426
|
+
"UG",
|
|
2427
|
+
"JG",
|
|
2428
|
+
"MQ",
|
|
2429
|
+
"UQ",
|
|
2430
|
+
"HC",
|
|
2431
|
+
"HD",
|
|
2432
|
+
"ND",
|
|
2433
|
+
"NA",
|
|
2434
|
+
"JM",
|
|
2435
|
+
"CA",
|
|
2436
|
+
"AL",
|
|
2437
|
+
"CR",
|
|
2438
|
+
"HI",
|
|
2439
|
+
"DB",
|
|
2440
|
+
"NO"
|
|
2441
|
+
],
|
|
2442
|
+
c: [
|
|
2443
|
+
"FU",
|
|
2444
|
+
"UF",
|
|
2445
|
+
"GE",
|
|
2446
|
+
"EG",
|
|
2447
|
+
"JS",
|
|
2448
|
+
"SJ",
|
|
2449
|
+
"MK",
|
|
2450
|
+
"KM",
|
|
2451
|
+
"QP",
|
|
2452
|
+
"PQ",
|
|
2453
|
+
"LA",
|
|
2454
|
+
"AL",
|
|
2455
|
+
"RC",
|
|
2456
|
+
"CR",
|
|
2457
|
+
"IH",
|
|
2458
|
+
"HI",
|
|
2459
|
+
"BD",
|
|
2460
|
+
"DB",
|
|
2461
|
+
"ON",
|
|
2462
|
+
"NO"
|
|
2463
|
+
]
|
|
2464
|
+
}
|
|
2465
|
+
};
|
|
2466
|
+
function defaultOrientations() {
|
|
2467
|
+
return {
|
|
2468
|
+
4: [
|
|
2469
|
+
["FLR", [0, 1, 0]],
|
|
2470
|
+
["F", [0, 0, 1]]
|
|
2471
|
+
],
|
|
2472
|
+
// FLR towards viewer
|
|
2473
|
+
6: [
|
|
2474
|
+
["U", [0, 1, 0]],
|
|
2475
|
+
["F", [0, 0, 1]]
|
|
2476
|
+
],
|
|
2477
|
+
// URF towards viewer
|
|
2478
|
+
8: [
|
|
2479
|
+
["U", [0, 1, 0]],
|
|
2480
|
+
["F", [0, 0, 1]]
|
|
2481
|
+
],
|
|
2482
|
+
// FLUR towards viewer
|
|
2483
|
+
12: [
|
|
2484
|
+
["U", [0, 1, 0]],
|
|
2485
|
+
["F", [0, 0, 1]]
|
|
2486
|
+
],
|
|
2487
|
+
// F towards viewer
|
|
2488
|
+
20: [
|
|
2489
|
+
["GUQMJ", [0, 1, 0]],
|
|
2490
|
+
["F", [0, 0, 1]]
|
|
2491
|
+
]
|
|
2492
|
+
// F towards viewer
|
|
2493
|
+
};
|
|
2494
|
+
}
|
|
2495
|
+
function findelement(a, p) {
|
|
2496
|
+
for (let i = 0; i < a.length; i++) {
|
|
2497
|
+
if (a[i][0].dist(p) < eps3) {
|
|
2498
|
+
return i;
|
|
2499
|
+
}
|
|
2500
|
+
}
|
|
2501
|
+
throw Error("Element not found");
|
|
2502
|
+
}
|
|
2503
|
+
function getPG3DNamedPuzzles() {
|
|
2504
|
+
return pgPuzzle;
|
|
2505
|
+
}
|
|
2506
|
+
function getPuzzleDescriptionString(puzzleName) {
|
|
2507
|
+
return pgPuzzle[puzzleName];
|
|
2508
|
+
}
|
|
2509
|
+
var PUZZLE_BASE_SHAPES = ["c", "t", "o", "d", "i"];
|
|
2510
|
+
var PUZZLE_CUT_TYPES = ["f", "v", "e"];
|
|
2511
|
+
function parsePuzzleDescription(s) {
|
|
2512
|
+
const a = s.split(/ /).filter(Boolean);
|
|
2513
|
+
if (a.length % 2 === 0) {
|
|
2514
|
+
return null;
|
|
2515
|
+
}
|
|
2516
|
+
const shape = a[0];
|
|
2517
|
+
if (shape !== "o" && shape !== "c" && shape !== "i" && shape !== "d" && shape !== "t") {
|
|
2518
|
+
return null;
|
|
2519
|
+
}
|
|
2520
|
+
const cuts = [];
|
|
2521
|
+
for (let i = 1; i < a.length; i += 2) {
|
|
2522
|
+
if (a[i] !== "f" && a[i] !== "v" && a[i] !== "e") {
|
|
2523
|
+
return null;
|
|
2524
|
+
}
|
|
2525
|
+
cuts.push({
|
|
2526
|
+
cutType: a[i],
|
|
2527
|
+
distance: parseFloat(a[i + 1])
|
|
2528
|
+
});
|
|
2529
|
+
}
|
|
2530
|
+
return { shape, cuts };
|
|
2531
|
+
}
|
|
2532
|
+
function getPuzzleGeometryByDesc(desc, options = {}) {
|
|
2533
|
+
const parsed = parsePuzzleDescription(desc);
|
|
2534
|
+
if (parsed === null) {
|
|
2535
|
+
throw Error("Could not parse the puzzle description");
|
|
2536
|
+
}
|
|
2537
|
+
const pg = new PuzzleGeometry(
|
|
2538
|
+
parsed,
|
|
2539
|
+
Object.assign({}, { allMoves: true }, options)
|
|
2540
|
+
);
|
|
2541
|
+
pg.allstickers();
|
|
2542
|
+
pg.genperms();
|
|
2543
|
+
return pg;
|
|
2544
|
+
}
|
|
2545
|
+
function getPuzzleGeometryByName(puzzleName, options) {
|
|
2546
|
+
return getPuzzleGeometryByDesc(pgPuzzle[puzzleName], options);
|
|
2547
|
+
}
|
|
2548
|
+
function getmovename(geo, bits, slices) {
|
|
2549
|
+
let inverted = false;
|
|
2550
|
+
if (slices - bits[1] < bits[0]) {
|
|
2551
|
+
geo = [geo[2], geo[3], geo[0], geo[1]];
|
|
2552
|
+
bits = [slices - bits[1], slices - bits[0]];
|
|
2553
|
+
inverted = true;
|
|
2554
|
+
}
|
|
2555
|
+
let movenameFamily = geo[0];
|
|
2556
|
+
let movenamePrefix = "";
|
|
2557
|
+
if (bits[0] === 0 && bits[1] === slices) {
|
|
2558
|
+
movenameFamily = `${movenameFamily}v`;
|
|
2559
|
+
} else if (bits[0] === bits[1]) {
|
|
2560
|
+
if (bits[1] > 0) {
|
|
2561
|
+
movenamePrefix = String(bits[1] + 1);
|
|
2562
|
+
}
|
|
2563
|
+
} else if (bits[0] === 0) {
|
|
2564
|
+
movenameFamily = movenameFamily.toLowerCase();
|
|
2565
|
+
if (bits[1] > 1) {
|
|
2566
|
+
movenamePrefix = String(bits[1] + 1);
|
|
2567
|
+
}
|
|
2568
|
+
} else {
|
|
2569
|
+
throw Error(
|
|
2570
|
+
`We only support slice and outer block moves right now. ${bits}`
|
|
2571
|
+
);
|
|
2572
|
+
}
|
|
2573
|
+
return [movenamePrefix + movenameFamily, inverted];
|
|
2574
|
+
}
|
|
2575
|
+
function splitByFaceNames(s, facenames) {
|
|
2576
|
+
const r = [];
|
|
2577
|
+
let at = 0;
|
|
2578
|
+
while (at < s.length) {
|
|
2579
|
+
if (at > 0 && at < s.length && s[at] === "_") {
|
|
2580
|
+
at++;
|
|
2581
|
+
}
|
|
2582
|
+
let currentMatch = "";
|
|
2583
|
+
for (const facename of facenames) {
|
|
2584
|
+
if (s.substr(at).startsWith(facename[1]) && facename[1].length > currentMatch.length) {
|
|
2585
|
+
currentMatch = facename[1];
|
|
2586
|
+
}
|
|
2587
|
+
}
|
|
2588
|
+
if (currentMatch !== "") {
|
|
2589
|
+
r.push(currentMatch);
|
|
2590
|
+
at += currentMatch.length;
|
|
2591
|
+
} else {
|
|
2592
|
+
throw Error(`Could not split ${s} into face names.`);
|
|
2593
|
+
}
|
|
2594
|
+
}
|
|
2595
|
+
return r;
|
|
2596
|
+
}
|
|
2597
|
+
function toCoords(q, maxdist) {
|
|
2598
|
+
return [q.b / maxdist, -q.c / maxdist, q.d / maxdist];
|
|
2599
|
+
}
|
|
2600
|
+
function toFaceCoords(q, maxdist) {
|
|
2601
|
+
const r = [];
|
|
2602
|
+
const n = q.length;
|
|
2603
|
+
for (let i = 0; i < n; i++) {
|
|
2604
|
+
const pt = toCoords(q.get(n - i - 1), maxdist);
|
|
2605
|
+
r[3 * i] = pt[0];
|
|
2606
|
+
r[3 * i + 1] = pt[1];
|
|
2607
|
+
r[3 * i + 2] = pt[2];
|
|
2608
|
+
}
|
|
2609
|
+
return r;
|
|
2610
|
+
}
|
|
2611
|
+
var PuzzleGeometry = class {
|
|
2612
|
+
constructor(puzzleDescription, options) {
|
|
2613
|
+
this.puzzleDescription = puzzleDescription;
|
|
2614
|
+
const t1 = tstart("genperms");
|
|
2615
|
+
this.options = new PuzzleGeometryFullOptions(options);
|
|
2616
|
+
if (this.options.verbosity > 0) {
|
|
2617
|
+
console.log(this.header("# "));
|
|
2618
|
+
}
|
|
2619
|
+
const { shape, cuts } = puzzleDescription;
|
|
2620
|
+
this.movePlanes = [];
|
|
2621
|
+
this.movePlanesFiltered = [];
|
|
2622
|
+
this.faces = [];
|
|
2623
|
+
this.cubies = [];
|
|
2624
|
+
let g = null;
|
|
2625
|
+
switch (shape) {
|
|
2626
|
+
case "c": {
|
|
2627
|
+
g = cube();
|
|
2628
|
+
break;
|
|
2629
|
+
}
|
|
2630
|
+
case "o": {
|
|
2631
|
+
g = octahedron();
|
|
2632
|
+
break;
|
|
2633
|
+
}
|
|
2634
|
+
case "i": {
|
|
2635
|
+
g = icosahedron();
|
|
2636
|
+
break;
|
|
2637
|
+
}
|
|
2638
|
+
case "t": {
|
|
2639
|
+
g = tetrahedron();
|
|
2640
|
+
break;
|
|
2641
|
+
}
|
|
2642
|
+
case "d": {
|
|
2643
|
+
g = dodecahedron();
|
|
2644
|
+
break;
|
|
2645
|
+
}
|
|
2646
|
+
default:
|
|
2647
|
+
throw Error(`Bad shape argument: ${shape}`);
|
|
2648
|
+
}
|
|
2649
|
+
this.rotations = closure(g);
|
|
2650
|
+
if (this.options.verbosity) {
|
|
2651
|
+
console.log(`# Rotations: ${this.rotations.length}`);
|
|
2652
|
+
}
|
|
2653
|
+
const baseplane = g[0];
|
|
2654
|
+
this.basePlaneRotations = uniqueplanes(baseplane, this.rotations);
|
|
2655
|
+
const baseplanes = this.basePlaneRotations.map(
|
|
2656
|
+
(_) => baseplane.rotateplane(_)
|
|
2657
|
+
);
|
|
2658
|
+
this.basePlanes = baseplanes;
|
|
2659
|
+
this.baseFaceCount = baseplanes.length;
|
|
2660
|
+
const net = defaultnets()[baseplanes.length];
|
|
2661
|
+
this.net = net;
|
|
2662
|
+
this.colors = defaultPlatonicColorSchemes()[baseplanes.length];
|
|
2663
|
+
if (this.options.verbosity > 0) {
|
|
2664
|
+
console.log(`# Base planes: ${baseplanes.length}`);
|
|
2665
|
+
}
|
|
2666
|
+
const baseface = getface(baseplanes);
|
|
2667
|
+
const zero = new Quat(0, 0, 0, 0);
|
|
2668
|
+
if (this.options.verbosity > 0) {
|
|
2669
|
+
console.log(`# Face vertices: ${baseface.length}`);
|
|
2670
|
+
}
|
|
2671
|
+
const facenormal = baseplanes[0].makenormal();
|
|
2672
|
+
const edgenormal = baseface[0].sum(baseface[1]).makenormal();
|
|
2673
|
+
const vertexnormal = baseface[0].makenormal();
|
|
2674
|
+
const boundary = new Quat(1, facenormal.b, facenormal.c, facenormal.d);
|
|
2675
|
+
if (this.options.verbosity > 0) {
|
|
2676
|
+
console.log(`# Boundary is ${boundary}`);
|
|
2677
|
+
}
|
|
2678
|
+
const planerot = uniqueplanes(boundary, this.rotations);
|
|
2679
|
+
const planes = planerot.map((_) => boundary.rotateplane(_));
|
|
2680
|
+
const firstface = getface(planes);
|
|
2681
|
+
this.edgeDistance = firstface[0].sum(firstface[1]).smul(0.5).dist(zero);
|
|
2682
|
+
this.vertexDistance = firstface[0].dist(zero);
|
|
2683
|
+
const cutplanes = [];
|
|
2684
|
+
const intersects = [];
|
|
2685
|
+
let sawface = false;
|
|
2686
|
+
let sawedge = false;
|
|
2687
|
+
let sawvertex = false;
|
|
2688
|
+
for (const cut of cuts) {
|
|
2689
|
+
let normal = null;
|
|
2690
|
+
let distance = 0;
|
|
2691
|
+
switch (cut.cutType) {
|
|
2692
|
+
case "f": {
|
|
2693
|
+
normal = facenormal;
|
|
2694
|
+
distance = 1;
|
|
2695
|
+
sawface = true;
|
|
2696
|
+
break;
|
|
2697
|
+
}
|
|
2698
|
+
case "v": {
|
|
2699
|
+
normal = vertexnormal;
|
|
2700
|
+
distance = this.vertexDistance;
|
|
2701
|
+
sawvertex = true;
|
|
2702
|
+
break;
|
|
2703
|
+
}
|
|
2704
|
+
case "e": {
|
|
2705
|
+
normal = edgenormal;
|
|
2706
|
+
distance = this.edgeDistance;
|
|
2707
|
+
sawedge = true;
|
|
2708
|
+
break;
|
|
2709
|
+
}
|
|
2710
|
+
default:
|
|
2711
|
+
throw Error(`Bad cut argument: ${cut.cutType}`);
|
|
2712
|
+
}
|
|
2713
|
+
cutplanes.push(normal.makecut(cut.distance));
|
|
2714
|
+
intersects.push(cut.distance < distance);
|
|
2715
|
+
}
|
|
2716
|
+
if (this.options.addRotations) {
|
|
2717
|
+
if (!sawface) {
|
|
2718
|
+
cutplanes.push(facenormal.makecut(10));
|
|
2719
|
+
}
|
|
2720
|
+
if (!sawvertex) {
|
|
2721
|
+
cutplanes.push(vertexnormal.makecut(10));
|
|
2722
|
+
}
|
|
2723
|
+
if (!sawedge) {
|
|
2724
|
+
cutplanes.push(edgenormal.makecut(10));
|
|
2725
|
+
}
|
|
2726
|
+
}
|
|
2727
|
+
this.baseFaces = [];
|
|
2728
|
+
for (const baseplanerot of this.basePlaneRotations) {
|
|
2729
|
+
const face = baseplanerot.rotateface(firstface);
|
|
2730
|
+
this.baseFaces.push(new Face(face));
|
|
2731
|
+
}
|
|
2732
|
+
const facenames = [];
|
|
2733
|
+
const faceplanes = [];
|
|
2734
|
+
const vertexnames = [];
|
|
2735
|
+
const edgenames = [];
|
|
2736
|
+
const edgesperface = firstface.length;
|
|
2737
|
+
function searchaddelement(a, p, name) {
|
|
2738
|
+
for (const el of a) {
|
|
2739
|
+
if (el[0].dist(p) < eps3) {
|
|
2740
|
+
el.push(name);
|
|
2741
|
+
return;
|
|
2742
|
+
}
|
|
2743
|
+
}
|
|
2744
|
+
a.push([p, name]);
|
|
2745
|
+
}
|
|
2746
|
+
for (let i = 0; i < this.basePlaneRotations.length; i++) {
|
|
2747
|
+
const face = this.basePlaneRotations[i].rotateface(firstface);
|
|
2748
|
+
for (let j = 0; j < face.length; j++) {
|
|
2749
|
+
const jj = (j + 1) % face.length;
|
|
2750
|
+
const midpoint = face[j].sum(face[jj]).smul(0.5);
|
|
2751
|
+
searchaddelement(edgenames, midpoint, i);
|
|
2752
|
+
}
|
|
2753
|
+
}
|
|
2754
|
+
const otherfaces = [];
|
|
2755
|
+
for (let i = 0; i < this.basePlaneRotations.length; i++) {
|
|
2756
|
+
const face = this.basePlaneRotations[i].rotateface(firstface);
|
|
2757
|
+
const facelist = [];
|
|
2758
|
+
for (let j = 0; j < face.length; j++) {
|
|
2759
|
+
const jj = (j + 1) % face.length;
|
|
2760
|
+
const midpoint = face[j].sum(face[jj]).smul(0.5);
|
|
2761
|
+
const el = edgenames[findelement(edgenames, midpoint)];
|
|
2762
|
+
if (i === el[1]) {
|
|
2763
|
+
facelist.push(el[2]);
|
|
2764
|
+
} else if (i === el[2]) {
|
|
2765
|
+
facelist.push(el[1]);
|
|
2766
|
+
} else {
|
|
2767
|
+
throw Error("Could not find edge");
|
|
2768
|
+
}
|
|
2769
|
+
}
|
|
2770
|
+
otherfaces.push(facelist);
|
|
2771
|
+
}
|
|
2772
|
+
const facenametoindex = {};
|
|
2773
|
+
const faceindextoname = [];
|
|
2774
|
+
faceindextoname.push(net[0][0]);
|
|
2775
|
+
facenametoindex[net[0][0]] = 0;
|
|
2776
|
+
faceindextoname[otherfaces[0][0]] = net[0][1];
|
|
2777
|
+
facenametoindex[net[0][1]] = otherfaces[0][0];
|
|
2778
|
+
for (const neti of net) {
|
|
2779
|
+
const f0 = neti[0];
|
|
2780
|
+
const fi = facenametoindex[f0];
|
|
2781
|
+
if (fi === void 0) {
|
|
2782
|
+
throw Error("Bad edge description; first edge not connected");
|
|
2783
|
+
}
|
|
2784
|
+
let ii = -1;
|
|
2785
|
+
for (let j = 0; j < otherfaces[fi].length; j++) {
|
|
2786
|
+
const fn2 = faceindextoname[otherfaces[fi][j]];
|
|
2787
|
+
if (fn2 !== void 0 && fn2 === neti[1]) {
|
|
2788
|
+
ii = j;
|
|
2789
|
+
break;
|
|
2790
|
+
}
|
|
2791
|
+
}
|
|
2792
|
+
if (ii < 0) {
|
|
2793
|
+
throw Error("First element of a net not known");
|
|
2794
|
+
}
|
|
2795
|
+
for (let j = 2; j < neti.length; j++) {
|
|
2796
|
+
if (neti[j] === "") {
|
|
2797
|
+
continue;
|
|
2798
|
+
}
|
|
2799
|
+
const of = otherfaces[fi][(j + ii - 1) % edgesperface];
|
|
2800
|
+
const fn2 = faceindextoname[of];
|
|
2801
|
+
if (fn2 !== void 0 && fn2 !== neti[j]) {
|
|
2802
|
+
throw Error("Face mismatch in net");
|
|
2803
|
+
}
|
|
2804
|
+
faceindextoname[of] = neti[j];
|
|
2805
|
+
facenametoindex[neti[j]] = of;
|
|
2806
|
+
}
|
|
2807
|
+
}
|
|
2808
|
+
for (let i = 0; i < this.basePlaneRotations.length; i++) {
|
|
2809
|
+
const face = this.basePlaneRotations[i].rotateface(firstface);
|
|
2810
|
+
const faceplane = boundary.rotateplane(this.basePlaneRotations[i]);
|
|
2811
|
+
const facename = faceindextoname[i];
|
|
2812
|
+
facenames.push([face, facename]);
|
|
2813
|
+
faceplanes.push([faceplane, facename]);
|
|
2814
|
+
}
|
|
2815
|
+
for (let i = 0; i < this.basePlaneRotations.length; i++) {
|
|
2816
|
+
const face = this.basePlaneRotations[i].rotateface(firstface);
|
|
2817
|
+
const facename = faceindextoname[i];
|
|
2818
|
+
for (let j = 0; j < face.length; j++) {
|
|
2819
|
+
const jj = (j + 1) % face.length;
|
|
2820
|
+
const midpoint = face[j].sum(face[jj]).smul(0.5);
|
|
2821
|
+
const jjj = (j + 2) % face.length;
|
|
2822
|
+
const midpoint2 = face[jj].sum(face[jjj]).smul(0.5);
|
|
2823
|
+
const e1 = findelement(edgenames, midpoint);
|
|
2824
|
+
const e2 = findelement(edgenames, midpoint2);
|
|
2825
|
+
searchaddelement(vertexnames, face[jj], [facename, e2, e1]);
|
|
2826
|
+
}
|
|
2827
|
+
}
|
|
2828
|
+
this.swizzler = new FaceNameSwizzler(facenames.map((_) => _[1]));
|
|
2829
|
+
const sep = this.swizzler.prefixFree ? "" : "_";
|
|
2830
|
+
const oridata = orientationDefaults[this.baseFaceCount];
|
|
2831
|
+
const markedface = [];
|
|
2832
|
+
for (let i = 0; i < this.baseFaceCount; i++) {
|
|
2833
|
+
markedface[1 << i] = i;
|
|
2834
|
+
}
|
|
2835
|
+
{
|
|
2836
|
+
const oriprefs = oridata["v"];
|
|
2837
|
+
for (const name of oriprefs) {
|
|
2838
|
+
const fn = this.swizzler.splitByFaceNames(name);
|
|
2839
|
+
let bits = 0;
|
|
2840
|
+
for (const i of fn) {
|
|
2841
|
+
bits |= 1 << i;
|
|
2842
|
+
}
|
|
2843
|
+
markedface[bits] = fn[0];
|
|
2844
|
+
}
|
|
2845
|
+
}
|
|
2846
|
+
{
|
|
2847
|
+
const oriprefs = oridata["e"];
|
|
2848
|
+
for (const name of oriprefs) {
|
|
2849
|
+
const fn = this.swizzler.splitByFaceNames(name);
|
|
2850
|
+
let bits = 0;
|
|
2851
|
+
for (const i of fn) {
|
|
2852
|
+
bits |= 1 << i;
|
|
2853
|
+
}
|
|
2854
|
+
markedface[bits] = fn[0];
|
|
2855
|
+
}
|
|
2856
|
+
}
|
|
2857
|
+
{
|
|
2858
|
+
const oriprefs = oridata["c"];
|
|
2859
|
+
for (const name of oriprefs) {
|
|
2860
|
+
const fn = this.swizzler.splitByFaceNames(name);
|
|
2861
|
+
const bits = 1 << fn[0] | 1 << this.baseFaceCount;
|
|
2862
|
+
markedface[bits] = fn[1];
|
|
2863
|
+
}
|
|
2864
|
+
}
|
|
2865
|
+
for (let i = 0; i < edgenames.length; i++) {
|
|
2866
|
+
if (edgenames[i].length !== 3) {
|
|
2867
|
+
throw Error(`Bad length in edge names ${edgenames[i]}`);
|
|
2868
|
+
}
|
|
2869
|
+
const f1 = edgenames[i][1];
|
|
2870
|
+
const f2 = edgenames[i][2];
|
|
2871
|
+
let c1 = faceindextoname[f1];
|
|
2872
|
+
const c2 = faceindextoname[f2];
|
|
2873
|
+
const bits = 1 << f1 | 1 << f2;
|
|
2874
|
+
if (markedface[bits] === f1) {
|
|
2875
|
+
c1 = c1 + sep + c2;
|
|
2876
|
+
} else {
|
|
2877
|
+
c1 = c2 + sep + c1;
|
|
2878
|
+
}
|
|
2879
|
+
edgenames[i] = [edgenames[i][0], c1];
|
|
2880
|
+
}
|
|
2881
|
+
for (let i = 0; i < vertexnames.length; i++) {
|
|
2882
|
+
let bits = 0;
|
|
2883
|
+
if (vertexnames[i].length < 4) {
|
|
2884
|
+
throw Error("Bad length in vertex names");
|
|
2885
|
+
}
|
|
2886
|
+
for (let j = 1; j < vertexnames[i].length; j++) {
|
|
2887
|
+
bits |= 1 << facenametoindex[vertexnames[i][j][0]];
|
|
2888
|
+
}
|
|
2889
|
+
const fi = markedface[bits];
|
|
2890
|
+
let st = -1;
|
|
2891
|
+
for (let j = 1; j < vertexnames[i].length; j++) {
|
|
2892
|
+
if (fi === facenametoindex[vertexnames[i][j][0]]) {
|
|
2893
|
+
st = j;
|
|
2894
|
+
}
|
|
2895
|
+
}
|
|
2896
|
+
if (st < 0) {
|
|
2897
|
+
throw Error(
|
|
2898
|
+
"Internal error; couldn't find face name when fixing corners"
|
|
2899
|
+
);
|
|
2900
|
+
}
|
|
2901
|
+
let r = "";
|
|
2902
|
+
for (let j = 1; j < vertexnames[i].length; j++) {
|
|
2903
|
+
if (j === 1) {
|
|
2904
|
+
r = vertexnames[i][st][0];
|
|
2905
|
+
} else {
|
|
2906
|
+
r = r + sep + vertexnames[i][st][0];
|
|
2907
|
+
}
|
|
2908
|
+
for (let k = 1; k < vertexnames[i].length; k++) {
|
|
2909
|
+
if (vertexnames[i][st][1] === vertexnames[i][k][2]) {
|
|
2910
|
+
st = k;
|
|
2911
|
+
break;
|
|
2912
|
+
}
|
|
2913
|
+
}
|
|
2914
|
+
}
|
|
2915
|
+
vertexnames[i] = [vertexnames[i][0], r];
|
|
2916
|
+
}
|
|
2917
|
+
this.markedFaceLookup = markedface;
|
|
2918
|
+
if (this.options.verbosity > 1) {
|
|
2919
|
+
console.log(`# Face names: ${facenames.map((_) => _[1]).join(" ")}`);
|
|
2920
|
+
console.log(`# Edge names: ${edgenames.map((_) => _[1]).join(" ")}`);
|
|
2921
|
+
console.log(`# Vertex names: ${vertexnames.map((_) => _[1]).join(" ")}`);
|
|
2922
|
+
}
|
|
2923
|
+
const geonormals = [];
|
|
2924
|
+
for (const faceplane of faceplanes) {
|
|
2925
|
+
geonormals.push([faceplane[0].makenormal(), faceplane[1], "f"]);
|
|
2926
|
+
}
|
|
2927
|
+
for (const edgename of edgenames) {
|
|
2928
|
+
geonormals.push([edgename[0].makenormal(), edgename[1], "e"]);
|
|
2929
|
+
}
|
|
2930
|
+
for (const vertexname of vertexnames) {
|
|
2931
|
+
geonormals.push([vertexname[0].makenormal(), vertexname[1], "v"]);
|
|
2932
|
+
}
|
|
2933
|
+
this.faceNames = facenames;
|
|
2934
|
+
this.facePlanes = faceplanes;
|
|
2935
|
+
this.edgeNames = edgenames;
|
|
2936
|
+
this.vertexNames = vertexnames;
|
|
2937
|
+
this.geometryNormals = geonormals;
|
|
2938
|
+
const geonormalnames = geonormals.map((_) => _[1]);
|
|
2939
|
+
this.swizzler.setGripNames(geonormalnames);
|
|
2940
|
+
if (this.options.verbosity > 0) {
|
|
2941
|
+
console.log(
|
|
2942
|
+
`# Distances: face ${1} edge ${this.edgeDistance} vertex ${this.vertexDistance}`
|
|
2943
|
+
);
|
|
2944
|
+
}
|
|
2945
|
+
for (let c = 0; c < cutplanes.length; c++) {
|
|
2946
|
+
for (const rotation of this.rotations) {
|
|
2947
|
+
const q = cutplanes[c].rotateplane(rotation);
|
|
2948
|
+
let wasseen = false;
|
|
2949
|
+
for (const moveplane of this.movePlanes) {
|
|
2950
|
+
if (q.sameplane(moveplane)) {
|
|
2951
|
+
wasseen = true;
|
|
2952
|
+
break;
|
|
2953
|
+
}
|
|
2954
|
+
}
|
|
2955
|
+
if (!wasseen) {
|
|
2956
|
+
this.movePlanes.push(q);
|
|
2957
|
+
if (intersects[c]) {
|
|
2958
|
+
this.movePlanesFiltered.push(q);
|
|
2959
|
+
}
|
|
2960
|
+
}
|
|
2961
|
+
}
|
|
2962
|
+
}
|
|
2963
|
+
let ft = new FaceTree(firstface);
|
|
2964
|
+
const tar = this.movePlanesFiltered.slice();
|
|
2965
|
+
let rval = 31;
|
|
2966
|
+
for (let i = 0; i < tar.length; i++) {
|
|
2967
|
+
const j = i + Math.floor((tar.length - i) * (rval / 65536));
|
|
2968
|
+
ft = ft.split(tar[j]);
|
|
2969
|
+
tar[j] = tar[i];
|
|
2970
|
+
rval = (rval * 1657 + 101) % 65536;
|
|
2971
|
+
}
|
|
2972
|
+
const faces = ft.collect([], true);
|
|
2973
|
+
this.faces = faces;
|
|
2974
|
+
if (this.options.verbosity > 0) {
|
|
2975
|
+
console.log(`# Faces is now ${faces.length}`);
|
|
2976
|
+
}
|
|
2977
|
+
this.stickersPerFace = faces.length;
|
|
2978
|
+
const simplerot = [];
|
|
2979
|
+
const cm = centermassface(firstface);
|
|
2980
|
+
for (const rotation of this.rotations) {
|
|
2981
|
+
const f = rotation.rotateface(firstface);
|
|
2982
|
+
if (cm.dist(centermassface(f)) < eps3) {
|
|
2983
|
+
simplerot.push(rotation);
|
|
2984
|
+
}
|
|
2985
|
+
}
|
|
2986
|
+
const finished = new Array(faces.length);
|
|
2987
|
+
const sortme = [];
|
|
2988
|
+
for (let i = 0; i < faces.length; i++) {
|
|
2989
|
+
const cm2 = faces[i].centermass();
|
|
2990
|
+
sortme.push([cm.dist(cm2), cm2, i]);
|
|
2991
|
+
}
|
|
2992
|
+
sortme.sort((a, b) => a[0] - b[0]);
|
|
2993
|
+
for (let ii = 0; ii < faces.length; ii++) {
|
|
2994
|
+
const i = sortme[ii][2];
|
|
2995
|
+
if (!finished[i]) {
|
|
2996
|
+
finished[i] = true;
|
|
2997
|
+
for (const rot of simplerot) {
|
|
2998
|
+
const f2 = faces[i].rotate(rot);
|
|
2999
|
+
const cm2 = f2.centermass();
|
|
3000
|
+
for (let kk = ii + 1; kk < faces.length; kk++) {
|
|
3001
|
+
if (sortme[kk][0] - sortme[ii][0] > eps3) {
|
|
3002
|
+
break;
|
|
3003
|
+
}
|
|
3004
|
+
const k = sortme[kk][2];
|
|
3005
|
+
if (!finished[k] && cm2.dist(sortme[kk][1]) < eps3) {
|
|
3006
|
+
finished[k] = true;
|
|
3007
|
+
faces[k] = f2;
|
|
3008
|
+
break;
|
|
3009
|
+
}
|
|
3010
|
+
}
|
|
3011
|
+
}
|
|
3012
|
+
}
|
|
3013
|
+
}
|
|
3014
|
+
this.shortestEdge = 1e99;
|
|
3015
|
+
for (const face of faces) {
|
|
3016
|
+
for (let j = 0; j < face.length; j++) {
|
|
3017
|
+
const k = (j + 1) % face.length;
|
|
3018
|
+
const t = face.get(j).dist(face.get(k));
|
|
3019
|
+
if (t < this.shortestEdge) {
|
|
3020
|
+
this.shortestEdge = t;
|
|
3021
|
+
}
|
|
3022
|
+
}
|
|
3023
|
+
}
|
|
3024
|
+
if (this.options.verbosity > 0) {
|
|
3025
|
+
console.log(`# Short edge is ${this.shortestEdge}`);
|
|
3026
|
+
}
|
|
3027
|
+
if (shape === "c" && sawface && !sawedge && !sawvertex) {
|
|
3028
|
+
this.addNotationMapper = "NxNxNCubeMapper";
|
|
3029
|
+
this.setReidOrSpeffzOrder = true;
|
|
3030
|
+
}
|
|
3031
|
+
if (shape === "c" && sawvertex && !sawface && !sawedge) {
|
|
3032
|
+
this.addNotationMapper = "SkewbMapper";
|
|
3033
|
+
}
|
|
3034
|
+
if (shape === "t" && (sawvertex || sawface) && !sawedge) {
|
|
3035
|
+
this.addNotationMapper = "PyraminxOrTetraminxMapper";
|
|
3036
|
+
}
|
|
3037
|
+
if (shape === "o" && sawface) {
|
|
3038
|
+
this.notationMapper = new FaceRenamingMapper(
|
|
3039
|
+
this.swizzler,
|
|
3040
|
+
new FaceNameSwizzler(["F", "D", "L", "BL", "R", "U", "BR", "B"])
|
|
3041
|
+
);
|
|
3042
|
+
if (!(sawedge || sawvertex)) {
|
|
3043
|
+
this.addNotationMapper = "FTOMapper";
|
|
3044
|
+
}
|
|
3045
|
+
}
|
|
3046
|
+
if (shape === "d" && sawface) {
|
|
3047
|
+
this.addNotationMapper = "MegaminxMapper";
|
|
3048
|
+
this.notationMapper = new FaceRenamingMapper(
|
|
3049
|
+
this.swizzler,
|
|
3050
|
+
new FaceNameSwizzler([
|
|
3051
|
+
"U",
|
|
3052
|
+
"F",
|
|
3053
|
+
"L",
|
|
3054
|
+
"BL",
|
|
3055
|
+
"BR",
|
|
3056
|
+
"R",
|
|
3057
|
+
"FR",
|
|
3058
|
+
"FL",
|
|
3059
|
+
"DL",
|
|
3060
|
+
"B",
|
|
3061
|
+
"DR",
|
|
3062
|
+
"D"
|
|
3063
|
+
])
|
|
3064
|
+
);
|
|
3065
|
+
}
|
|
3066
|
+
tend(t1);
|
|
3067
|
+
}
|
|
3068
|
+
puzzleDescription;
|
|
3069
|
+
rotations;
|
|
3070
|
+
// all members of the rotation group
|
|
3071
|
+
basePlaneRotations;
|
|
3072
|
+
// unique rotations of the baseplane
|
|
3073
|
+
basePlanes;
|
|
3074
|
+
// planes, corresponding to faces
|
|
3075
|
+
faceNames;
|
|
3076
|
+
// face names
|
|
3077
|
+
facePlanes;
|
|
3078
|
+
// face planes
|
|
3079
|
+
edgeNames;
|
|
3080
|
+
// edge names
|
|
3081
|
+
vertexNames;
|
|
3082
|
+
// vertexnames
|
|
3083
|
+
geometryNormals;
|
|
3084
|
+
// all geometric directions, with names and types
|
|
3085
|
+
movePlanes;
|
|
3086
|
+
// the planes that split moves
|
|
3087
|
+
movePlanesFiltered;
|
|
3088
|
+
// the planes that split moves, filtered
|
|
3089
|
+
movePlaneSets;
|
|
3090
|
+
// the move planes, in parallel sets
|
|
3091
|
+
movePlaneNormals;
|
|
3092
|
+
// one move plane
|
|
3093
|
+
movesetorders;
|
|
3094
|
+
// the order of rotations for each move set
|
|
3095
|
+
movesetgeos;
|
|
3096
|
+
// geometric feature information for move sets
|
|
3097
|
+
baseFaces;
|
|
3098
|
+
// polytope faces before cuts
|
|
3099
|
+
faces;
|
|
3100
|
+
// all the stickers
|
|
3101
|
+
faceCenterMass;
|
|
3102
|
+
// center of mass of all faces
|
|
3103
|
+
baseFaceCount;
|
|
3104
|
+
// number of base faces
|
|
3105
|
+
stickersPerFace;
|
|
3106
|
+
// number of stickers per face
|
|
3107
|
+
shortestEdge;
|
|
3108
|
+
markedFaceLookup;
|
|
3109
|
+
// given a bitmap of faces, identify the marked one
|
|
3110
|
+
cubies;
|
|
3111
|
+
// the cubies
|
|
3112
|
+
vertexDistance;
|
|
3113
|
+
// vertex distance
|
|
3114
|
+
edgeDistance;
|
|
3115
|
+
// edge distance
|
|
3116
|
+
faceToCubie;
|
|
3117
|
+
// map a face to a cubie index
|
|
3118
|
+
faceToCubieOrd;
|
|
3119
|
+
// map a face to a cubie ord
|
|
3120
|
+
moveRotations;
|
|
3121
|
+
// move rotations
|
|
3122
|
+
faceListHash;
|
|
3123
|
+
// face list by key
|
|
3124
|
+
cubieSetNames;
|
|
3125
|
+
// cubie set names
|
|
3126
|
+
cubieOrbitSizes;
|
|
3127
|
+
// the size of each orbit
|
|
3128
|
+
cubieSetNums;
|
|
3129
|
+
cubieOrdNums;
|
|
3130
|
+
orbitOrientations;
|
|
3131
|
+
// the orientation size of each orbit
|
|
3132
|
+
cubieValueMap;
|
|
3133
|
+
// the map for identical cubies
|
|
3134
|
+
cubieSetCubies;
|
|
3135
|
+
// cubies in each cubie set
|
|
3136
|
+
cmovesBySlice = [];
|
|
3137
|
+
// cmoves as perms by slice
|
|
3138
|
+
parsedMoveList;
|
|
3139
|
+
// parsed move list
|
|
3140
|
+
duplicatedFaces = [];
|
|
3141
|
+
// which faces are duplicated
|
|
3142
|
+
duplicatedCubies = [];
|
|
3143
|
+
// which cubies are duplicated
|
|
3144
|
+
fixedCubie = -1;
|
|
3145
|
+
// fixed cubie, if any
|
|
3146
|
+
net = [];
|
|
3147
|
+
colors = [];
|
|
3148
|
+
swizzler;
|
|
3149
|
+
notationMapper = new NullMapper();
|
|
3150
|
+
addNotationMapper = "";
|
|
3151
|
+
setReidOrSpeffzOrder = false;
|
|
3152
|
+
options;
|
|
3153
|
+
keyface(face) {
|
|
3154
|
+
return this.keyface2(face.centermass());
|
|
3155
|
+
}
|
|
3156
|
+
keyface2(cm) {
|
|
3157
|
+
let s = "";
|
|
3158
|
+
const sfcc = String.fromCharCode;
|
|
3159
|
+
for (const moveplaneset of this.movePlaneSets) {
|
|
3160
|
+
if (moveplaneset.length > 0) {
|
|
3161
|
+
const dv = cm.dot(moveplaneset[0]);
|
|
3162
|
+
let t = 0;
|
|
3163
|
+
let b = 1;
|
|
3164
|
+
while (b * 2 <= moveplaneset.length) {
|
|
3165
|
+
b *= 2;
|
|
3166
|
+
}
|
|
3167
|
+
for (; b > 0; b >>= 1) {
|
|
3168
|
+
if (t + b <= moveplaneset.length && dv > moveplaneset[t + b - 1].a) {
|
|
3169
|
+
t += b;
|
|
3170
|
+
}
|
|
3171
|
+
}
|
|
3172
|
+
if (t < 47) {
|
|
3173
|
+
s = s + sfcc(33 + t);
|
|
3174
|
+
} else if (t < 47 + 47 * 47) {
|
|
3175
|
+
s = s + sfcc(33 + 47 + Math.floor(t / 47) - 1) + sfcc(33 + t % 47);
|
|
3176
|
+
} else if (t < 47 + 47 * 47 + 47 * 47 * 47) {
|
|
3177
|
+
s = s + sfcc(33 + 47 + Math.floor((t - 47) / (47 * 47) - 1)) + sfcc(33 + 47 + Math.floor((t - 47) / 47) % 47) + sfcc(33 + t % 47);
|
|
3178
|
+
} else {
|
|
3179
|
+
throw Error("Too many slices for cubie encoding");
|
|
3180
|
+
}
|
|
3181
|
+
}
|
|
3182
|
+
}
|
|
3183
|
+
return s;
|
|
3184
|
+
}
|
|
3185
|
+
// same as above, but instead of returning an encoded string, return
|
|
3186
|
+
// an array with offsets.
|
|
3187
|
+
keyface3(face) {
|
|
3188
|
+
const cm = face.centermass();
|
|
3189
|
+
const r = [];
|
|
3190
|
+
for (const moveplaneset of this.movePlaneSets) {
|
|
3191
|
+
if (moveplaneset.length > 0) {
|
|
3192
|
+
const dv = cm.dot(moveplaneset[0]);
|
|
3193
|
+
let t = 0;
|
|
3194
|
+
let b = 1;
|
|
3195
|
+
while (b * 2 <= moveplaneset.length) {
|
|
3196
|
+
b *= 2;
|
|
3197
|
+
}
|
|
3198
|
+
for (; b > 0; b >>= 1) {
|
|
3199
|
+
if (t + b <= moveplaneset.length && dv > moveplaneset[t + b - 1].a) {
|
|
3200
|
+
t += b;
|
|
3201
|
+
}
|
|
3202
|
+
}
|
|
3203
|
+
r.push(t);
|
|
3204
|
+
}
|
|
3205
|
+
}
|
|
3206
|
+
return r;
|
|
3207
|
+
}
|
|
3208
|
+
findface(cm) {
|
|
3209
|
+
const key = this.keyface2(cm);
|
|
3210
|
+
const arr = this.faceListHash.get(key);
|
|
3211
|
+
if (arr.length === 1) {
|
|
3212
|
+
return arr[0];
|
|
3213
|
+
}
|
|
3214
|
+
for (let i = 0; i + 1 < arr.length; i++) {
|
|
3215
|
+
const face2 = this.faceListHash.get(key)[i];
|
|
3216
|
+
if (Math.abs(cm.dist(this.faceCenterMass[face2])) < eps3) {
|
|
3217
|
+
return face2;
|
|
3218
|
+
}
|
|
3219
|
+
}
|
|
3220
|
+
return arr[arr.length - 1];
|
|
3221
|
+
}
|
|
3222
|
+
project2d(facen, edgen, targvec) {
|
|
3223
|
+
const face = this.faceNames[facen][0];
|
|
3224
|
+
const edgen2 = (edgen + 1) % face.length;
|
|
3225
|
+
const plane = this.basePlanes[facen];
|
|
3226
|
+
let x0 = face[edgen2].sub(face[edgen]);
|
|
3227
|
+
const olen = x0.len();
|
|
3228
|
+
x0 = x0.normalize();
|
|
3229
|
+
const y0 = x0.cross(plane).normalize();
|
|
3230
|
+
let delta = targvec[1].sub(targvec[0]);
|
|
3231
|
+
const len = delta.len() / olen;
|
|
3232
|
+
delta = delta.normalize();
|
|
3233
|
+
const cosr = delta.b;
|
|
3234
|
+
const sinr = delta.c;
|
|
3235
|
+
const x1 = x0.smul(cosr).sub(y0.smul(sinr)).smul(len);
|
|
3236
|
+
const y1 = y0.smul(cosr).sum(x0.smul(sinr)).smul(len);
|
|
3237
|
+
const off = new Quat(
|
|
3238
|
+
0,
|
|
3239
|
+
targvec[0].b - x1.dot(face[edgen]),
|
|
3240
|
+
targvec[0].c - y1.dot(face[edgen]),
|
|
3241
|
+
0
|
|
3242
|
+
);
|
|
3243
|
+
return [x1, y1, off];
|
|
3244
|
+
}
|
|
3245
|
+
// Given an string of uppercase letters, make a bitmask
|
|
3246
|
+
// indicating what letters are in it. Cheap swizzling
|
|
3247
|
+
// for internal use.
|
|
3248
|
+
upperStringToBitSet(geo) {
|
|
3249
|
+
let r = 0;
|
|
3250
|
+
for (let i = 0; i < geo.length; i++) {
|
|
3251
|
+
r |= 1 << geo.charCodeAt(i) - 65;
|
|
3252
|
+
}
|
|
3253
|
+
return r;
|
|
3254
|
+
}
|
|
3255
|
+
allstickers() {
|
|
3256
|
+
const t1 = tstart("allstickers");
|
|
3257
|
+
this.faces = expandfaces(this.basePlaneRotations, this.faces);
|
|
3258
|
+
if (this.options.verbosity > 0) {
|
|
3259
|
+
console.log(`# Total stickers is now ${this.faces.length}`);
|
|
3260
|
+
}
|
|
3261
|
+
this.faceCenterMass = new Array(this.faces.length);
|
|
3262
|
+
for (let i = 0; i < this.faces.length; i++) {
|
|
3263
|
+
this.faceCenterMass[i] = this.faces[i].centermass();
|
|
3264
|
+
}
|
|
3265
|
+
const moveplanesets = [];
|
|
3266
|
+
const moveplanenormals = [];
|
|
3267
|
+
for (const q of this.movePlanes) {
|
|
3268
|
+
const qnormal = q.makenormal();
|
|
3269
|
+
let wasseen = false;
|
|
3270
|
+
for (const moveplanenormal of moveplanenormals) {
|
|
3271
|
+
if (qnormal.sameplane(moveplanenormal.makenormal())) {
|
|
3272
|
+
wasseen = true;
|
|
3273
|
+
}
|
|
3274
|
+
}
|
|
3275
|
+
if (!wasseen) {
|
|
3276
|
+
moveplanenormals.push(qnormal);
|
|
3277
|
+
moveplanesets.push([]);
|
|
3278
|
+
}
|
|
3279
|
+
}
|
|
3280
|
+
for (const q of this.movePlanesFiltered) {
|
|
3281
|
+
const qnormal = q.makenormal();
|
|
3282
|
+
for (let j = 0; j < moveplanenormals.length; j++) {
|
|
3283
|
+
if (qnormal.sameplane(moveplanenormals[j])) {
|
|
3284
|
+
moveplanesets[j].push(q);
|
|
3285
|
+
break;
|
|
3286
|
+
}
|
|
3287
|
+
}
|
|
3288
|
+
}
|
|
3289
|
+
for (let i = 0; i < moveplanesets.length; i++) {
|
|
3290
|
+
const q = moveplanesets[i].map((_) => _.normalizeplane());
|
|
3291
|
+
const goodnormal = moveplanenormals[i];
|
|
3292
|
+
for (let j = 0; j < q.length; j++) {
|
|
3293
|
+
if (q[j].makenormal().dist(goodnormal) > eps3) {
|
|
3294
|
+
q[j] = q[j].smul(-1);
|
|
3295
|
+
}
|
|
3296
|
+
}
|
|
3297
|
+
q.sort((a, b) => a.a - b.a);
|
|
3298
|
+
moveplanesets[i] = q;
|
|
3299
|
+
}
|
|
3300
|
+
this.movePlaneSets = moveplanesets;
|
|
3301
|
+
this.movePlaneNormals = moveplanenormals;
|
|
3302
|
+
const sizes = moveplanesets.map((_) => _.length);
|
|
3303
|
+
if (this.options.verbosity > 0) {
|
|
3304
|
+
console.log(`# Move plane sets: ${sizes}`);
|
|
3305
|
+
}
|
|
3306
|
+
const moverotations = [];
|
|
3307
|
+
for (let i = 0; i < moveplanesets.length; i++) {
|
|
3308
|
+
moverotations.push([]);
|
|
3309
|
+
}
|
|
3310
|
+
for (const q of this.rotations) {
|
|
3311
|
+
if (Math.abs(Math.abs(q.a) - 1) < eps3) {
|
|
3312
|
+
continue;
|
|
3313
|
+
}
|
|
3314
|
+
const qnormal = q.makenormal();
|
|
3315
|
+
for (let j = 0; j < moveplanesets.length; j++) {
|
|
3316
|
+
if (qnormal.sameplane(moveplanenormals[j])) {
|
|
3317
|
+
moverotations[j].push(q);
|
|
3318
|
+
break;
|
|
3319
|
+
}
|
|
3320
|
+
}
|
|
3321
|
+
}
|
|
3322
|
+
this.moveRotations = moverotations;
|
|
3323
|
+
for (let i = 0; i < moverotations.length; i++) {
|
|
3324
|
+
const r = moverotations[i];
|
|
3325
|
+
const goodnormal = r[0].makenormal();
|
|
3326
|
+
for (let j = 0; j < r.length; j++) {
|
|
3327
|
+
if (goodnormal.dist(r[j].makenormal()) > eps3) {
|
|
3328
|
+
r[j] = r[j].smul(-1);
|
|
3329
|
+
}
|
|
3330
|
+
}
|
|
3331
|
+
r.sort((a, b) => a.angle() - b.angle());
|
|
3332
|
+
if (moverotations[i][0].dot(moveplanenormals[i]) < 0) {
|
|
3333
|
+
r.reverse();
|
|
3334
|
+
}
|
|
3335
|
+
}
|
|
3336
|
+
const sizes2 = moverotations.map((_) => 1 + _.length);
|
|
3337
|
+
this.movesetorders = sizes2;
|
|
3338
|
+
const movesetgeos = [];
|
|
3339
|
+
let gtype = "?";
|
|
3340
|
+
for (let i = 0; i < moveplanesets.length; i++) {
|
|
3341
|
+
const p0 = moveplanenormals[i];
|
|
3342
|
+
let neg = null;
|
|
3343
|
+
let pos = null;
|
|
3344
|
+
for (const geonormal of this.geometryNormals) {
|
|
3345
|
+
const d = p0.dot(geonormal[0]);
|
|
3346
|
+
if (Math.abs(d - 1) < eps3) {
|
|
3347
|
+
pos = [geonormal[1], geonormal[2]];
|
|
3348
|
+
gtype = geonormal[2];
|
|
3349
|
+
} else if (Math.abs(d + 1) < eps3) {
|
|
3350
|
+
neg = [geonormal[1], geonormal[2]];
|
|
3351
|
+
gtype = geonormal[2];
|
|
3352
|
+
}
|
|
3353
|
+
}
|
|
3354
|
+
if (pos === null || neg === null) {
|
|
3355
|
+
throw Error("Saw positive or negative sides as null");
|
|
3356
|
+
}
|
|
3357
|
+
movesetgeos.push([
|
|
3358
|
+
pos[0],
|
|
3359
|
+
pos[1],
|
|
3360
|
+
neg[0],
|
|
3361
|
+
neg[1],
|
|
3362
|
+
1 + moveplanesets[i].length
|
|
3363
|
+
]);
|
|
3364
|
+
if (this.addNotationMapper === "NxNxNCubeMapper" && gtype === "f") {
|
|
3365
|
+
this.notationMapper = new NxNxNCubeMapper(1 + moveplanesets[i].length);
|
|
3366
|
+
this.addNotationMapper = "";
|
|
3367
|
+
}
|
|
3368
|
+
if (this.addNotationMapper === "SkewbMapper" && moveplanesets[0].length === 1) {
|
|
3369
|
+
this.notationMapper = new SkewbNotationMapper(this.swizzler);
|
|
3370
|
+
this.addNotationMapper = "";
|
|
3371
|
+
}
|
|
3372
|
+
if (this.addNotationMapper === "PyraminxOrTetraminxMapper") {
|
|
3373
|
+
if (moveplanesets[0].length === 2 && moveplanesets[0][0].a === 0.333333333333333 && moveplanesets[0][1].a === 1.66666666666667) {
|
|
3374
|
+
this.notationMapper = new PyraminxNotationMapper(this.swizzler);
|
|
3375
|
+
this.addNotationMapper = "";
|
|
3376
|
+
} else {
|
|
3377
|
+
this.notationMapper = new TetraminxNotationMapper(this.swizzler);
|
|
3378
|
+
this.addNotationMapper = "";
|
|
3379
|
+
}
|
|
3380
|
+
}
|
|
3381
|
+
if (this.addNotationMapper === "MegaminxMapper" && gtype === "f") {
|
|
3382
|
+
if (1 + moveplanesets[i].length === 3) {
|
|
3383
|
+
this.notationMapper = new MegaminxScramblingNotationMapper(
|
|
3384
|
+
this.notationMapper
|
|
3385
|
+
);
|
|
3386
|
+
}
|
|
3387
|
+
this.addNotationMapper = "";
|
|
3388
|
+
}
|
|
3389
|
+
if (this.addNotationMapper === "FTOMapper" && gtype === "f") {
|
|
3390
|
+
if (1 + moveplanesets[i].length === 3) {
|
|
3391
|
+
this.notationMapper = new FTONotationMapper(
|
|
3392
|
+
this.notationMapper,
|
|
3393
|
+
this.swizzler
|
|
3394
|
+
);
|
|
3395
|
+
}
|
|
3396
|
+
this.addNotationMapper = "";
|
|
3397
|
+
}
|
|
3398
|
+
}
|
|
3399
|
+
this.movesetgeos = movesetgeos;
|
|
3400
|
+
const facelisthash = /* @__PURE__ */ new Map();
|
|
3401
|
+
const faces = this.faces;
|
|
3402
|
+
for (let i = 0; i < faces.length; i++) {
|
|
3403
|
+
const face = faces[i];
|
|
3404
|
+
const s = this.keyface(face);
|
|
3405
|
+
if (!facelisthash.get(s)) {
|
|
3406
|
+
facelisthash.set(s, [i]);
|
|
3407
|
+
} else {
|
|
3408
|
+
const arr = facelisthash.get(s);
|
|
3409
|
+
arr.push(i);
|
|
3410
|
+
if (arr.length === this.baseFaceCount) {
|
|
3411
|
+
if (this.options.verbosity > 0) {
|
|
3412
|
+
console.log("# Splitting core.");
|
|
3413
|
+
}
|
|
3414
|
+
for (let suff = 0; suff < arr.length; suff++) {
|
|
3415
|
+
const s2 = `${s} ${suff}`;
|
|
3416
|
+
facelisthash.set(s2, [arr[suff]]);
|
|
3417
|
+
}
|
|
3418
|
+
}
|
|
3419
|
+
}
|
|
3420
|
+
}
|
|
3421
|
+
this.faceListHash = facelisthash;
|
|
3422
|
+
if (this.options.verbosity > 0) {
|
|
3423
|
+
console.log(`# Cubies: ${facelisthash.size}`);
|
|
3424
|
+
}
|
|
3425
|
+
const cubies = [];
|
|
3426
|
+
const facetocubie = [];
|
|
3427
|
+
const facetoord = [];
|
|
3428
|
+
for (const facelist of facelisthash.values()) {
|
|
3429
|
+
if (facelist.length === this.baseFaceCount) {
|
|
3430
|
+
continue;
|
|
3431
|
+
}
|
|
3432
|
+
if (facelist.length > 1) {
|
|
3433
|
+
const cm = facelist.map((_) => faces[_].centermass());
|
|
3434
|
+
const cmall = centermassface(cm);
|
|
3435
|
+
for (let looplimit = 0; facelist.length > 2; looplimit++) {
|
|
3436
|
+
let changed = false;
|
|
3437
|
+
for (let i = 0; i < facelist.length; i++) {
|
|
3438
|
+
const j = (i + 1) % facelist.length;
|
|
3439
|
+
if (cmall.dot(cm[i].cross(cm[j])) < 0) {
|
|
3440
|
+
const u = cm[i];
|
|
3441
|
+
cm[i] = cm[j];
|
|
3442
|
+
cm[j] = u;
|
|
3443
|
+
const v = facelist[i];
|
|
3444
|
+
facelist[i] = facelist[j];
|
|
3445
|
+
facelist[j] = v;
|
|
3446
|
+
changed = true;
|
|
3447
|
+
}
|
|
3448
|
+
}
|
|
3449
|
+
if (!changed) {
|
|
3450
|
+
break;
|
|
3451
|
+
}
|
|
3452
|
+
if (looplimit > 1e3) {
|
|
3453
|
+
throw Error("Bad epsilon math; too close to border");
|
|
3454
|
+
}
|
|
3455
|
+
}
|
|
3456
|
+
let bits = 0;
|
|
3457
|
+
for (const f of facelist) {
|
|
3458
|
+
bits |= 1 << Math.floor(f / this.stickersPerFace);
|
|
3459
|
+
}
|
|
3460
|
+
const markedface = this.markedFaceLookup[bits];
|
|
3461
|
+
let mini = -1;
|
|
3462
|
+
for (let i = 0; i < facelist.length; i++) {
|
|
3463
|
+
if (Math.floor(facelist[i] / this.stickersPerFace) === markedface) {
|
|
3464
|
+
mini = i;
|
|
3465
|
+
}
|
|
3466
|
+
}
|
|
3467
|
+
if (mini < 0) {
|
|
3468
|
+
throw Error("Could not find marked face in list");
|
|
3469
|
+
}
|
|
3470
|
+
if (mini !== 0) {
|
|
3471
|
+
const ofacelist = facelist.slice();
|
|
3472
|
+
for (let i = 0; i < facelist.length; i++) {
|
|
3473
|
+
facelist[i] = ofacelist[(mini + i) % facelist.length];
|
|
3474
|
+
}
|
|
3475
|
+
}
|
|
3476
|
+
}
|
|
3477
|
+
for (let j = 0; j < facelist.length; j++) {
|
|
3478
|
+
const k = facelist[j];
|
|
3479
|
+
facetocubie[k] = cubies.length;
|
|
3480
|
+
facetoord[k] = j;
|
|
3481
|
+
}
|
|
3482
|
+
cubies.push(facelist);
|
|
3483
|
+
}
|
|
3484
|
+
this.cubies = cubies;
|
|
3485
|
+
this.faceToCubie = facetocubie;
|
|
3486
|
+
this.faceToCubieOrd = facetoord;
|
|
3487
|
+
const typenames = ["?", "CENTERS", "EDGES", "CORNERS", "C4RNER", "C5RNER"];
|
|
3488
|
+
const cubiesetnames = [];
|
|
3489
|
+
const cubietypecounts = [0, 0, 0, 0, 0, 0];
|
|
3490
|
+
const orbitoris = [];
|
|
3491
|
+
const seen = [];
|
|
3492
|
+
let cubiesetnum = 0;
|
|
3493
|
+
const cubiesetnums = [];
|
|
3494
|
+
const cubieordnums = [];
|
|
3495
|
+
const cubieords = [];
|
|
3496
|
+
const cubievaluemap = [];
|
|
3497
|
+
const getcolorkey = (cubienum) => {
|
|
3498
|
+
return cubies[cubienum].map((_) => this.getfaceindex(_)).join(" ");
|
|
3499
|
+
};
|
|
3500
|
+
const cubiesetcubies = [];
|
|
3501
|
+
for (let i = 0; i < cubies.length; i++) {
|
|
3502
|
+
const cubie = cubies[i];
|
|
3503
|
+
if (cubie.length === 0) {
|
|
3504
|
+
continue;
|
|
3505
|
+
}
|
|
3506
|
+
if (seen[i]) {
|
|
3507
|
+
continue;
|
|
3508
|
+
}
|
|
3509
|
+
const cubiekeymap = {};
|
|
3510
|
+
let cubievalueid = 0;
|
|
3511
|
+
cubieords.push(0);
|
|
3512
|
+
cubiesetcubies.push([]);
|
|
3513
|
+
const facecnt = cubie.length;
|
|
3514
|
+
const typectr = cubietypecounts[facecnt]++;
|
|
3515
|
+
let typename = typenames[facecnt];
|
|
3516
|
+
if (typename === void 0 || facecnt === this.baseFaceCount) {
|
|
3517
|
+
typename = "CORE";
|
|
3518
|
+
}
|
|
3519
|
+
typename = typename + (typectr === 0 ? "" : typectr + 1);
|
|
3520
|
+
cubiesetnames[cubiesetnum] = typename;
|
|
3521
|
+
orbitoris[cubiesetnum] = facecnt;
|
|
3522
|
+
const queue = [i];
|
|
3523
|
+
let qg = 0;
|
|
3524
|
+
seen[i] = true;
|
|
3525
|
+
while (qg < queue.length) {
|
|
3526
|
+
const cind = queue[qg++];
|
|
3527
|
+
const cubiecolorkey = getcolorkey(cind);
|
|
3528
|
+
if (cubie.length > 1 || cubiekeymap[cubiecolorkey] === void 0) {
|
|
3529
|
+
cubiekeymap[cubiecolorkey] = cubievalueid++;
|
|
3530
|
+
}
|
|
3531
|
+
cubievaluemap[cind] = cubiekeymap[cubiecolorkey];
|
|
3532
|
+
cubiesetnums[cind] = cubiesetnum;
|
|
3533
|
+
cubiesetcubies[cubiesetnum].push(cind);
|
|
3534
|
+
cubieordnums[cind] = cubieords[cubiesetnum]++;
|
|
3535
|
+
if (queue.length < this.rotations.length) {
|
|
3536
|
+
const cm = this.faceCenterMass[cubies[cind][0]];
|
|
3537
|
+
for (const moverotation of moverotations) {
|
|
3538
|
+
const tq = this.faceToCubie[this.findface(cm.rotatepoint(moverotation[0]))];
|
|
3539
|
+
if (!seen[tq]) {
|
|
3540
|
+
queue.push(tq);
|
|
3541
|
+
seen[tq] = true;
|
|
3542
|
+
}
|
|
3543
|
+
}
|
|
3544
|
+
}
|
|
3545
|
+
}
|
|
3546
|
+
cubiesetnum++;
|
|
3547
|
+
}
|
|
3548
|
+
if (this.setReidOrSpeffzOrder && 4 <= this.stickersPerFace) {
|
|
3549
|
+
const reidorder = [
|
|
3550
|
+
[
|
|
3551
|
+
"UF",
|
|
3552
|
+
"UR",
|
|
3553
|
+
"UB",
|
|
3554
|
+
"UL",
|
|
3555
|
+
"DF",
|
|
3556
|
+
"DR",
|
|
3557
|
+
"DB",
|
|
3558
|
+
"DL",
|
|
3559
|
+
"FR",
|
|
3560
|
+
"FL",
|
|
3561
|
+
"BR",
|
|
3562
|
+
"BL"
|
|
3563
|
+
],
|
|
3564
|
+
["UFR", "URB", "UBL", "ULF", "DRF", "DFL", "DLB", "DBR"],
|
|
3565
|
+
["U", "L", "F", "R", "B", "D"]
|
|
3566
|
+
];
|
|
3567
|
+
const spefffaceorder = ["U", "L", "F", "R", "B", "D"];
|
|
3568
|
+
const speffcornerorder = [
|
|
3569
|
+
"UBL",
|
|
3570
|
+
"URB",
|
|
3571
|
+
"UFR",
|
|
3572
|
+
"ULF",
|
|
3573
|
+
"UBL",
|
|
3574
|
+
"ULF",
|
|
3575
|
+
"DFL",
|
|
3576
|
+
"DLB",
|
|
3577
|
+
"ULF",
|
|
3578
|
+
"UFR",
|
|
3579
|
+
"DRF",
|
|
3580
|
+
"DFL",
|
|
3581
|
+
"UFR",
|
|
3582
|
+
"URB",
|
|
3583
|
+
"DBR",
|
|
3584
|
+
"DRF",
|
|
3585
|
+
"URB",
|
|
3586
|
+
"UBL",
|
|
3587
|
+
"DLB",
|
|
3588
|
+
"DBR",
|
|
3589
|
+
"DFL",
|
|
3590
|
+
"DRF",
|
|
3591
|
+
"DBR",
|
|
3592
|
+
"DLB"
|
|
3593
|
+
];
|
|
3594
|
+
const reidmap = {};
|
|
3595
|
+
for (const cubie of reidorder) {
|
|
3596
|
+
for (let j = 0; j < cubie.length; j++) {
|
|
3597
|
+
reidmap[this.upperStringToBitSet(cubie[j])] = j;
|
|
3598
|
+
}
|
|
3599
|
+
}
|
|
3600
|
+
const cornerloc = {};
|
|
3601
|
+
const spefffacelookup = {};
|
|
3602
|
+
const speffcornerlocs = [];
|
|
3603
|
+
const speffuncorner = [
|
|
3604
|
+
-1,
|
|
3605
|
+
0,
|
|
3606
|
+
1,
|
|
3607
|
+
0,
|
|
3608
|
+
2,
|
|
3609
|
+
-1,
|
|
3610
|
+
1,
|
|
3611
|
+
-1,
|
|
3612
|
+
3,
|
|
3613
|
+
3,
|
|
3614
|
+
-1,
|
|
3615
|
+
-1,
|
|
3616
|
+
2,
|
|
3617
|
+
-1,
|
|
3618
|
+
-1,
|
|
3619
|
+
-1
|
|
3620
|
+
];
|
|
3621
|
+
if (this.stickersPerFace > 9) {
|
|
3622
|
+
for (const vertex of this.vertexNames) {
|
|
3623
|
+
cornerloc[this.upperStringToBitSet(vertex[1])] = vertex[0];
|
|
3624
|
+
}
|
|
3625
|
+
for (let i = 0; i < 6; i++) {
|
|
3626
|
+
spefffacelookup[spefffaceorder[i]] = i;
|
|
3627
|
+
}
|
|
3628
|
+
for (const co of speffcornerorder) {
|
|
3629
|
+
speffcornerlocs.push(cornerloc[this.upperStringToBitSet(co)]);
|
|
3630
|
+
}
|
|
3631
|
+
}
|
|
3632
|
+
for (const cubieset of cubiesetcubies) {
|
|
3633
|
+
for (const cubienum of cubieset) {
|
|
3634
|
+
if (cubies[cubienum].length === 3 || this.stickersPerFace <= 9) {
|
|
3635
|
+
let mask = 0;
|
|
3636
|
+
for (const cubie of cubies[cubienum]) {
|
|
3637
|
+
mask |= 1 << this.faceNames[this.getfaceindex(cubie)][1].charCodeAt(0) - 65;
|
|
3638
|
+
}
|
|
3639
|
+
cubieordnums[cubienum] = reidmap[mask];
|
|
3640
|
+
} else {
|
|
3641
|
+
if (cubies[cubienum].length <= 2) {
|
|
3642
|
+
const ordset = [];
|
|
3643
|
+
for (let k = 0; k < cubies[cubienum].length; k++) {
|
|
3644
|
+
const sticker = cubies[cubienum][k];
|
|
3645
|
+
const facekey = spefffacelookup[this.faceNames[this.getfaceindex(sticker)][1]];
|
|
3646
|
+
let bestdist = 1e20;
|
|
3647
|
+
const cubieloc = this.faces[sticker].centermass();
|
|
3648
|
+
let bestmask = 0;
|
|
3649
|
+
for (let i = 0; i < 4; i++) {
|
|
3650
|
+
const t = cubieloc.dist(speffcornerlocs[4 * facekey + i]);
|
|
3651
|
+
if (t + eps3 < bestdist) {
|
|
3652
|
+
bestdist = t;
|
|
3653
|
+
bestmask = 1 << i;
|
|
3654
|
+
} else if (t < bestdist + eps3) {
|
|
3655
|
+
bestmask |= 1 << i;
|
|
3656
|
+
}
|
|
3657
|
+
}
|
|
3658
|
+
bestmask = speffuncorner[bestmask];
|
|
3659
|
+
if (bestmask >= 0) {
|
|
3660
|
+
const speffind = 4 * facekey + bestmask;
|
|
3661
|
+
ordset.push([speffind, speffcornerorder[speffind]]);
|
|
3662
|
+
}
|
|
3663
|
+
}
|
|
3664
|
+
if (ordset.length > 0) {
|
|
3665
|
+
if (cubies[cubienum].length === 1) {
|
|
3666
|
+
cubieordnums[cubienum] = ordset[0][0];
|
|
3667
|
+
} else if (ordset.length === 2 && ordset[0][1] === ordset[1][1]) {
|
|
3668
|
+
let k = 0;
|
|
3669
|
+
const f0c = spefffaceorder[ordset[0][0] >> 2];
|
|
3670
|
+
const f1c = spefffaceorder[ordset[1][0] >> 2];
|
|
3671
|
+
const corn = ordset[0][1];
|
|
3672
|
+
while (k < 3 && (f0c === corn.charAt(k) || f1c === corn.charAt(k))) {
|
|
3673
|
+
k++;
|
|
3674
|
+
}
|
|
3675
|
+
if (k === 3) {
|
|
3676
|
+
throw Error("Internal error (2) in Speffz");
|
|
3677
|
+
}
|
|
3678
|
+
k = (k + 1) % 3;
|
|
3679
|
+
if (f0c === corn.charAt(k)) {
|
|
3680
|
+
cubieordnums[cubienum] = ordset[0][0];
|
|
3681
|
+
} else if (f1c === corn.charAt(k)) {
|
|
3682
|
+
cubieordnums[cubienum] = ordset[1][0];
|
|
3683
|
+
} else {
|
|
3684
|
+
console.log(f0c, f1c, corn, k, ordset);
|
|
3685
|
+
throw Error("Internal error (3) in Speffz");
|
|
3686
|
+
}
|
|
3687
|
+
}
|
|
3688
|
+
}
|
|
3689
|
+
}
|
|
3690
|
+
}
|
|
3691
|
+
}
|
|
3692
|
+
}
|
|
3693
|
+
}
|
|
3694
|
+
this.cubieSetNums = cubiesetnums;
|
|
3695
|
+
this.cubieOrdNums = cubieordnums;
|
|
3696
|
+
this.cubieSetNames = cubiesetnames;
|
|
3697
|
+
this.cubieOrbitSizes = cubieords;
|
|
3698
|
+
this.orbitOrientations = orbitoris;
|
|
3699
|
+
this.cubieValueMap = cubievaluemap;
|
|
3700
|
+
this.cubieSetCubies = cubiesetcubies;
|
|
3701
|
+
if (this.options.fixedPieceType !== null) {
|
|
3702
|
+
for (let i = 0; i < cubies.length; i++) {
|
|
3703
|
+
if (this.options.fixedPieceType === "v" && cubies[i].length > 2 || this.options.fixedPieceType === "e" && cubies[i].length === 2 || this.options.fixedPieceType === "f" && cubies[i].length === 1) {
|
|
3704
|
+
this.fixedCubie = i;
|
|
3705
|
+
break;
|
|
3706
|
+
}
|
|
3707
|
+
}
|
|
3708
|
+
if (this.fixedCubie < 0) {
|
|
3709
|
+
throw Error(
|
|
3710
|
+
`Could not find a cubie of type ${this.options.fixedPieceType} to fix.`
|
|
3711
|
+
);
|
|
3712
|
+
}
|
|
3713
|
+
}
|
|
3714
|
+
if (this.options.verbosity > 0) {
|
|
3715
|
+
console.log(`# Cubie orbit sizes ${cubieords}`);
|
|
3716
|
+
}
|
|
3717
|
+
tend(t1);
|
|
3718
|
+
}
|
|
3719
|
+
unswizzle(mv) {
|
|
3720
|
+
const newmv = this.notationMapper.notationToInternal(mv);
|
|
3721
|
+
if (newmv === null) {
|
|
3722
|
+
return null;
|
|
3723
|
+
}
|
|
3724
|
+
return newmv.modified({ family: this.swizzler.unswizzle(newmv.family) });
|
|
3725
|
+
}
|
|
3726
|
+
// TODO: remove this in favor of `Move.fromString(…)`
|
|
3727
|
+
// We use an extremely permissive parse here; any character but
|
|
3728
|
+
// digits are allowed in a family name.
|
|
3729
|
+
stringToBlockMove(mv) {
|
|
3730
|
+
const re = /^(([0-9]+)-)?([0-9]+)?([^0-9]+)([0-9]+'?)?$/;
|
|
3731
|
+
const p = mv.match(re);
|
|
3732
|
+
if (p === null) {
|
|
3733
|
+
throw Error(`Bad move passed ${mv}`);
|
|
3734
|
+
}
|
|
3735
|
+
const grip = p[4];
|
|
3736
|
+
let loslice;
|
|
3737
|
+
let hislice;
|
|
3738
|
+
if (p[2] !== void 0) {
|
|
3739
|
+
if (p[3] === void 0) {
|
|
3740
|
+
throw Error("Missing second number in range");
|
|
3741
|
+
}
|
|
3742
|
+
loslice = parseInt(p[2], 10);
|
|
3743
|
+
}
|
|
3744
|
+
if (p[3] !== void 0) {
|
|
3745
|
+
hislice = parseInt(p[3], 10);
|
|
3746
|
+
}
|
|
3747
|
+
let amountstr = "1";
|
|
3748
|
+
let amount = 1;
|
|
3749
|
+
if (p[5] !== void 0) {
|
|
3750
|
+
amountstr = p[5];
|
|
3751
|
+
if (amountstr[0] === "'") {
|
|
3752
|
+
amountstr = `-${amountstr.substring(1)}`;
|
|
3753
|
+
}
|
|
3754
|
+
amount = parseInt(amountstr, 10);
|
|
3755
|
+
}
|
|
3756
|
+
return new Move(new QuantumMove(grip, hislice, loslice), amount);
|
|
3757
|
+
}
|
|
3758
|
+
parseMove(move) {
|
|
3759
|
+
const bm = this.notationMapper.notationToInternal(move);
|
|
3760
|
+
if (bm === null) {
|
|
3761
|
+
throw Error(`Bad move ${move.family}`);
|
|
3762
|
+
}
|
|
3763
|
+
move = bm;
|
|
3764
|
+
let grip = move.family;
|
|
3765
|
+
let fullrotation = false;
|
|
3766
|
+
if (grip.endsWith("v") && grip[0] <= "Z") {
|
|
3767
|
+
if (move.innerLayer !== void 0 || move.outerLayer !== void 0) {
|
|
3768
|
+
throw Error("Cannot use a prefix with full cube rotations");
|
|
3769
|
+
}
|
|
3770
|
+
grip = grip.slice(0, -1);
|
|
3771
|
+
fullrotation = true;
|
|
3772
|
+
}
|
|
3773
|
+
if (grip.endsWith("w") && grip[0] <= "Z") {
|
|
3774
|
+
grip = grip.slice(0, -1).toLowerCase();
|
|
3775
|
+
}
|
|
3776
|
+
let geo;
|
|
3777
|
+
let msi = -1;
|
|
3778
|
+
const geoname = this.swizzler.unswizzle(grip);
|
|
3779
|
+
let firstgrip = false;
|
|
3780
|
+
for (let i = 0; i < this.movesetgeos.length; i++) {
|
|
3781
|
+
const g = this.movesetgeos[i];
|
|
3782
|
+
if (geoname === g[0]) {
|
|
3783
|
+
firstgrip = true;
|
|
3784
|
+
geo = g;
|
|
3785
|
+
msi = i;
|
|
3786
|
+
}
|
|
3787
|
+
if (geoname === g[2]) {
|
|
3788
|
+
firstgrip = false;
|
|
3789
|
+
geo = g;
|
|
3790
|
+
msi = i;
|
|
3791
|
+
}
|
|
3792
|
+
}
|
|
3793
|
+
let loslice = 1;
|
|
3794
|
+
let hislice = 1;
|
|
3795
|
+
if (grip.toUpperCase() !== grip) {
|
|
3796
|
+
hislice = 2;
|
|
3797
|
+
}
|
|
3798
|
+
if (geo === void 0) {
|
|
3799
|
+
throw Error(`Bad grip in move ${move.family}`);
|
|
3800
|
+
}
|
|
3801
|
+
if (move.outerLayer !== void 0) {
|
|
3802
|
+
loslice = move.outerLayer;
|
|
3803
|
+
}
|
|
3804
|
+
if (move.innerLayer !== void 0) {
|
|
3805
|
+
if (move.outerLayer === void 0) {
|
|
3806
|
+
hislice = move.innerLayer;
|
|
3807
|
+
if (grip <= "Z") {
|
|
3808
|
+
loslice = hislice;
|
|
3809
|
+
} else {
|
|
3810
|
+
loslice = 1;
|
|
3811
|
+
}
|
|
3812
|
+
} else {
|
|
3813
|
+
hislice = move.innerLayer;
|
|
3814
|
+
}
|
|
3815
|
+
}
|
|
3816
|
+
loslice--;
|
|
3817
|
+
hislice--;
|
|
3818
|
+
const movePlaneSets = this.movePlaneSets;
|
|
3819
|
+
if (fullrotation) {
|
|
3820
|
+
loslice = 0;
|
|
3821
|
+
hislice = movePlaneSets[msi].length;
|
|
3822
|
+
}
|
|
3823
|
+
if (loslice < 0 || loslice > movePlaneSets[msi].length || hislice < 0 || hislice > movePlaneSets[msi].length) {
|
|
3824
|
+
throw Error(
|
|
3825
|
+
`Bad slice spec ${loslice} ${hislice} vs ${movePlaneSets[msi].length}`
|
|
3826
|
+
);
|
|
3827
|
+
}
|
|
3828
|
+
if (!permissivieMoveParsing && loslice === 0 && hislice === movePlaneSets[msi].length && !fullrotation) {
|
|
3829
|
+
throw Error("! full puzzle rotations must be specified with v suffix.");
|
|
3830
|
+
}
|
|
3831
|
+
return [void 0, msi, loslice, hislice, firstgrip, move.amount];
|
|
3832
|
+
}
|
|
3833
|
+
parsemove(mv) {
|
|
3834
|
+
if (mv instanceof Move) {
|
|
3835
|
+
mv = mv.toString();
|
|
3836
|
+
}
|
|
3837
|
+
const r = this.parseMove(this.stringToBlockMove(mv));
|
|
3838
|
+
r[0] = mv;
|
|
3839
|
+
return r;
|
|
3840
|
+
}
|
|
3841
|
+
genperms() {
|
|
3842
|
+
const t1 = tstart("genperms");
|
|
3843
|
+
if (this.cmovesBySlice.length > 0) {
|
|
3844
|
+
return;
|
|
3845
|
+
}
|
|
3846
|
+
const cmovesbyslice = [];
|
|
3847
|
+
if (this.options.orientCenters) {
|
|
3848
|
+
for (let k = 0; k < this.cubies.length; k++) {
|
|
3849
|
+
if (this.cubies[k].length === 1) {
|
|
3850
|
+
const kk = this.cubies[k][0];
|
|
3851
|
+
const i = this.getfaceindex(kk);
|
|
3852
|
+
const center = this.baseFaces[i].centermass();
|
|
3853
|
+
if (center.dist(this.faceCenterMass[kk]) < eps3) {
|
|
3854
|
+
const bits = 1 << i | 1 << this.baseFaceCount;
|
|
3855
|
+
const towards = this.markedFaceLookup[bits];
|
|
3856
|
+
const normal = this.basePlanes[towards].makenormal();
|
|
3857
|
+
let hiv = -1;
|
|
3858
|
+
let hii = -1;
|
|
3859
|
+
for (let ii = 0; ii < this.faces[kk].length; ii++) {
|
|
3860
|
+
const pt = this.faces[kk].get(ii);
|
|
3861
|
+
const t = normal.dot(pt.sub(center));
|
|
3862
|
+
if (t > hiv) {
|
|
3863
|
+
hiv = t;
|
|
3864
|
+
hii = ii;
|
|
3865
|
+
}
|
|
3866
|
+
}
|
|
3867
|
+
const hii2 = (hii + 1) % this.faces[kk].length;
|
|
3868
|
+
if (Math.abs(normal.dot(this.faces[kk].get(hii2).sub(center)) - hiv) < eps3) {
|
|
3869
|
+
hii = hii2;
|
|
3870
|
+
}
|
|
3871
|
+
if (hii !== 0) {
|
|
3872
|
+
const qs = [];
|
|
3873
|
+
for (let ii = 0; ii < this.faces[kk].length; ii++) {
|
|
3874
|
+
qs.push(this.faces[kk].get((ii + hii) % this.faces[kk].length));
|
|
3875
|
+
}
|
|
3876
|
+
this.faces[kk] = new Face(qs);
|
|
3877
|
+
}
|
|
3878
|
+
const o = this.baseFaces[i].length;
|
|
3879
|
+
for (let m = 1; m < o; m++) {
|
|
3880
|
+
this.cubies[k].push(this.cubies[k][m - 1]);
|
|
3881
|
+
}
|
|
3882
|
+
this.duplicatedFaces[kk] = o;
|
|
3883
|
+
this.duplicatedCubies[k] = o;
|
|
3884
|
+
this.orbitOrientations[this.cubieSetNums[k]] = o;
|
|
3885
|
+
}
|
|
3886
|
+
}
|
|
3887
|
+
}
|
|
3888
|
+
}
|
|
3889
|
+
for (let k = 0; k < this.movePlaneSets.length; k++) {
|
|
3890
|
+
const moveplaneset = this.movePlaneSets[k];
|
|
3891
|
+
const slicenum = [];
|
|
3892
|
+
const slicecnts = [moveplaneset.length + 1, 0];
|
|
3893
|
+
let bhi = 1;
|
|
3894
|
+
while (bhi * 2 <= moveplaneset.length) {
|
|
3895
|
+
bhi *= 2;
|
|
3896
|
+
}
|
|
3897
|
+
for (let i = 0; i < this.faces.length; i++) {
|
|
3898
|
+
let t = 0;
|
|
3899
|
+
if (moveplaneset.length > 0) {
|
|
3900
|
+
const dv = this.faceCenterMass[i].dot(moveplaneset[0]);
|
|
3901
|
+
for (let b = bhi; b > 0; b >>= 1) {
|
|
3902
|
+
if (t + b <= moveplaneset.length && dv > moveplaneset[t + b - 1].a) {
|
|
3903
|
+
t += b;
|
|
3904
|
+
}
|
|
3905
|
+
}
|
|
3906
|
+
t = moveplaneset.length - t;
|
|
3907
|
+
}
|
|
3908
|
+
slicenum.push(t);
|
|
3909
|
+
while (slicecnts.length <= t) {
|
|
3910
|
+
slicecnts.push(0);
|
|
3911
|
+
}
|
|
3912
|
+
slicecnts[t]++;
|
|
3913
|
+
}
|
|
3914
|
+
const axiscmoves = new Array(slicecnts.length);
|
|
3915
|
+
for (let sc = 0; sc < slicecnts.length; sc++) {
|
|
3916
|
+
axiscmoves[sc] = [];
|
|
3917
|
+
}
|
|
3918
|
+
const cubiedone = [];
|
|
3919
|
+
for (let i = 0; i < this.faces.length; i++) {
|
|
3920
|
+
if (slicenum[i] < 0) {
|
|
3921
|
+
continue;
|
|
3922
|
+
}
|
|
3923
|
+
const b = [this.faceToCubie[i], this.faceToCubieOrd[i]];
|
|
3924
|
+
let cm = this.faceCenterMass[i];
|
|
3925
|
+
const ocm = cm;
|
|
3926
|
+
let fi2 = i;
|
|
3927
|
+
const sc = slicenum[fi2];
|
|
3928
|
+
for (; ; ) {
|
|
3929
|
+
slicenum[fi2] = -1;
|
|
3930
|
+
const cm2 = cm.rotatepoint(this.moveRotations[k][0]);
|
|
3931
|
+
if (cm2.dist(ocm) < eps3) {
|
|
3932
|
+
break;
|
|
3933
|
+
}
|
|
3934
|
+
fi2 = this.findface(cm2);
|
|
3935
|
+
b.push(this.faceToCubie[fi2], this.faceToCubieOrd[fi2]);
|
|
3936
|
+
cm = cm2;
|
|
3937
|
+
}
|
|
3938
|
+
if (b.length > 2 && this.options.orientCenters && (this.cubies[b[0]].length === 1 || this.duplicatedCubies[b[0]] > 1)) {
|
|
3939
|
+
if (
|
|
3940
|
+
// TODO: refactor to avoid non-null-assertion
|
|
3941
|
+
this.faceCenterMass[i].dist(
|
|
3942
|
+
this.baseFaces[this.getfaceindex(i)].centermass()
|
|
3943
|
+
) < eps3
|
|
3944
|
+
) {
|
|
3945
|
+
let face1 = this.faces[this.cubies[b[0]][0]];
|
|
3946
|
+
for (let ii = 0; ii < b.length; ii += 2) {
|
|
3947
|
+
const face0 = this.faces[this.cubies[b[ii]][0]];
|
|
3948
|
+
let o = -1;
|
|
3949
|
+
for (let jj = 0; jj < face1.length; jj++) {
|
|
3950
|
+
if (face0.get(jj).dist(face1.get(0)) < eps3) {
|
|
3951
|
+
o = jj;
|
|
3952
|
+
break;
|
|
3953
|
+
}
|
|
3954
|
+
}
|
|
3955
|
+
if (o < 0) {
|
|
3956
|
+
throw Error(
|
|
3957
|
+
"Couldn't find rotation of center faces; ignoring for now."
|
|
3958
|
+
);
|
|
3959
|
+
} else {
|
|
3960
|
+
b[ii + 1] = o;
|
|
3961
|
+
face1 = face1.rotate(this.moveRotations[k][0]);
|
|
3962
|
+
}
|
|
3963
|
+
}
|
|
3964
|
+
}
|
|
3965
|
+
}
|
|
3966
|
+
if (b.length === 2 && this.options.orientCenters) {
|
|
3967
|
+
const dir = this.faceCenterMass[i].dot(this.movePlaneNormals[k]);
|
|
3968
|
+
for (let ii = 1; ii < this.movesetorders[k]; ii++) {
|
|
3969
|
+
if (dir > 0) {
|
|
3970
|
+
b.push(b[0], ii);
|
|
3971
|
+
} else {
|
|
3972
|
+
b.push(
|
|
3973
|
+
b[0],
|
|
3974
|
+
(this.movesetorders[k] - ii) % this.movesetorders[k]
|
|
3975
|
+
// TODO: refactor to avoid non-null-assertion
|
|
3976
|
+
);
|
|
3977
|
+
}
|
|
3978
|
+
}
|
|
3979
|
+
}
|
|
3980
|
+
if (b.length > 2 && !cubiedone[b[0]]) {
|
|
3981
|
+
if (b.length !== 2 * this.movesetorders[k]) {
|
|
3982
|
+
throw Error("Bad length in perm gen");
|
|
3983
|
+
}
|
|
3984
|
+
for (const v of b) {
|
|
3985
|
+
axiscmoves[sc].push(v);
|
|
3986
|
+
}
|
|
3987
|
+
}
|
|
3988
|
+
for (let j = 0; j < b.length; j += 2) {
|
|
3989
|
+
cubiedone[b[j]] = true;
|
|
3990
|
+
}
|
|
3991
|
+
}
|
|
3992
|
+
for (let kk = 0; kk < axiscmoves.length; kk++) {
|
|
3993
|
+
axiscmoves[kk] = axiscmoves[kk].slice();
|
|
3994
|
+
}
|
|
3995
|
+
cmovesbyslice.push(axiscmoves);
|
|
3996
|
+
}
|
|
3997
|
+
this.cmovesBySlice = cmovesbyslice;
|
|
3998
|
+
if (this.options.moveList) {
|
|
3999
|
+
const parsedmovelist = [];
|
|
4000
|
+
for (const moveString of this.options.moveList) {
|
|
4001
|
+
parsedmovelist.push(this.parsemove(moveString));
|
|
4002
|
+
}
|
|
4003
|
+
this.parsedMoveList = parsedmovelist;
|
|
4004
|
+
}
|
|
4005
|
+
this.faceListHash.clear();
|
|
4006
|
+
this.faceCenterMass = [];
|
|
4007
|
+
tend(t1);
|
|
4008
|
+
}
|
|
4009
|
+
getboundarygeometry() {
|
|
4010
|
+
return {
|
|
4011
|
+
baseplanes: this.basePlanes,
|
|
4012
|
+
facenames: this.faceNames,
|
|
4013
|
+
faceplanes: this.facePlanes,
|
|
4014
|
+
vertexnames: this.vertexNames,
|
|
4015
|
+
edgenames: this.edgeNames,
|
|
4016
|
+
geonormals: this.geometryNormals
|
|
4017
|
+
};
|
|
4018
|
+
}
|
|
4019
|
+
getmovesets(k) {
|
|
4020
|
+
const slices = this.movePlaneSets[k].length;
|
|
4021
|
+
let r = [];
|
|
4022
|
+
if (this.parsedMoveList !== void 0) {
|
|
4023
|
+
for (const parsedmove of this.parsedMoveList) {
|
|
4024
|
+
if (parsedmove[1] !== k) {
|
|
4025
|
+
continue;
|
|
4026
|
+
}
|
|
4027
|
+
if (parsedmove[4]) {
|
|
4028
|
+
r.push([parsedmove[2], parsedmove[3]]);
|
|
4029
|
+
} else {
|
|
4030
|
+
r.push([slices - parsedmove[3], slices - parsedmove[2]]);
|
|
4031
|
+
}
|
|
4032
|
+
r.push(parsedmove[5]);
|
|
4033
|
+
}
|
|
4034
|
+
} else {
|
|
4035
|
+
const msg = this.movesetgeos[k];
|
|
4036
|
+
const isTetrahedron = msg[1] !== msg[3];
|
|
4037
|
+
if (this.options.vertexMoves && isTetrahedron && !this.options.allMoves) {
|
|
4038
|
+
if (msg[1] !== msg[3]) {
|
|
4039
|
+
for (let i = 0; i < slices; i++) {
|
|
4040
|
+
if (msg[1] !== "v") {
|
|
4041
|
+
if (this.options.outerBlockMoves) {
|
|
4042
|
+
r.push([i + 1, slices]);
|
|
4043
|
+
} else {
|
|
4044
|
+
r.push([i + 1, i + 1]);
|
|
4045
|
+
}
|
|
4046
|
+
r.push(1);
|
|
4047
|
+
} else {
|
|
4048
|
+
if (this.options.outerBlockMoves) {
|
|
4049
|
+
r.push([0, i]);
|
|
4050
|
+
} else {
|
|
4051
|
+
r.push([i, i]);
|
|
4052
|
+
}
|
|
4053
|
+
r.push(1);
|
|
4054
|
+
}
|
|
4055
|
+
}
|
|
4056
|
+
}
|
|
4057
|
+
} else {
|
|
4058
|
+
for (let i = 0; i <= slices; i++) {
|
|
4059
|
+
if (!this.options.allMoves && i + i === slices) {
|
|
4060
|
+
continue;
|
|
4061
|
+
}
|
|
4062
|
+
if (this.options.outerBlockMoves) {
|
|
4063
|
+
if (i + i > slices) {
|
|
4064
|
+
r.push([i, slices]);
|
|
4065
|
+
} else {
|
|
4066
|
+
r.push([0, i]);
|
|
4067
|
+
}
|
|
4068
|
+
} else {
|
|
4069
|
+
r.push([i, i]);
|
|
4070
|
+
}
|
|
4071
|
+
r.push(1);
|
|
4072
|
+
}
|
|
4073
|
+
}
|
|
4074
|
+
}
|
|
4075
|
+
if (this.fixedCubie >= 0) {
|
|
4076
|
+
const dep = this.keyface3(this.faces[this.cubies[this.fixedCubie][0]])[k];
|
|
4077
|
+
const newr = [];
|
|
4078
|
+
for (let i = 0; i < r.length; i += 2) {
|
|
4079
|
+
let o = r[i];
|
|
4080
|
+
if (dep >= o[0] && dep <= o[1]) {
|
|
4081
|
+
if (o[0] === 0) {
|
|
4082
|
+
o = [o[1] + 1, slices];
|
|
4083
|
+
} else if (slices === o[1]) {
|
|
4084
|
+
o = [0, o[0] - 1];
|
|
4085
|
+
} else {
|
|
4086
|
+
throw Error("fixed cubie option would disconnect move");
|
|
4087
|
+
}
|
|
4088
|
+
}
|
|
4089
|
+
let found = false;
|
|
4090
|
+
for (let j = 0; j < newr.length; j += 2) {
|
|
4091
|
+
if (newr[j][0] === o[0] && newr[j][1] === o[1] && newr[j + 1] === r[i + 1]) {
|
|
4092
|
+
found = true;
|
|
4093
|
+
break;
|
|
4094
|
+
}
|
|
4095
|
+
}
|
|
4096
|
+
if (!found) {
|
|
4097
|
+
newr.push(o);
|
|
4098
|
+
newr.push(r[i + 1]);
|
|
4099
|
+
}
|
|
4100
|
+
}
|
|
4101
|
+
r = newr;
|
|
4102
|
+
}
|
|
4103
|
+
return r;
|
|
4104
|
+
}
|
|
4105
|
+
graybyori(cubie) {
|
|
4106
|
+
let ori = this.cubies[cubie].length;
|
|
4107
|
+
if (this.duplicatedCubies[cubie]) {
|
|
4108
|
+
ori = 1;
|
|
4109
|
+
}
|
|
4110
|
+
return ori === 1 && (this.options.grayCenters || !this.options.includeCenterOrbits) || ori === 2 && (this.options.grayEdges || !this.options.includeEdgeOrbits) || ori > 2 && (this.options.grayCorners || !this.options.includeCornerOrbits);
|
|
4111
|
+
}
|
|
4112
|
+
skipbyori(cubie) {
|
|
4113
|
+
let ori = this.cubies[cubie].length;
|
|
4114
|
+
if (this.duplicatedCubies[cubie]) {
|
|
4115
|
+
ori = 1;
|
|
4116
|
+
}
|
|
4117
|
+
return ori === 1 && !this.options.includeCenterOrbits || ori === 2 && !this.options.includeEdgeOrbits || ori > 2 && !this.options.includeCornerOrbits;
|
|
4118
|
+
}
|
|
4119
|
+
skipcubie(fi) {
|
|
4120
|
+
return this.skipbyori(fi);
|
|
4121
|
+
}
|
|
4122
|
+
header(comment) {
|
|
4123
|
+
return `${comment + copyright}
|
|
4124
|
+
${comment}
|
|
4125
|
+
`;
|
|
4126
|
+
}
|
|
4127
|
+
writegap() {
|
|
4128
|
+
const os = this.getOrbitsDef(false);
|
|
4129
|
+
const r = [];
|
|
4130
|
+
const mvs = [];
|
|
4131
|
+
for (let i = 0; i < os.moveops.length; i++) {
|
|
4132
|
+
let movename = os.movenames[i];
|
|
4133
|
+
if (!os.forcenames[i]) {
|
|
4134
|
+
movename = `M_${externalName(this.notationMapper, movename)}`;
|
|
4135
|
+
}
|
|
4136
|
+
movename = `M_${movename}`;
|
|
4137
|
+
let doinv = false;
|
|
4138
|
+
if (movename[movename.length - 1] === "'") {
|
|
4139
|
+
movename = movename.substring(0, movename.length - 1);
|
|
4140
|
+
doinv = true;
|
|
4141
|
+
}
|
|
4142
|
+
mvs.push(movename);
|
|
4143
|
+
if (doinv) {
|
|
4144
|
+
r.push(`${movename}:=${os.moveops[i].toPerm().inv().toGap()};`);
|
|
4145
|
+
} else {
|
|
4146
|
+
r.push(`${movename}:=${os.moveops[i].toPerm().toGap()};`);
|
|
4147
|
+
}
|
|
4148
|
+
}
|
|
4149
|
+
r.push("Gen:=[");
|
|
4150
|
+
r.push(mvs.join(","));
|
|
4151
|
+
r.push("];");
|
|
4152
|
+
const ip = os.solved.identicalPieces();
|
|
4153
|
+
r.push(
|
|
4154
|
+
`ip:=[${ip.map((_) => `[${_.map((__) => __ + 1).join(",")}]`).join(",")}];`
|
|
4155
|
+
);
|
|
4156
|
+
r.push("# Size(Group(Gen));");
|
|
4157
|
+
r.push("# Size(Stabilizer(Group(Gen), ip, OnTuplesSets));");
|
|
4158
|
+
r.push("");
|
|
4159
|
+
return this.header("# ") + r.join("\n");
|
|
4160
|
+
}
|
|
4161
|
+
writemathematica() {
|
|
4162
|
+
const os = this.getOrbitsDef(false);
|
|
4163
|
+
const r = [];
|
|
4164
|
+
const mvs = [];
|
|
4165
|
+
r.push(`(* ${this.header("").trim()} *)`);
|
|
4166
|
+
for (let i = 0; i < os.moveops.length; i++) {
|
|
4167
|
+
let movename = `m${externalName(this.notationMapper, os.movenames[i])}`;
|
|
4168
|
+
let doinv = false;
|
|
4169
|
+
if (movename[movename.length - 1] === "'") {
|
|
4170
|
+
movename = movename.substring(0, movename.length - 1);
|
|
4171
|
+
doinv = true;
|
|
4172
|
+
}
|
|
4173
|
+
mvs.push(movename);
|
|
4174
|
+
if (doinv) {
|
|
4175
|
+
r.push(`${movename}=${os.moveops[i].toPerm().inv().toMathematica()};`);
|
|
4176
|
+
} else {
|
|
4177
|
+
r.push(`${movename}=${os.moveops[i].toPerm().toMathematica()};`);
|
|
4178
|
+
}
|
|
4179
|
+
}
|
|
4180
|
+
r.push(`gen={${mvs.join(",")}};`);
|
|
4181
|
+
return r.join("\n");
|
|
4182
|
+
}
|
|
4183
|
+
writeksolve(name = "PuzzleGeometryPuzzle") {
|
|
4184
|
+
const od = this.getOrbitsDef(false);
|
|
4185
|
+
return this.header("# ") + od.toKsolve(name, this.notationMapper).join("\n");
|
|
4186
|
+
}
|
|
4187
|
+
getKPuzzleDefinition(fortwisty = true, includemoves = true) {
|
|
4188
|
+
const od = this.getOrbitsDef(fortwisty, includemoves);
|
|
4189
|
+
const internalDefinition = od.toKPuzzleDefinition(includemoves);
|
|
4190
|
+
internalDefinition.experimentalPuzzleDescription = this.puzzleDescription;
|
|
4191
|
+
if (!internalDefinition) {
|
|
4192
|
+
throw Error("Missing definition!");
|
|
4193
|
+
}
|
|
4194
|
+
return internalDefinition;
|
|
4195
|
+
}
|
|
4196
|
+
getMoveFromBits(moverange, amount, inverted, axiscmoves, setmoves, movesetorder) {
|
|
4197
|
+
const moveorbits = [];
|
|
4198
|
+
const perms = [];
|
|
4199
|
+
const oris = [];
|
|
4200
|
+
for (const len of this.cubieOrbitSizes) {
|
|
4201
|
+
perms.push(iota(len));
|
|
4202
|
+
oris.push(zeros(len));
|
|
4203
|
+
}
|
|
4204
|
+
for (let m = moverange[0]; m <= moverange[1]; m++) {
|
|
4205
|
+
const slicecmoves = axiscmoves[m];
|
|
4206
|
+
for (let j = 0; j < slicecmoves.length; j += 2 * movesetorder) {
|
|
4207
|
+
const mperm = slicecmoves.slice(j, j + 2 * movesetorder);
|
|
4208
|
+
const setnum = this.cubieSetNums[mperm[0]];
|
|
4209
|
+
for (let ii = 0; ii < mperm.length; ii += 2) {
|
|
4210
|
+
mperm[ii] = this.cubieOrdNums[mperm[ii]];
|
|
4211
|
+
}
|
|
4212
|
+
let inc = 2;
|
|
4213
|
+
let oinc = 3;
|
|
4214
|
+
if (inverted) {
|
|
4215
|
+
inc = mperm.length - 2;
|
|
4216
|
+
oinc = mperm.length - 1;
|
|
4217
|
+
}
|
|
4218
|
+
if (perms[setnum] === iota(this.cubieOrbitSizes[setnum])) {
|
|
4219
|
+
perms[setnum] = perms[setnum].slice();
|
|
4220
|
+
if (this.orbitOrientations[setnum] > 1 && // TODO: refactor to avoid non-null-assertion
|
|
4221
|
+
!this.options.fixedOrientation) {
|
|
4222
|
+
oris[setnum] = oris[setnum].slice();
|
|
4223
|
+
}
|
|
4224
|
+
}
|
|
4225
|
+
for (let ii = 0; ii < mperm.length; ii += 2) {
|
|
4226
|
+
perms[setnum][mperm[(ii + inc) % mperm.length]] = mperm[ii];
|
|
4227
|
+
if (this.orbitOrientations[setnum] > 1 && // TODO: refactor to avoid non-null-assertion
|
|
4228
|
+
!this.options.fixedOrientation) {
|
|
4229
|
+
oris[setnum][mperm[ii]] = (mperm[(ii + oinc) % mperm.length] - mperm[(ii + 1) % mperm.length] + 2 * this.orbitOrientations[setnum]) % // TODO: refactor to avoid non-null-assertion
|
|
4230
|
+
this.orbitOrientations[setnum];
|
|
4231
|
+
}
|
|
4232
|
+
}
|
|
4233
|
+
}
|
|
4234
|
+
}
|
|
4235
|
+
let lastId = new PGOrbit(iota(24), zeros(24), 1);
|
|
4236
|
+
for (let ii = 0; ii < this.cubieSetNames.length; ii++) {
|
|
4237
|
+
if (setmoves && !setmoves[ii]) {
|
|
4238
|
+
continue;
|
|
4239
|
+
}
|
|
4240
|
+
if (this.orbitOrientations[ii] === 1 || this.options.fixedOrientation) {
|
|
4241
|
+
if (perms[ii] === iota(lastId.perm.length)) {
|
|
4242
|
+
if (perms[ii] !== lastId.perm) {
|
|
4243
|
+
lastId = new PGOrbit(perms[ii], oris[ii], 1);
|
|
4244
|
+
}
|
|
4245
|
+
moveorbits.push(lastId);
|
|
4246
|
+
} else {
|
|
4247
|
+
moveorbits.push(new PGOrbit(perms[ii], oris[ii], 1));
|
|
4248
|
+
}
|
|
4249
|
+
} else {
|
|
4250
|
+
const no = new Array(oris[ii].length);
|
|
4251
|
+
for (let jj = 0; jj < perms[ii].length; jj++) {
|
|
4252
|
+
no[jj] = oris[ii][perms[ii][jj]];
|
|
4253
|
+
}
|
|
4254
|
+
moveorbits.push(
|
|
4255
|
+
new PGOrbit(perms[ii], no, this.orbitOrientations[ii])
|
|
4256
|
+
);
|
|
4257
|
+
}
|
|
4258
|
+
}
|
|
4259
|
+
let mv = new PGTransform(moveorbits);
|
|
4260
|
+
if (amount !== 1) {
|
|
4261
|
+
mv = mv.mulScalar(amount);
|
|
4262
|
+
}
|
|
4263
|
+
return mv;
|
|
4264
|
+
}
|
|
4265
|
+
omitSet(name) {
|
|
4266
|
+
for (const excludedSet of this.options.excludeOrbits) {
|
|
4267
|
+
if (excludedSet === name) {
|
|
4268
|
+
return true;
|
|
4269
|
+
}
|
|
4270
|
+
}
|
|
4271
|
+
return false;
|
|
4272
|
+
}
|
|
4273
|
+
diffmvsets(a, b, slices, neg) {
|
|
4274
|
+
for (let i = 0; i < a.length; i += 2) {
|
|
4275
|
+
let found = false;
|
|
4276
|
+
for (let j = 0; !found && j < b.length; j += 2) {
|
|
4277
|
+
if (neg) {
|
|
4278
|
+
if (a[i][0] + b[j][1] === slices && a[i][1] + b[j][0] === slices && a[i + 1] === b[j + 1]) {
|
|
4279
|
+
found = true;
|
|
4280
|
+
}
|
|
4281
|
+
} else {
|
|
4282
|
+
if (a[i][0] === b[j][0] && a[i][1] === b[j][1] && a[i + 1] === b[j + 1]) {
|
|
4283
|
+
found = true;
|
|
4284
|
+
}
|
|
4285
|
+
}
|
|
4286
|
+
}
|
|
4287
|
+
if (!found) {
|
|
4288
|
+
return true;
|
|
4289
|
+
}
|
|
4290
|
+
}
|
|
4291
|
+
return false;
|
|
4292
|
+
}
|
|
4293
|
+
// TODO: This is only public for testing; can we make it private again?
|
|
4294
|
+
getOrbitsDef(fortwisty, includemoves = true) {
|
|
4295
|
+
const setmoves = [];
|
|
4296
|
+
if (fortwisty) {
|
|
4297
|
+
for (let i = 0; i < this.cubieSetNames.length; i++) {
|
|
4298
|
+
setmoves.push(1);
|
|
4299
|
+
}
|
|
4300
|
+
}
|
|
4301
|
+
const setnames = [];
|
|
4302
|
+
const setdefs = [];
|
|
4303
|
+
const mps = [];
|
|
4304
|
+
const addrot = [];
|
|
4305
|
+
for (let k = 0; k < this.movePlaneSets.length; k++) {
|
|
4306
|
+
const moveset = this.getmovesets(k);
|
|
4307
|
+
mps.push(moveset);
|
|
4308
|
+
if (this.options.addRotations) {
|
|
4309
|
+
addrot.push(1);
|
|
4310
|
+
} else {
|
|
4311
|
+
addrot.push(0);
|
|
4312
|
+
}
|
|
4313
|
+
}
|
|
4314
|
+
const hasrotation = [];
|
|
4315
|
+
for (let k = 0; k < this.movePlaneSets.length; k++) {
|
|
4316
|
+
const slices = this.movePlaneSets[k].length;
|
|
4317
|
+
let sawone = false;
|
|
4318
|
+
const moveset = mps[k];
|
|
4319
|
+
for (let i = 0; i < moveset.length; i += 2) {
|
|
4320
|
+
if (moveset[i][0] === 0 && moveset[i][1] === slices) {
|
|
4321
|
+
sawone = true;
|
|
4322
|
+
}
|
|
4323
|
+
}
|
|
4324
|
+
hasrotation[k] = sawone;
|
|
4325
|
+
}
|
|
4326
|
+
if (this.options.addRotations && (this.options.moveList || this.options.fixedPieceType !== null)) {
|
|
4327
|
+
for (let i = 0; i < this.moveRotations.length; i++) {
|
|
4328
|
+
addrot[i] = 0;
|
|
4329
|
+
}
|
|
4330
|
+
for (let k = 0; k < this.movePlaneSets.length; k++) {
|
|
4331
|
+
if (hasrotation[k]) {
|
|
4332
|
+
addrot[k] = 3;
|
|
4333
|
+
continue;
|
|
4334
|
+
}
|
|
4335
|
+
for (let i = 0; i < this.moveRotations.length; i++) {
|
|
4336
|
+
let nn = this.movePlaneNormals[k];
|
|
4337
|
+
for (let ii = 1; ii * 2 <= this.movesetorders[i]; ii++) {
|
|
4338
|
+
nn = nn.rotatepoint(this.moveRotations[i][0]);
|
|
4339
|
+
if (addrot[i] & ii) {
|
|
4340
|
+
continue;
|
|
4341
|
+
}
|
|
4342
|
+
let found = -1;
|
|
4343
|
+
let neg = false;
|
|
4344
|
+
for (let j = 0; j < this.movePlaneNormals.length; j++) {
|
|
4345
|
+
if (nn.dist(this.movePlaneNormals[j]) < eps3) {
|
|
4346
|
+
found = j;
|
|
4347
|
+
break;
|
|
4348
|
+
} else if (nn.dist(this.movePlaneNormals[j].smul(-1)) < eps3) {
|
|
4349
|
+
found = j;
|
|
4350
|
+
neg = true;
|
|
4351
|
+
break;
|
|
4352
|
+
}
|
|
4353
|
+
}
|
|
4354
|
+
if (found < 0) {
|
|
4355
|
+
throw Error("Could not find rotation");
|
|
4356
|
+
}
|
|
4357
|
+
const cmp = mps[found];
|
|
4358
|
+
if (cmp.length !== mps[k].length || this.movePlaneSets[k].length !== // TODO
|
|
4359
|
+
this.movePlaneSets[found].length || // TODO
|
|
4360
|
+
this.diffmvsets(
|
|
4361
|
+
cmp,
|
|
4362
|
+
mps[k],
|
|
4363
|
+
this.movePlaneSets[found].length,
|
|
4364
|
+
// TODO
|
|
4365
|
+
neg
|
|
4366
|
+
)) {
|
|
4367
|
+
addrot[i] |= ii;
|
|
4368
|
+
}
|
|
4369
|
+
}
|
|
4370
|
+
}
|
|
4371
|
+
}
|
|
4372
|
+
for (let i = 0; i < this.moveRotations.length; i++) {
|
|
4373
|
+
if (addrot[i] === 0) {
|
|
4374
|
+
addrot[i] = 1;
|
|
4375
|
+
} else if (addrot[i] === 1) {
|
|
4376
|
+
if (this.movesetorders[i] > 3) {
|
|
4377
|
+
addrot[i] = 2;
|
|
4378
|
+
} else {
|
|
4379
|
+
addrot[i] = 0;
|
|
4380
|
+
}
|
|
4381
|
+
} else if (addrot[i] === 3) {
|
|
4382
|
+
addrot[i] = 0;
|
|
4383
|
+
} else {
|
|
4384
|
+
throw Error("Impossible addrot val");
|
|
4385
|
+
}
|
|
4386
|
+
}
|
|
4387
|
+
}
|
|
4388
|
+
for (let k = 0; k < this.movePlaneSets.length; k++) {
|
|
4389
|
+
if (addrot[k] !== 0 && !hasrotation[k]) {
|
|
4390
|
+
mps[k].push([0, this.movePlaneSets[k].length]);
|
|
4391
|
+
mps[k].push(addrot[k]);
|
|
4392
|
+
}
|
|
4393
|
+
}
|
|
4394
|
+
for (let k = 0; k < this.movePlaneSets.length; k++) {
|
|
4395
|
+
const moveset = mps[k];
|
|
4396
|
+
const movesetorder = this.movesetorders[k];
|
|
4397
|
+
for (let i = 0; i < moveset.length; i += 2) {
|
|
4398
|
+
for (let j = 0; j < i; j += 2) {
|
|
4399
|
+
if (moveset[i][0] === moveset[j][0] && moveset[i][1] === moveset[j][1]) {
|
|
4400
|
+
throw Error("Redundant moves in moveset.");
|
|
4401
|
+
}
|
|
4402
|
+
}
|
|
4403
|
+
}
|
|
4404
|
+
const allbits = [];
|
|
4405
|
+
for (let i = 0; i < moveset.length; i += 2) {
|
|
4406
|
+
for (let j = moveset[i][0]; j <= moveset[i][1]; j++) {
|
|
4407
|
+
allbits[j] = 1;
|
|
4408
|
+
}
|
|
4409
|
+
}
|
|
4410
|
+
const axiscmoves = this.cmovesBySlice[k];
|
|
4411
|
+
for (let i = 0; i < axiscmoves.length; i++) {
|
|
4412
|
+
if (allbits[i] !== 1) {
|
|
4413
|
+
continue;
|
|
4414
|
+
}
|
|
4415
|
+
const slicecmoves = axiscmoves[i];
|
|
4416
|
+
for (let j = 0; j < slicecmoves.length; j += 2 * movesetorder) {
|
|
4417
|
+
if (this.skipcubie(slicecmoves[j])) {
|
|
4418
|
+
continue;
|
|
4419
|
+
}
|
|
4420
|
+
const ind = this.cubieSetNums[slicecmoves[j]];
|
|
4421
|
+
setmoves[ind] = 1;
|
|
4422
|
+
}
|
|
4423
|
+
}
|
|
4424
|
+
}
|
|
4425
|
+
for (let i = 0; i < this.cubieSetNames.length; i++) {
|
|
4426
|
+
if (!setmoves[i]) {
|
|
4427
|
+
continue;
|
|
4428
|
+
}
|
|
4429
|
+
if (this.omitSet(this.cubieSetNames[i])) {
|
|
4430
|
+
setmoves[i] = 0;
|
|
4431
|
+
continue;
|
|
4432
|
+
}
|
|
4433
|
+
setnames.push(this.cubieSetNames[i]);
|
|
4434
|
+
setdefs.push(
|
|
4435
|
+
new PGOrbitDef(
|
|
4436
|
+
this.cubieOrbitSizes[i],
|
|
4437
|
+
// TODO: refactor to avoid non-null-assertion
|
|
4438
|
+
this.options.fixedOrientation ? 1 : this.orbitOrientations[i]
|
|
4439
|
+
// TODO: refactor to avoid non-null-assertion
|
|
4440
|
+
)
|
|
4441
|
+
);
|
|
4442
|
+
}
|
|
4443
|
+
const solved = [];
|
|
4444
|
+
for (let i = 0; i < this.cubieSetNames.length; i++) {
|
|
4445
|
+
if (!setmoves[i]) {
|
|
4446
|
+
continue;
|
|
4447
|
+
}
|
|
4448
|
+
if (this.omitSet(this.cubieSetNames[i])) {
|
|
4449
|
+
continue;
|
|
4450
|
+
}
|
|
4451
|
+
const p = Array(this.cubieOrbitSizes[i]).fill(-1);
|
|
4452
|
+
const o = [];
|
|
4453
|
+
for (let j = 0; j < this.cubieOrbitSizes[i]; j++) {
|
|
4454
|
+
if (fortwisty) {
|
|
4455
|
+
p[j] = j;
|
|
4456
|
+
} else {
|
|
4457
|
+
const cubie = this.cubieSetCubies[i][j];
|
|
4458
|
+
p[this.cubieOrdNums[cubie]] = this.cubieValueMap[cubie];
|
|
4459
|
+
}
|
|
4460
|
+
o.push(0);
|
|
4461
|
+
}
|
|
4462
|
+
solved.push(
|
|
4463
|
+
new PGOrbit(
|
|
4464
|
+
p,
|
|
4465
|
+
o,
|
|
4466
|
+
this.options.fixedOrientation ? 1 : this.orbitOrientations[i]
|
|
4467
|
+
// TODO: refactor to avoid non-null-assertion
|
|
4468
|
+
)
|
|
4469
|
+
);
|
|
4470
|
+
}
|
|
4471
|
+
const movenames = [];
|
|
4472
|
+
const forcenames = [];
|
|
4473
|
+
const moves = [];
|
|
4474
|
+
const isrots = [];
|
|
4475
|
+
if (includemoves) {
|
|
4476
|
+
for (let k = 0; k < this.movePlaneSets.length; k++) {
|
|
4477
|
+
const moveplaneset = this.movePlaneSets[k];
|
|
4478
|
+
const slices = moveplaneset.length;
|
|
4479
|
+
const moveset = mps[k];
|
|
4480
|
+
const movesetgeo = this.movesetgeos[k];
|
|
4481
|
+
for (let i = 0; i < moveset.length; i += 2) {
|
|
4482
|
+
const movebits = moveset[i];
|
|
4483
|
+
let nameoverride;
|
|
4484
|
+
let inverted = false;
|
|
4485
|
+
if (this.parsedMoveList !== void 0) {
|
|
4486
|
+
for (const parsedmove of this.parsedMoveList) {
|
|
4487
|
+
if (parsedmove[1] !== k) {
|
|
4488
|
+
continue;
|
|
4489
|
+
}
|
|
4490
|
+
let r2 = [];
|
|
4491
|
+
if (parsedmove[4]) {
|
|
4492
|
+
r2 = [parsedmove[2], parsedmove[3]];
|
|
4493
|
+
} else {
|
|
4494
|
+
r2 = [slices - parsedmove[3], slices - parsedmove[2]];
|
|
4495
|
+
}
|
|
4496
|
+
if (r2[0] === movebits[0] && r2[1] === movebits[1]) {
|
|
4497
|
+
nameoverride = parsedmove[0];
|
|
4498
|
+
inverted = !parsedmove[4];
|
|
4499
|
+
}
|
|
4500
|
+
}
|
|
4501
|
+
}
|
|
4502
|
+
if (nameoverride) {
|
|
4503
|
+
movenames.push(nameoverride);
|
|
4504
|
+
forcenames.push(true);
|
|
4505
|
+
} else {
|
|
4506
|
+
const mna = getmovename(movesetgeo, movebits, slices);
|
|
4507
|
+
inverted = mna[1];
|
|
4508
|
+
const movename = mna[0];
|
|
4509
|
+
if (moveset[i + 1] === 1) {
|
|
4510
|
+
movenames.push(movename);
|
|
4511
|
+
} else {
|
|
4512
|
+
movenames.push(movename + moveset[i + 1]);
|
|
4513
|
+
}
|
|
4514
|
+
forcenames.push(false);
|
|
4515
|
+
}
|
|
4516
|
+
isrots.push(movebits[0] === 0 && movebits[1] === slices);
|
|
4517
|
+
const mv = this.getMoveFromBits(
|
|
4518
|
+
movebits,
|
|
4519
|
+
moveset[i + 1],
|
|
4520
|
+
inverted,
|
|
4521
|
+
this.cmovesBySlice[k],
|
|
4522
|
+
setmoves,
|
|
4523
|
+
this.movesetorders[k]
|
|
4524
|
+
// TODO: refactor to avoid non-null-assertion
|
|
4525
|
+
);
|
|
4526
|
+
moves.push(mv);
|
|
4527
|
+
}
|
|
4528
|
+
}
|
|
4529
|
+
}
|
|
4530
|
+
let r = new PGOrbitsDef(
|
|
4531
|
+
setnames,
|
|
4532
|
+
setdefs,
|
|
4533
|
+
new VisibleState(solved),
|
|
4534
|
+
movenames,
|
|
4535
|
+
moves,
|
|
4536
|
+
isrots,
|
|
4537
|
+
forcenames
|
|
4538
|
+
);
|
|
4539
|
+
if (this.options.optimizeOrbits) {
|
|
4540
|
+
r = r.optimize();
|
|
4541
|
+
}
|
|
4542
|
+
if (this.options.scrambleAmount !== 0) {
|
|
4543
|
+
r.scramble(this.options.scrambleAmount);
|
|
4544
|
+
}
|
|
4545
|
+
return r;
|
|
4546
|
+
}
|
|
4547
|
+
getScramble(n = 0) {
|
|
4548
|
+
const od = this.getOrbitsDef(false);
|
|
4549
|
+
return od.toKTransformationData(od.getScrambleTransformation(n));
|
|
4550
|
+
}
|
|
4551
|
+
getMovesAsPerms() {
|
|
4552
|
+
return this.getOrbitsDef(false).moveops.map((_) => _.toPerm());
|
|
4553
|
+
}
|
|
4554
|
+
showcanon(disp) {
|
|
4555
|
+
showcanon(this.getOrbitsDef(false), disp);
|
|
4556
|
+
}
|
|
4557
|
+
getsolved() {
|
|
4558
|
+
const r = [];
|
|
4559
|
+
for (let i = 0; i < this.baseFaceCount; i++) {
|
|
4560
|
+
for (let j = 0; j < this.stickersPerFace; j++) {
|
|
4561
|
+
r.push(i);
|
|
4562
|
+
}
|
|
4563
|
+
}
|
|
4564
|
+
return new Perm(r);
|
|
4565
|
+
}
|
|
4566
|
+
// Given a rotation description that says to align feature1
|
|
4567
|
+
// with a given vector, and then as much as possible feature2
|
|
4568
|
+
// with another given vector, return a Quaternion that
|
|
4569
|
+
// performs this rotation.
|
|
4570
|
+
getOrientationRotation(desiredRotation) {
|
|
4571
|
+
const [feature1name, [x1, y1, z1]] = desiredRotation[0];
|
|
4572
|
+
const direction1 = new Quat(0, x1, -y1, z1);
|
|
4573
|
+
const [feature2name, [x2, y2, z2]] = desiredRotation[1];
|
|
4574
|
+
const direction2 = new Quat(0, x2, -y2, z2);
|
|
4575
|
+
let feature1 = null;
|
|
4576
|
+
let feature2 = null;
|
|
4577
|
+
const feature1geoname = this.swizzler.unswizzle(feature1name);
|
|
4578
|
+
const feature2geoname = this.swizzler.unswizzle(feature2name);
|
|
4579
|
+
for (const gn of this.geometryNormals) {
|
|
4580
|
+
if (feature1geoname === gn[1]) {
|
|
4581
|
+
feature1 = gn[0];
|
|
4582
|
+
}
|
|
4583
|
+
if (feature2geoname === gn[1]) {
|
|
4584
|
+
feature2 = gn[0];
|
|
4585
|
+
}
|
|
4586
|
+
}
|
|
4587
|
+
if (!feature1) {
|
|
4588
|
+
throw Error(`Could not find feature ${feature1name}`);
|
|
4589
|
+
}
|
|
4590
|
+
if (!feature2) {
|
|
4591
|
+
throw Error(`Could not find feature ${feature2name}`);
|
|
4592
|
+
}
|
|
4593
|
+
const r1 = feature1.pointrotation(direction1);
|
|
4594
|
+
const feature2rot = feature2.rotatepoint(r1);
|
|
4595
|
+
const r2 = feature2rot.unproject(direction1).pointrotation(direction2.unproject(direction1));
|
|
4596
|
+
return r2.mul(r1);
|
|
4597
|
+
}
|
|
4598
|
+
getInitial3DRotation() {
|
|
4599
|
+
const basefacecount = this.baseFaceCount;
|
|
4600
|
+
let orientationDescription = null;
|
|
4601
|
+
if (this.options.puzzleOrientation) {
|
|
4602
|
+
orientationDescription = this.options.puzzleOrientation;
|
|
4603
|
+
} else if (this.options.puzzleOrientations) {
|
|
4604
|
+
orientationDescription = this.options.puzzleOrientations[basefacecount];
|
|
4605
|
+
}
|
|
4606
|
+
if (!orientationDescription) {
|
|
4607
|
+
orientationDescription = defaultOrientations()[basefacecount];
|
|
4608
|
+
}
|
|
4609
|
+
if (!orientationDescription) {
|
|
4610
|
+
throw Error("No default orientation?");
|
|
4611
|
+
}
|
|
4612
|
+
return this.getOrientationRotation(orientationDescription);
|
|
4613
|
+
}
|
|
4614
|
+
generate2dmapping(w = 800, h = 500, trim = 10, threed = false, twodshrink = 0.92) {
|
|
4615
|
+
w -= 2 * trim;
|
|
4616
|
+
h -= 2 * trim;
|
|
4617
|
+
function extendedges(a, n) {
|
|
4618
|
+
let dx = a[1][0] - a[0][0];
|
|
4619
|
+
let dy = a[1][1] - a[0][1];
|
|
4620
|
+
const ang = 2 * Math.PI / n;
|
|
4621
|
+
const cosa = Math.cos(ang);
|
|
4622
|
+
const sina = Math.sin(ang);
|
|
4623
|
+
for (let i = 2; i < n; i++) {
|
|
4624
|
+
const ndx = dx * cosa + dy * sina;
|
|
4625
|
+
dy = dy * cosa - dx * sina;
|
|
4626
|
+
dx = ndx;
|
|
4627
|
+
a.push([a[i - 1][0] + dx, a[i - 1][1] + dy]);
|
|
4628
|
+
}
|
|
4629
|
+
}
|
|
4630
|
+
this.genperms();
|
|
4631
|
+
const boundarygeo = this.getboundarygeometry();
|
|
4632
|
+
const face0 = boundarygeo.facenames[0][0];
|
|
4633
|
+
const polyn = face0.length;
|
|
4634
|
+
const net = this.net;
|
|
4635
|
+
if (net === null) {
|
|
4636
|
+
throw Error("No net?");
|
|
4637
|
+
}
|
|
4638
|
+
const edges = {};
|
|
4639
|
+
let minx = 0;
|
|
4640
|
+
let miny = 0;
|
|
4641
|
+
let maxx = 1;
|
|
4642
|
+
let maxy = 0;
|
|
4643
|
+
edges[net[0][0]] = [
|
|
4644
|
+
[1, 0],
|
|
4645
|
+
[0, 0]
|
|
4646
|
+
];
|
|
4647
|
+
extendedges(edges[net[0][0]], polyn);
|
|
4648
|
+
for (const neti of net) {
|
|
4649
|
+
const f0 = neti[0];
|
|
4650
|
+
if (!edges[f0]) {
|
|
4651
|
+
throw Error("Bad edge description; first edge not connected.");
|
|
4652
|
+
}
|
|
4653
|
+
for (let j = 1; j < neti.length; j++) {
|
|
4654
|
+
const f1 = neti[j];
|
|
4655
|
+
if (f1 === "" || edges[f1]) {
|
|
4656
|
+
continue;
|
|
4657
|
+
}
|
|
4658
|
+
edges[f1] = [edges[f0][j % polyn], edges[f0][(j + polyn - 1) % polyn]];
|
|
4659
|
+
extendedges(edges[f1], polyn);
|
|
4660
|
+
}
|
|
4661
|
+
}
|
|
4662
|
+
for (const f in edges) {
|
|
4663
|
+
const es = edges[f];
|
|
4664
|
+
for (const esi of es) {
|
|
4665
|
+
minx = Math.min(minx, esi[0]);
|
|
4666
|
+
maxx = Math.max(maxx, esi[0]);
|
|
4667
|
+
miny = Math.min(miny, esi[1]);
|
|
4668
|
+
maxy = Math.max(maxy, esi[1]);
|
|
4669
|
+
}
|
|
4670
|
+
}
|
|
4671
|
+
const sc = Math.min(w / (maxx - minx), h / (maxy - miny));
|
|
4672
|
+
const xoff = 0.5 * (w - sc * (maxx + minx));
|
|
4673
|
+
const yoff = 0.5 * (h - sc * (maxy + miny));
|
|
4674
|
+
const geos = {};
|
|
4675
|
+
const bg = this.getboundarygeometry();
|
|
4676
|
+
const edges2 = {};
|
|
4677
|
+
const initv = [
|
|
4678
|
+
[sc + xoff, yoff],
|
|
4679
|
+
[xoff, yoff]
|
|
4680
|
+
];
|
|
4681
|
+
edges2[net[0][0]] = initv;
|
|
4682
|
+
extendedges(edges2[net[0][0]], polyn);
|
|
4683
|
+
geos[this.faceNames[0][1]] = this.project2d(0, 0, [
|
|
4684
|
+
new Quat(0, initv[0][0], initv[0][1], 0),
|
|
4685
|
+
new Quat(0, initv[1][0], initv[1][1], 0)
|
|
4686
|
+
]);
|
|
4687
|
+
const connectat = [];
|
|
4688
|
+
connectat[0] = 0;
|
|
4689
|
+
for (const neti of net) {
|
|
4690
|
+
const f0 = neti[0];
|
|
4691
|
+
if (!edges2[f0]) {
|
|
4692
|
+
throw Error("Bad edge description; first edge not connected.");
|
|
4693
|
+
}
|
|
4694
|
+
let gfi = -1;
|
|
4695
|
+
for (let j = 0; j < bg.facenames.length; j++) {
|
|
4696
|
+
if (f0 === bg.facenames[j][1]) {
|
|
4697
|
+
gfi = j;
|
|
4698
|
+
break;
|
|
4699
|
+
}
|
|
4700
|
+
}
|
|
4701
|
+
if (gfi < 0) {
|
|
4702
|
+
throw Error(`Could not find first face name ${f0}`);
|
|
4703
|
+
}
|
|
4704
|
+
const thisface = bg.facenames[gfi][0];
|
|
4705
|
+
for (let j = 1; j < neti.length; j++) {
|
|
4706
|
+
const f1 = neti[j];
|
|
4707
|
+
if (f1 === "" || edges2[f1]) {
|
|
4708
|
+
continue;
|
|
4709
|
+
}
|
|
4710
|
+
edges2[f1] = [
|
|
4711
|
+
edges2[f0][j % polyn],
|
|
4712
|
+
edges2[f0][(j + polyn - 1) % polyn]
|
|
4713
|
+
];
|
|
4714
|
+
extendedges(edges2[f1], polyn);
|
|
4715
|
+
const caf0 = connectat[gfi];
|
|
4716
|
+
const mp = thisface[(caf0 + j) % polyn].sum(thisface[(caf0 + j + polyn - 1) % polyn]).smul(0.5);
|
|
4717
|
+
const epi = findelement(bg.edgenames, mp);
|
|
4718
|
+
const edgename = bg.edgenames[epi][1];
|
|
4719
|
+
const el = splitByFaceNames(edgename, this.faceNames);
|
|
4720
|
+
const gf1 = el[f0 === el[0] ? 1 : 0];
|
|
4721
|
+
let gf1i = -1;
|
|
4722
|
+
for (let k = 0; k < bg.facenames.length; k++) {
|
|
4723
|
+
if (gf1 === bg.facenames[k][1]) {
|
|
4724
|
+
gf1i = k;
|
|
4725
|
+
break;
|
|
4726
|
+
}
|
|
4727
|
+
}
|
|
4728
|
+
if (gf1i < 0) {
|
|
4729
|
+
throw Error("Could not find second face name");
|
|
4730
|
+
}
|
|
4731
|
+
const otherface = bg.facenames[gf1i][0];
|
|
4732
|
+
for (let k = 0; k < otherface.length; k++) {
|
|
4733
|
+
const mp2 = otherface[k].sum(otherface[(k + 1) % polyn]).smul(0.5);
|
|
4734
|
+
if (mp2.dist(mp) <= eps3) {
|
|
4735
|
+
const p1 = edges2[f0][(j + polyn - 1) % polyn];
|
|
4736
|
+
const p2 = edges2[f0][j % polyn];
|
|
4737
|
+
connectat[gf1i] = k;
|
|
4738
|
+
geos[gf1] = this.project2d(gf1i, k, [
|
|
4739
|
+
new Quat(0, p2[0], p2[1], 0),
|
|
4740
|
+
new Quat(0, p1[0], p1[1], 0)
|
|
4741
|
+
]);
|
|
4742
|
+
break;
|
|
4743
|
+
}
|
|
4744
|
+
}
|
|
4745
|
+
}
|
|
4746
|
+
}
|
|
4747
|
+
let hix = 0;
|
|
4748
|
+
let hiy = 0;
|
|
4749
|
+
const rot = this.getInitial3DRotation();
|
|
4750
|
+
for (let face of this.faces) {
|
|
4751
|
+
if (threed) {
|
|
4752
|
+
face = face.rotate(rot);
|
|
4753
|
+
}
|
|
4754
|
+
for (let j = 0; j < face.length; j++) {
|
|
4755
|
+
hix = Math.max(hix, Math.abs(face.get(j).b));
|
|
4756
|
+
hiy = Math.max(hiy, Math.abs(face.get(j).c));
|
|
4757
|
+
}
|
|
4758
|
+
}
|
|
4759
|
+
const sc2 = Math.min(h / hiy / 2, (w - trim) / hix / 4);
|
|
4760
|
+
const mappt2d = (fn, q) => {
|
|
4761
|
+
if (threed) {
|
|
4762
|
+
q = q.rotatepoint(rot);
|
|
4763
|
+
const xoff2 = 0.5 * trim + 0.25 * w;
|
|
4764
|
+
const xmul = this.basePlanes[fn].rotateplane(rot).d < 0 ? 1 : -1;
|
|
4765
|
+
return [
|
|
4766
|
+
trim + w * 0.5 + xmul * (xoff2 - q.b * sc2),
|
|
4767
|
+
trim + h * 0.5 + q.c * sc2
|
|
4768
|
+
];
|
|
4769
|
+
} else {
|
|
4770
|
+
const g = geos[this.faceNames[fn][1]];
|
|
4771
|
+
return [
|
|
4772
|
+
trim + twodshrink * q.dot(g[0]) + g[2].b,
|
|
4773
|
+
trim + h - twodshrink * q.dot(g[1]) - g[2].c
|
|
4774
|
+
];
|
|
4775
|
+
}
|
|
4776
|
+
};
|
|
4777
|
+
return mappt2d;
|
|
4778
|
+
}
|
|
4779
|
+
generatesvg(w = 800, h = 500, trim = 10, threed = false) {
|
|
4780
|
+
const mappt2d = this.generate2dmapping(w, h, trim, threed);
|
|
4781
|
+
function drawedges(id, pts, color) {
|
|
4782
|
+
return `<polygon id="${id}" class="sticker" style="fill: ${color}" points="${pts.map((p) => `${p[0]} ${p[1]}`).join(" ")}"/>
|
|
4783
|
+
`;
|
|
4784
|
+
}
|
|
4785
|
+
const pos = this.getsolved();
|
|
4786
|
+
const colormap = [];
|
|
4787
|
+
const facegeo = [];
|
|
4788
|
+
for (let i = 0; i < this.baseFaceCount; i++) {
|
|
4789
|
+
colormap[i] = this.colors[this.faceNames[i][1]];
|
|
4790
|
+
}
|
|
4791
|
+
for (let i = 0; i < this.faces.length; i++) {
|
|
4792
|
+
const face = this.faces[i];
|
|
4793
|
+
const facenum = Math.floor(i / this.stickersPerFace);
|
|
4794
|
+
const fg = [];
|
|
4795
|
+
for (let j = 0; j < face.length; j++) {
|
|
4796
|
+
fg.push(mappt2d(facenum, face.get(j)));
|
|
4797
|
+
}
|
|
4798
|
+
facegeo.push(fg);
|
|
4799
|
+
}
|
|
4800
|
+
const svg = [];
|
|
4801
|
+
for (let j = 0; j < this.baseFaceCount; j++) {
|
|
4802
|
+
svg.push("<g>");
|
|
4803
|
+
svg.push(`<title>${this.faceNames[j][1]}</title>
|
|
4804
|
+
`);
|
|
4805
|
+
for (let ii = 0; ii < this.stickersPerFace; ii++) {
|
|
4806
|
+
const i = j * this.stickersPerFace + ii;
|
|
4807
|
+
const cubie = this.faceToCubie[i];
|
|
4808
|
+
const cubieori = this.faceToCubieOrd[i];
|
|
4809
|
+
const cubiesetnum = this.cubieSetNums[cubie];
|
|
4810
|
+
const cubieord = this.cubieOrdNums[cubie];
|
|
4811
|
+
const color = this.graybyori(cubie) ? "#808080" : colormap[pos.p[i]];
|
|
4812
|
+
let id = `${this.cubieSetNames[cubiesetnum]}-l${cubieord}-o${cubieori}`;
|
|
4813
|
+
svg.push(drawedges(id, facegeo[i], color));
|
|
4814
|
+
if (this.duplicatedFaces[i]) {
|
|
4815
|
+
for (let jj = 1; jj < this.duplicatedFaces[i]; jj++) {
|
|
4816
|
+
id = `${this.cubieSetNames[cubiesetnum]}-l${cubieord}-o${jj}`;
|
|
4817
|
+
svg.push(drawedges(id, facegeo[i], color));
|
|
4818
|
+
}
|
|
4819
|
+
}
|
|
4820
|
+
}
|
|
4821
|
+
svg.push("</g>");
|
|
4822
|
+
}
|
|
4823
|
+
const html = `<svg id="svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 800 500">
|
|
4824
|
+
<style type="text/css"><![CDATA[.sticker { stroke: #000000; stroke-width: 1px; }]]></style>
|
|
4825
|
+
${svg.join(
|
|
4826
|
+
""
|
|
4827
|
+
)}</svg>`;
|
|
4828
|
+
return html;
|
|
4829
|
+
}
|
|
4830
|
+
// The colorfrac parameter says how much of the face should be
|
|
4831
|
+
// colored (vs dividing lines); we default to 0.77 which seems
|
|
4832
|
+
// to work pretty well. It should be a number between probably
|
|
4833
|
+
// 0.4 and 0.9.
|
|
4834
|
+
get3d(options) {
|
|
4835
|
+
const stickers = [];
|
|
4836
|
+
const rot = this.getInitial3DRotation();
|
|
4837
|
+
const faces = [];
|
|
4838
|
+
const maxdist = 0.52 * this.baseFaces[0].get(0).len();
|
|
4839
|
+
for (let i = 0; i < this.baseFaces.length; i++) {
|
|
4840
|
+
const coords = this.baseFaces[i].rotate(rot);
|
|
4841
|
+
const name = this.faceNames[i][1];
|
|
4842
|
+
faces.push({ coords: toFaceCoords(coords, maxdist), name });
|
|
4843
|
+
}
|
|
4844
|
+
for (let i = 0; i < this.faces.length; i++) {
|
|
4845
|
+
const facenum = Math.floor(i / this.stickersPerFace);
|
|
4846
|
+
const cubie = this.faceToCubie[i];
|
|
4847
|
+
const cubieori = this.faceToCubieOrd[i];
|
|
4848
|
+
const cubiesetnum = this.cubieSetNums[cubie];
|
|
4849
|
+
const cubieord = this.cubieOrdNums[cubie];
|
|
4850
|
+
let color = this.graybyori(cubie) ? options?.darkIgnoredOrbits ? "#222222" : "#808080" : this.colors[this.faceNames[facenum][1]];
|
|
4851
|
+
if (options?.stickerColors) {
|
|
4852
|
+
color = options.stickerColors[i];
|
|
4853
|
+
}
|
|
4854
|
+
const coords = this.faces[i].rotate(rot);
|
|
4855
|
+
stickers.push({
|
|
4856
|
+
coords: toFaceCoords(coords, maxdist),
|
|
4857
|
+
color,
|
|
4858
|
+
orbit: this.cubieSetNames[cubiesetnum],
|
|
4859
|
+
// TODO: refactor to avoid non-null-assertion
|
|
4860
|
+
ord: cubieord,
|
|
4861
|
+
ori: cubieori,
|
|
4862
|
+
face: facenum
|
|
4863
|
+
});
|
|
4864
|
+
let fcoords = coords;
|
|
4865
|
+
if (this.duplicatedFaces[i]) {
|
|
4866
|
+
const rotdist = fcoords.length / this.duplicatedFaces[i];
|
|
4867
|
+
for (let jj = 1; jj < this.duplicatedFaces[i]; jj++) {
|
|
4868
|
+
for (let k = 0; k < rotdist; k++) {
|
|
4869
|
+
fcoords = fcoords.rotateforward();
|
|
4870
|
+
}
|
|
4871
|
+
stickers.push({
|
|
4872
|
+
coords: toFaceCoords(fcoords, maxdist),
|
|
4873
|
+
color,
|
|
4874
|
+
orbit: this.cubieSetNames[cubiesetnum],
|
|
4875
|
+
// TODO: refactor to avoid non-null-assertion
|
|
4876
|
+
ord: cubieord,
|
|
4877
|
+
ori: jj,
|
|
4878
|
+
face: facenum,
|
|
4879
|
+
isDup: true
|
|
4880
|
+
});
|
|
4881
|
+
}
|
|
4882
|
+
}
|
|
4883
|
+
}
|
|
4884
|
+
const grips = [];
|
|
4885
|
+
for (let i = 0; i < this.movesetgeos.length; i++) {
|
|
4886
|
+
const msg = this.movesetgeos[i];
|
|
4887
|
+
const order = this.movesetorders[i];
|
|
4888
|
+
for (const gn of this.geometryNormals) {
|
|
4889
|
+
if (msg[0] === gn[1] && msg[1] === gn[2]) {
|
|
4890
|
+
grips.push({
|
|
4891
|
+
coordinates: toCoords(gn[0].rotatepoint(rot), 1),
|
|
4892
|
+
quantumMove: new Move(msg[0]),
|
|
4893
|
+
order
|
|
4894
|
+
});
|
|
4895
|
+
grips.push({
|
|
4896
|
+
coordinates: toCoords(gn[0].rotatepoint(rot).smul(-1), 1),
|
|
4897
|
+
quantumMove: new Move(msg[2]),
|
|
4898
|
+
order
|
|
4899
|
+
});
|
|
4900
|
+
}
|
|
4901
|
+
}
|
|
4902
|
+
}
|
|
4903
|
+
const twodmapper = this.generate2dmapping(2880, 2160, 0, false, 1);
|
|
4904
|
+
const g = (() => {
|
|
4905
|
+
const irot = rot.invrot();
|
|
4906
|
+
return (facenum, coords) => {
|
|
4907
|
+
let q = new Quat(
|
|
4908
|
+
0,
|
|
4909
|
+
coords[0] * maxdist,
|
|
4910
|
+
-coords[1] * maxdist,
|
|
4911
|
+
coords[2] * maxdist
|
|
4912
|
+
);
|
|
4913
|
+
q = q.rotatepoint(irot);
|
|
4914
|
+
const x = twodmapper(facenum, q);
|
|
4915
|
+
x[0] /= 2880;
|
|
4916
|
+
x[1] = 1 - x[1] / 2160;
|
|
4917
|
+
return x;
|
|
4918
|
+
};
|
|
4919
|
+
})().bind(this);
|
|
4920
|
+
return {
|
|
4921
|
+
stickers,
|
|
4922
|
+
faces,
|
|
4923
|
+
axis: grips,
|
|
4924
|
+
unswizzle: this.unswizzle.bind(this),
|
|
4925
|
+
notationMapper: this.notationMapper,
|
|
4926
|
+
textureMapper: { getuv: g }
|
|
4927
|
+
};
|
|
4928
|
+
}
|
|
4929
|
+
// From the name of a geometric element (face, vertex, edge), get a
|
|
4930
|
+
// normal vector respecting the default orientation. This is useful
|
|
4931
|
+
// to define the initial position of the camera in a 3D scene. The
|
|
4932
|
+
// return value is normalized, so multiply it by the camera distance.
|
|
4933
|
+
// Returns undefined if no such geometric element.
|
|
4934
|
+
getGeoNormal(geoname) {
|
|
4935
|
+
const rot = this.getInitial3DRotation();
|
|
4936
|
+
const grip = this.swizzler.unswizzle(geoname);
|
|
4937
|
+
for (const gn of this.geometryNormals) {
|
|
4938
|
+
if (grip === gn[1]) {
|
|
4939
|
+
const r = toCoords(gn[0].rotatepoint(rot), 1);
|
|
4940
|
+
if (Math.abs(r[0]) < eps3 && Math.abs(r[2]) < eps3) {
|
|
4941
|
+
r[0] = 0;
|
|
4942
|
+
r[2] = 1e-6;
|
|
4943
|
+
}
|
|
4944
|
+
return r;
|
|
4945
|
+
}
|
|
4946
|
+
}
|
|
4947
|
+
return void 0;
|
|
4948
|
+
}
|
|
4949
|
+
getfaceindex(facenum) {
|
|
4950
|
+
const divid = this.stickersPerFace;
|
|
4951
|
+
return Math.floor(facenum / divid);
|
|
4952
|
+
}
|
|
4953
|
+
textForTwizzleExplorer() {
|
|
4954
|
+
return `Faces ${this.basePlaneRotations.length}
|
|
4955
|
+
Stickers per face ${this.stickersPerFace}
|
|
4956
|
+
Short edge ${this.shortestEdge}
|
|
4957
|
+
Cubies ${this.cubies.length}
|
|
4958
|
+
Edge distance ${this.edgeDistance}
|
|
4959
|
+
Vertex distance ${this.vertexDistance}`;
|
|
4960
|
+
}
|
|
4961
|
+
writeSchreierSims(tw) {
|
|
4962
|
+
const os = this.getOrbitsDef(false);
|
|
4963
|
+
const as = os.reassemblySize();
|
|
4964
|
+
tw(`Reassembly size is ${as}`);
|
|
4965
|
+
const ss = schreierSims(this.getMovesAsPerms(), tw);
|
|
4966
|
+
const r = as / ss;
|
|
4967
|
+
tw(`Ratio is ${r}`);
|
|
4968
|
+
}
|
|
4969
|
+
};
|
|
4970
|
+
var PGNotation = class {
|
|
4971
|
+
constructor(pg, od) {
|
|
4972
|
+
this.pg = pg;
|
|
4973
|
+
this.orbitNames = od.orbitnames;
|
|
4974
|
+
}
|
|
4975
|
+
pg;
|
|
4976
|
+
orbitNames;
|
|
4977
|
+
lookupMove(move) {
|
|
4978
|
+
const mv = this.pg.parseMove(move);
|
|
4979
|
+
if (this.pg.parsedMoveList) {
|
|
4980
|
+
let found = false;
|
|
4981
|
+
for (const parsedmove of this.pg.parsedMoveList) {
|
|
4982
|
+
if (parsedmove[1] === mv[1] && parsedmove[2] === mv[2] && parsedmove[3] === mv[3] && parsedmove[4] === mv[4]) {
|
|
4983
|
+
found = true;
|
|
4984
|
+
}
|
|
4985
|
+
}
|
|
4986
|
+
if (!found) {
|
|
4987
|
+
return null;
|
|
4988
|
+
}
|
|
4989
|
+
}
|
|
4990
|
+
let bits = [mv[2], mv[3]];
|
|
4991
|
+
if (!mv[4]) {
|
|
4992
|
+
const slices = this.pg.movePlaneSets[mv[1]].length;
|
|
4993
|
+
bits = [slices - mv[3], slices - mv[2]];
|
|
4994
|
+
}
|
|
4995
|
+
const pgmv = this.pg.getMoveFromBits(
|
|
4996
|
+
bits,
|
|
4997
|
+
mv[5],
|
|
4998
|
+
!mv[4],
|
|
4999
|
+
this.pg.cmovesBySlice[mv[1]],
|
|
5000
|
+
void 0,
|
|
5001
|
+
this.pg.movesetorders[mv[1]]
|
|
5002
|
+
// TODO: refactor to avoid non-null-assertion
|
|
5003
|
+
);
|
|
5004
|
+
const r = PGOrbitsDef.transformToKTransformationData(this.orbitNames, pgmv);
|
|
5005
|
+
return r;
|
|
5006
|
+
}
|
|
5007
|
+
remapKPuzzleDefinition(kpuzzleDefinition) {
|
|
5008
|
+
return remapKPuzzleDefinition(kpuzzleDefinition, this.pg.notationMapper);
|
|
5009
|
+
}
|
|
5010
|
+
};
|
|
5011
|
+
export {
|
|
5012
|
+
PUZZLE_BASE_SHAPES as EXPERIMENTAL_PUZZLE_BASE_SHAPES,
|
|
5013
|
+
PUZZLE_CUT_TYPES as EXPERIMENTAL_PUZZLE_CUT_TYPES,
|
|
5014
|
+
PGNotation as ExperimentalPGNotation,
|
|
5015
|
+
PuzzleGeometry,
|
|
5016
|
+
Quat,
|
|
5017
|
+
getPG3DNamedPuzzles,
|
|
5018
|
+
getPuzzleDescriptionString,
|
|
5019
|
+
getPuzzleGeometryByDesc,
|
|
5020
|
+
getPuzzleGeometryByName,
|
|
5021
|
+
parsePuzzleDescription,
|
|
5022
|
+
schreierSims
|
|
5023
|
+
};
|
|
5024
|
+
//# sourceMappingURL=index.js.map
|