import { PuzzleInterface } from "./constants";
import { randomCSSId } from "./strings";
import { svgnum } from "./utils";
import { CENTER, DOWN, FRONT, Vector3D } from "./vector3d";
const F = 0.1;
function circle(
parts: string[][],
x: number,
y: number,
rad: number,
col: string,
omitStroke = false,
) {
parts.push([
col,
!omitStroke ? col : "",
``,
]);
}
function drawSingleClock(
parts: string[][],
RAD: number,
X: number,
Y: number,
MAT: any,
PINS: any,
{
BLACK,
WHITE,
GRAY,
RED,
}: {
BLACK: string;
WHITE: string;
GRAY: string;
RED: string;
},
) {
const W = RAD * 0.582491582491582;
const RAD_CLOCK = RAD * 0.2020202020202;
const BORDER = RAD * 0.0909090909090909;
const BORDER1 = RAD * 0.02;
const PI = Math.PI;
const TAU = PI * 2;
const arrow = [
new Vector3D(0.0, 1.0),
new Vector3D(0.1491, 0.4056),
new Vector3D(0.0599, 0.2551),
new Vector3D(0.0, 0.0),
new Vector3D(-0.0599, 0.2551),
new Vector3D(-0.1491, 0.4056),
].map((v) => v.mul(RAD_CLOCK));
const circles = [new Vector3D(0.1672), new Vector3D(0.1254)].map((v) =>
v.mul(RAD_CLOCK),
);
const R_PIN = circles[0].x * 2.3;
circle(parts, X, Y, RAD, WHITE);
for (let p = 0; p <= 1; p++) {
const rads = [RAD_CLOCK + BORDER + BORDER1, RAD_CLOCK + BORDER];
const cols = [WHITE, BLACK];
for (let i = -1; i < 2; i += 2) {
for (let j = -1; j < 2; j += 2) {
circle(parts, X + W * i, Y + W * j, rads[p], cols[p]);
}
}
}
circle(parts, X, Y, RAD - BORDER1, BLACK);
for (let i = -1; i < 2; i += 1) {
for (let j = -1; j < 2; j += 1) {
circle(parts, X + W * i, Y + W * j, RAD_CLOCK, WHITE);
const ANCHOR = new Vector3D(X + W * i, Y + W * j);
const angId = MAT[j + 1][i + 1];
const ang = (angId * TAU) / 12;
const pts = arrow.map((v) =>
v.rotate(CENTER, FRONT, PI + ang).add(ANCHOR),
);
const pathParts: string[] = [];
for (let p = 0, maxp = pts.length; p < maxp; p += 1) {
if (p === 0)
pathParts.push(`M ${svgnum(pts[p].x * F)} ${svgnum(pts[p].y * F)}`);
else
pathParts.push(`L ${svgnum(pts[p].x * F)} ${svgnum(pts[p].y * F)}`);
}
pathParts.push("Z");
parts.push([
BLACK,
BLACK,
``,
]);
circle(parts, ANCHOR.x, ANCHOR.y, circles[0].x, BLACK);
circle(parts, ANCHOR.x, ANCHOR.y, circles[1].x, WHITE);
for (let a = 1; a <= 12; a += 1) {
const na = a % 12;
const pt = ANCHOR.add(
DOWN.mul(RAD_CLOCK + BORDER / 2).rotate(
CENTER,
FRONT,
(na * TAU) / 12,
),
);
const r = (circles[0].x / 4) * (na ? 1 : 1.6);
const c = na ? WHITE : RED;
circle(parts, pt.x, pt.y, r, c);
}
if (i <= 0 && j <= 0) {
const val = PINS[(j + 1) * 2 + i + 1];
circle(parts, ANCHOR.x + W / 2, ANCHOR.y + W / 2, R_PIN, GRAY);
circle(
parts,
ANCHOR.x + W / 2,
ANCHOR.y + W / 2,
R_PIN * 0.7,
val ? "#181818" : GRAY,
);
}
}
}
}
function clockImage(cube: PuzzleInterface, DIM: number, ID: string) {
const W = svgnum(DIM * 2.2);
const PINS1 = cube.raw[0];
const PINS2 = cube.raw[0].map(
(e: any, p: number) => !PINS1[((p >> 1) << 1) + 1 - (p & 1)],
);
const MAT = cube.raw[1];
const RAD = DIM / 2;
const BLACK = cube.palette.black;
const WHITE = cube.palette.white;
const GRAY = cube.palette.gray;
const RED = "red";
const parts: string[][] = [];
drawSingleClock(parts, RAD, RAD, RAD, MAT[0], PINS2, {
BLACK,
WHITE,
GRAY,
RED,
});
drawSingleClock(parts, RAD, W - RAD, RAD, MAT[1], PINS1, {
BLACK: WHITE,
WHITE: BLACK,
GRAY,
RED,
});
const fillClass: any = {
[BLACK]: "f0",
[WHITE]: "f1",
[GRAY]: "f2",
[RED]: "f3",
"": "",
};
const strokeClass: any = {
[BLACK]: "s0",
[WHITE]: "s1",
[GRAY]: "s2",
[RED]: "s3",
"": "",
};
const cls = parts.map((p) => {
// let pos = p[2].indexOf(" ");
// let tagName = p[2].slice(0, pos);
let classes: string[] = [];
if (p[0]) classes.push(fillClass[p[0]]);
if (p[1]) classes.push(strokeClass[p[1]]);
return classes
.map((c) => c.trim())
.filter((c) => c)
.join(" ");
// return tagName + `${cl ? ` class="${cl}"` : ""}` + p[2].slice(pos);
});
let groups: string[][] = parts.reduce((acc, e, p) => {
if (p == 0) return [[cls[p], e[2]]];
if (cls[p] === acc[acc.length - 1][0]) {
acc[acc.length - 1].push(e[2]);
} else {
acc.push([cls[p], e[2]]);
}
return acc;
}, [] as string[][]);
return [
``,
].join("");
}
export function CLOCK(): PuzzleInterface {
const clock: PuzzleInterface = {
move: () => true,
palette: {
black: "#181818",
white: "#aaa",
gray: "#7f7f7f",
},
};
const ID = randomCSSId();
const pins: boolean[] = [false, false, false, false];
const clocks = [
[
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
],
[
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
],
];
const add = function (i: number, j: number, k: number, val: number) {
clocks[i][j][k] = (((clocks[i][j][k] + val) % 12) + 12) % 12;
};
const mat = [
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
];
clock.move = function (moves: any[]) {
let first = true;
let upFace = 0;
for (let i = 0, maxi = moves.length; i < maxi; i += 1) {
const mv = moves[i];
const pinCode = mv[0];
const up = mv[1];
const down = mv[2];
if (mv[0] === -1) {
upFace ^= 1;
continue;
}
for (let x = 0; x < 3; x += 1) {
for (let y = 0; y < 3; y += 1) {
mat[x][y] = 0;
}
}
for (let j = 0, mask = 8; j < 4; j += 1, mask >>= 1) {
if (isNaN(up) || isNaN(down)) {
if (first) {
pins.length = 0;
pins.push(false, false, false, false);
first = false;
}
pins[j] = pinCode & mask ? true : pins[j];
} else {
pins[j] = !!(pinCode & mask);
}
if (pins[j]) {
const x = j >> 1;
const y = j & 1;
mat[x][y] = mat[x + 1][y] = mat[x][y + 1] = mat[x + 1][y + 1] = 1;
}
}
if (!isNaN(up) && !isNaN(down)) {
for (let x = 0; x < 3; x += 1) {
for (let y = 0; y < 3; y += 1) {
if (mat[x][y]) {
add(upFace, x, y, up);
if ((x & 1) == 0 && (y & 1) == 0) {
add(1 - upFace, x, 2 - y, -up);
}
}
}
}
}
}
};
clock.getImage = () => clockImage(clock, 100, ID);
clock.raw = [pins, clocks];
return clock;
}