B9 implemented: rotate 90 degrees about all 3 axes. Keys: x -> abou…
B9 implemented: rotate 90 degrees about all 3 axes. Keys: x -> about X, y -> about Y, z -> about Z. Core: assembly/index.ts rotate(axis) - rigid 90deg transform of the cubelet offsets, re-anchored so the bounding-box min corner stays at the origin (no wall-kick), then an all-or-nothing bounds check. Committed 0efdb54. As required, evidence is CORE-LEVEL (the symmetric cube shows NO visible change by design). Pure core: scripts/core-test.mjs; real keys re-affirmed in headless Chrome.
ROTATION RULES (90deg, applied to cubelet offsets then re-anchor min->0):
axis X: (y,z) -> (z, -y)
axis Y: (x,z) -> (z, -x)
axis Z: (x,y) -> (y, -x)
E1 (three axes, 90deg, one press = one rotation): keys x/y/z each rotate 90deg about the named axis.
PASS: B9.E1/E3 rotate X #1..#3 legal & in-bounds (and same for Y, Z)
E2 + E5b (correct cell transform, CORE-LEVEL, explicit before/after on an ASYMMETRIC probe): a 3-cubelet Z-bar probe loaded at origin (2,6,1):
before: [[2,6,1],[2,6,2],[2,6,3]]
PASS: B9.E2/E5b rotateX probe -> Y-bar [[2,6,1],[2,7,1],[2,8,1]] (rule (y,z)->(z,-y))
PASS: B9.E2/E5b rotateY probe -> X-bar [[2,6,1],[3,6,1],[4,6,1]] (rule (x,z)->(z,-x))
These match the hand-computed sets exactly. (No screenshot offered, per the directive.)
E3 (cell-aligned + in bounds on legal rotation):
PASS: B9.E3 X/Y/Z cubelets in bounds & integer (every rotation)
PASS: B9.E3 all cubelets remain integer & in-bounds after rotations (browser)
All cubelets stay on integer coords with 0<=x<5, 0<=z<5, 0<=y<12.
E4 (bounds rejection - piece unchanged, no wall-kick): Z-bar at origin (3,6,1); a rotateY would need x cells 3,4,5 (5 is out):
PASS: B9.E4 out-of-bounds rotation REJECTED, piece unchanged ([[3,6,1],[3,6,2],[3,6,3]])
Cell set byte-identical before and after; no partial rotation, no kick. (The 2x2x2 game cube keeps offsets in {0,1} under rotation so it is in-bounds anywhere by construction; rejection is demonstrated with the asymmetric probe through the SAME code path.)
E5 (PROVE the transform is real, not a symmetric no-op): used BOTH methods.
(a) round-trip per-cubelet on the cube - 4 rotations return cubelet#0 to start, and it visits 4 DISTINCT positions in between:
PASS: B9.E5a rotate X x4 returns cubelet#0 to start ([1,10,1])
PASS: B9.E5a rotate X actually permutes cubelet#0 through 4 positions (transform is real, not skipped)
(same for Y and Z)
(b) asymmetric probe coordinate check - see E2/E5b above (Z-bar -> Y-bar / X-bar matches hand-computed).
E6 (rotation only - no translation, no drop):
PASS: B9.E6 rotation does not translate the piece (origin X/Z unchanged)
PASS: B9.E6 rotation does not trigger/alter a gravity step
PASS: B9.E6 gravity cadence continues unaffected after rotations
E7 (integration): while the piece falls, real x/y/z keys perform legal in-bounds rotations (core-asserted), out-of-bounds rotations are rejected leaving the piece unchanged, and gravity cadence continues. NOTE (by design): the 2x2x2 cube is rotation-symmetric so there is NO visible change - verification is core-level, as the expectation requires.
PASS: B9.E7 real x/y/z rotations legal & cube set invariant (NO visible change EXPECTED - verified core-level)
PASS: B9.E6/E7 rotations don't move/rotate-stall gravity; 500ms step still fires
VERIFY: ALL CHECKS PASSED