blog: Rubiks' Cube Scrambler post
Implement the rubiks-cube-scrambler custom element, including JS and template files. Put these things in the body-extras.html partial that the termlite theme added. resource-builders: Update submodule commit termlite: Update submodule commit
This commit is contained in:
parent
cb16a35020
commit
122e55b1fa
7 changed files with 221 additions and 2 deletions
123
assets/scripts/rubiks/scrambler.js
Normal file
123
assets/scripts/rubiks/scrambler.js
Normal file
|
@ -0,0 +1,123 @@
|
|||
class RubiksCubeScrambler extends HTMLElement {
|
||||
static #RandomMoveHysteresisMaxLength = 2;
|
||||
|
||||
#shadowRoot;
|
||||
#movesListElement;
|
||||
|
||||
#numberOfMovesToGenerate = 25;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.#shadowRoot = this.attachShadow({ mode: "open" });
|
||||
}
|
||||
|
||||
scramble() {
|
||||
console.log("Randomizing Rubik's cube...");
|
||||
|
||||
const movesList = this.#movesListElement;
|
||||
|
||||
while (movesList.childElementCount > this.#numberOfMovesToGenerate) {
|
||||
movesList.removeChild(movesList.lastChild);
|
||||
}
|
||||
|
||||
let randomMoveHysteresis = [];
|
||||
|
||||
for (let i = 0; i < this.#numberOfMovesToGenerate; i++) {
|
||||
const randomMove = this.#randomMove(randomMoveHysteresis);
|
||||
|
||||
let moveItem;
|
||||
if (i < movesList.childElementCount) {
|
||||
moveItem = movesList.children[i];
|
||||
} else {
|
||||
moveItem = document.createElement("li");
|
||||
movesList.appendChild(moveItem);
|
||||
}
|
||||
|
||||
moveItem.classList.add("scrambler__move");
|
||||
moveItem.classList.remove("scrambler__move--start", "scrambler__move--end");
|
||||
|
||||
if (randomMove.includes("2")) {
|
||||
moveItem.classList.add("scrambler__move--start");
|
||||
} else if (randomMove.includes("'")) {
|
||||
moveItem.classList.add("scrambler__move--end");
|
||||
}
|
||||
|
||||
moveItem.innerText = randomMove;
|
||||
}
|
||||
}
|
||||
|
||||
#randomMove(hysteresis) {
|
||||
const faces = "FBLRUD";
|
||||
|
||||
let move;
|
||||
do {
|
||||
move = faces.charAt(Math.floor(Math.random() * faces.length));
|
||||
} while (hysteresis && hysteresis.includes(move));
|
||||
|
||||
if (hysteresis) {
|
||||
hysteresis.unshift(move);
|
||||
while (hysteresis.length > RubiksCubeScrambler.#RandomMoveHysteresisMaxLength) {
|
||||
hysteresis.pop();
|
||||
}
|
||||
}
|
||||
|
||||
const modifierFactor = Math.random();
|
||||
if (modifierFactor < 0.33333) {
|
||||
move = "2" + move;
|
||||
} else if (modifierFactor < 0.666666) {
|
||||
move = move + "'";
|
||||
}
|
||||
|
||||
return move;
|
||||
}
|
||||
|
||||
#removeAllMoves() {
|
||||
const element = this.#movesListElement;
|
||||
while (element.hasChildNodes()) {
|
||||
element.removeChild(element.lastChild);
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Custom Element
|
||||
|
||||
connectedCallback() {
|
||||
let template = document.getElementById("rubiks-cube-scrambler-template");
|
||||
console.assert(template, "Couldn't find RubiksCubeScrambler component template in the document");
|
||||
|
||||
const shadowRoot = this.#shadowRoot;
|
||||
shadowRoot.appendChild(template.content.cloneNode(true));
|
||||
|
||||
this.#movesListElement = shadowRoot.querySelector(".scrambler__move-list");
|
||||
|
||||
shadowRoot
|
||||
.querySelector("button[name='scramble']")
|
||||
.addEventListener("click", () => this.scramble());
|
||||
|
||||
const patternLengthInputElement = shadowRoot.querySelector(".scrambler__pattern-length > input");
|
||||
patternLengthInputElement.value = this.#numberOfMovesToGenerate;
|
||||
patternLengthInputElement.addEventListener("input", event => {
|
||||
try {
|
||||
const integerValue = parseInt(event.target.value);
|
||||
this.#numberOfMovesToGenerate = integerValue;
|
||||
} catch (e) {
|
||||
console.error("Non-integer value of pattern length field", e);
|
||||
}
|
||||
});
|
||||
|
||||
this.scramble();
|
||||
}
|
||||
|
||||
attributeChangedCallback(name, oldValue, newValue) {
|
||||
console.debug("RubiksCubeScrambler attribute changed", name, oldValue, newValue);
|
||||
if (name === "count") {
|
||||
try {
|
||||
let newIntValue = parseInt(newValue);
|
||||
this.#numberOfMovesToGenerate = newIntValue;
|
||||
} catch (e) {
|
||||
console.error("`count` attribute should have an integer value.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.customElements.define("rubiks-cube-scrambler", RubiksCubeScrambler);
|
Loading…
Add table
Add a link
Reference in a new issue