diff --git a/src/cubing/puzzles/events.ts b/src/cubing/puzzles/events.ts index 8a72066a8..cb82c59d2 100644 --- a/src/cubing/puzzles/events.ts +++ b/src/cubing/puzzles/events.ts @@ -45,6 +45,10 @@ export const twizzleEvents: Record = { puzzleID: "redi_cube", eventName: "Redi Cube", }, + loopover: { + puzzleID: "loopover", + eventName: "Loopover", + }, }; /** @category Event Info */ diff --git a/src/cubing/puzzles/implementations/dynamic/unofficial/loopover.kpuzzle.json.ts b/src/cubing/puzzles/implementations/dynamic/unofficial/loopover.kpuzzle.json.ts new file mode 100644 index 000000000..49d83c116 --- /dev/null +++ b/src/cubing/puzzles/implementations/dynamic/unofficial/loopover.kpuzzle.json.ts @@ -0,0 +1,120 @@ +import type { KPuzzleDefinition } from "../../../../kpuzzle"; + +const o = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +]; + +export const loopoverJSON: KPuzzleDefinition = { + name: "2x2x2", + orbits: [{ orbitName: "SQUARES", numPieces: 25, numOrientations: 3 }], + defaultPattern: { + SQUARES: { + pieces: [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, + ], + orientation: o, + }, + }, + moves: { + U: { + SQUARES: { + permutation: [ + 1, 2, 3, 4, 0, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, + ], + orientationDelta: o, + }, + }, + "2U": { + SQUARES: { + permutation: [ + 0, 1, 2, 3, 4, 6, 7, 8, 9, 5, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, + ], + orientationDelta: o, + }, + }, + "3U": { + SQUARES: { + permutation: [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 10, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, + ], + orientationDelta: o, + }, + }, + "4U": { + SQUARES: { + permutation: [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 18, 19, 15, + 20, 21, 22, 23, 24, + ], + orientationDelta: o, + }, + }, + "5U": { + SQUARES: { + permutation: [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 21, 22, 23, 24, 20, + ], + orientationDelta: o, + }, + }, + R: { + SQUARES: { + permutation: [ + 0, 1, 2, 3, 9, 5, 6, 7, 8, 14, 10, 11, 12, 13, 19, 15, 16, 17, 18, 24, + 20, 21, 22, 23, 4, + ], + orientationDelta: o, + }, + }, + "2R": { + SQUARES: { + permutation: [ + 0, 1, 2, 8, 4, 5, 6, 7, 13, 9, 10, 11, 12, 18, 14, 15, 16, 17, 23, 19, + 20, 21, 22, 3, 24, + ], + orientationDelta: o, + }, + }, + "3R": { + SQUARES: { + permutation: [ + 0, 1, 7, 3, 4, 5, 6, 12, 8, 9, 10, 11, 17, 13, 14, 15, 16, 22, 18, 19, + 20, 21, 2, 23, 24, + ], + orientationDelta: o, + }, + }, + "4R": { + SQUARES: { + permutation: [ + 0, 6, 2, 3, 4, 5, 11, 7, 8, 9, 10, 16, 12, 13, 14, 15, 21, 17, 18, 19, + 20, 1, 22, 23, 24, + ], + orientationDelta: o, + }, + }, + "5R": { + SQUARES: { + permutation: [ + 5, 1, 2, 3, 4, 10, 6, 7, 8, 9, 15, 11, 12, 13, 14, 20, 16, 17, 18, 19, + 0, 21, 22, 23, 24, + ], + orientationDelta: o, + }, + }, + }, + derivedMoves: { + L: "5R'", + "2L": "4R'", + "3L": "3R'", + "4L": "2R'", + "5L": "R'", + E: "3D", + M: "3L", + }, +}; diff --git a/src/cubing/puzzles/implementations/dynamic/unofficial/loopover.kpuzzle.svg.ts b/src/cubing/puzzles/implementations/dynamic/unofficial/loopover.kpuzzle.svg.ts new file mode 100644 index 000000000..bca43fe22 --- /dev/null +++ b/src/cubing/puzzles/implementations/dynamic/unofficial/loopover.kpuzzle.svg.ts @@ -0,0 +1,40 @@ +export const loopoverSVG = ` + + 3x3x3 LL + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +`; diff --git a/src/cubing/puzzles/implementations/dynamic/unofficial/puzzles-dynamic-unofficial.ts b/src/cubing/puzzles/implementations/dynamic/unofficial/puzzles-dynamic-unofficial.ts index 46cd4f516..99d0d025d 100644 --- a/src/cubing/puzzles/implementations/dynamic/unofficial/puzzles-dynamic-unofficial.ts +++ b/src/cubing/puzzles/implementations/dynamic/unofficial/puzzles-dynamic-unofficial.ts @@ -2,3 +2,5 @@ export { ftoSVG } from "./fto.kpuzzle.svg"; export { kilominxSVG } from "./kilominx.kpuzzle.svg"; export { rediCubeJSON } from "./redi_cube.kpuzzle.json"; export { rediCubeSVG } from "./redi_cube.kpuzzle.svg"; +export { loopoverJSON } from "./loopover.kpuzzle.json"; +export { loopoverSVG } from "./loopover.kpuzzle.svg"; diff --git a/src/cubing/puzzles/implementations/loopover/index.ts b/src/cubing/puzzles/implementations/loopover/index.ts new file mode 100644 index 000000000..2610d1439 --- /dev/null +++ b/src/cubing/puzzles/implementations/loopover/index.ts @@ -0,0 +1,21 @@ +import { KPuzzle } from "../../../kpuzzle"; +import { getCached } from "../../async/lazy-cached"; +import type { PuzzleLoader } from "../../PuzzleLoader"; + +export const loopover: PuzzleLoader = { + id: "loopover", + fullName: "Loopover", + inventedBy: ["Cary Huang"], + inventionYear: 2018, + kpuzzle: getCached( + async () => + new KPuzzle( + (await import("../dynamic/unofficial/puzzles-dynamic-unofficial")) + .loopoverJSON, + ), + ), + svg: async () => { + return (await import("../dynamic/unofficial/puzzles-dynamic-unofficial")) + .loopoverSVG; + }, +}; diff --git a/src/cubing/puzzles/index.ts b/src/cubing/puzzles/index.ts index fc7bf7a2e..d6365899d 100644 --- a/src/cubing/puzzles/index.ts +++ b/src/cubing/puzzles/index.ts @@ -15,6 +15,7 @@ import type { PuzzleLoader } from "./PuzzleLoader"; import { rediCube } from "./implementations/redi-cube"; import { cube4x4x4 } from "./implementations/4x4x4"; import { melindas2x2x2x2 } from "./implementations/melindas2x2x2x2"; +import { loopover } from "./implementations/loopover"; /** @category All Puzzles */ export const puzzles: Record = { @@ -62,4 +63,5 @@ export const puzzles: Record = { kilominx, redi_cube: rediCube, melindas2x2x2x2, + loopover, }; diff --git a/src/cubing/twisty/model/props/puzzle/structure/PuzzleIDRequestProp.ts b/src/cubing/twisty/model/props/puzzle/structure/PuzzleIDRequestProp.ts index daf454ad9..8dd2309df 100644 --- a/src/cubing/twisty/model/props/puzzle/structure/PuzzleIDRequestProp.ts +++ b/src/cubing/twisty/model/props/puzzle/structure/PuzzleIDRequestProp.ts @@ -24,6 +24,7 @@ export const puzzleIDs = { kilominx: true, redi_cube: true, melindas2x2x2x2: true, + loopover: true, }; export type PuzzleID = keyof typeof puzzleIDs; diff --git a/src/cubing/twisty/model/props/viewer/VisualizationStrategyProp.ts b/src/cubing/twisty/model/props/viewer/VisualizationStrategyProp.ts index 80fc22f2e..943dc22e4 100644 --- a/src/cubing/twisty/model/props/viewer/VisualizationStrategyProp.ts +++ b/src/cubing/twisty/model/props/viewer/VisualizationStrategyProp.ts @@ -24,6 +24,7 @@ export class VisualizationStrategyProp extends TwistyPropDerived< case "square1": case "redi_cube": case "melindas2x2x2x2": + case "loopover": return "2D"; case "3x3x3": switch (inputs.visualizationRequest) { diff --git a/src/sites/alpha.twizzle.net/edit/app.ts b/src/sites/alpha.twizzle.net/edit/app.ts index 7e50cc468..a08d84a07 100644 --- a/src/sites/alpha.twizzle.net/edit/app.ts +++ b/src/sites/alpha.twizzle.net/edit/app.ts @@ -71,6 +71,7 @@ const SCRAMBLE_EVENTS: Partial> = { master_tetraminx: "master_tetraminx", kilominx: "kilominx", redi_cube: "redi_cube", + loopover: "loopover", }; export class App { @@ -635,7 +636,9 @@ class ControlPane { "clock", "square1", "redi_cube", + "loopover", "melindas2x2x2x2", + "loopover", ].includes(puzzle); this.toolGrid.setButtonEnabled( "solve", diff --git a/src/sites/alpha.twizzle.net/edit/supported-puzzles.ts b/src/sites/alpha.twizzle.net/edit/supported-puzzles.ts index 81f07b231..63d96c6b1 100644 --- a/src/sites/alpha.twizzle.net/edit/supported-puzzles.ts +++ b/src/sites/alpha.twizzle.net/edit/supported-puzzles.ts @@ -113,6 +113,11 @@ const puzzleData: Partial< optgroup: OptGroup.Other, symbol: GeometrySymbol.Square, }, + loopover: { + "2D": true, + optgroup: OptGroup.Other, + symbol: GeometrySymbol.Square, + }, }; const puzzleGroups: Record = {};