では、ルービックキューブのアプリを実装してみましょう。以下が、実装する機能とルールとなります。
【ルール】
- 3x3のルービックキューブを実装する。
- 操作できる面は、上下左右前後の6面とする。
- 1面の状態を変える操作は、時計回りと反時計回りの2種類とする。
- 実際のルービックキューブと同じように、同じ面を連続して動かすことができる。
- キューブを揃えることができたら、クリアのメッセージを表示する。
【ジョーク】
- "ルービックキューブは、真実を見つけ出すための哲学的なトレーニングだ。" by Erno Rubik
【実装例】
以下が、実装例です。
```
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Rubik's Cube</title>
<style>
table {
border-collapse: collapse;
margin: 0 auto;
}
td {
width: 30px;
height: 30px;
border: 2px solid black;
text-align: center;
vertical-align: middle;
}
</style>
</head>
<body>
<h1>Rubik's Cube</h1>
<table>
<tr>
<td id="ul"></td>
<td id="u"></td>
<td id="ur"></td>
</tr>
<tr>
<td id="l"></td>
<td id="c"></td>
<td id="r"></td>
</tr>
<tr>
<td id="dl"></td>
<td id="d"></td>
<td id="dr"></td>
</tr>
</table>
<br>
<button onclick="shuffle()">Shuffle</button>
<button onclick="undo()">Undo</button>
<button onclick="reset()">Reset</button>
<script>
const ul = document.getElementById("ul");
const u = document.getElementById("u");
const ur = document.getElementById("ur");
const l = document.getElementById("l");
const c = document.getElementById("c");
const r = document.getElementById("r");
const dl = document.getElementById("dl");
const d = document.getElementById("d");
const dr = document.getElementById("dr");
const faces = [ul, u, ur, l, c, r, dl, d, dr];
let state = ["w", "w", "w", "w", "w", "w", "w", "w", "w",
"o", "o", "o", "o", "o", "o", "o", "o", "o",
"r", "r", "r", "r", "r", "r", "r", "r", "r",
"g", "g", "g", "g", "g", "g", "g", "g", "g",
"b", "b", "b", "b", "b", "b", "b", "b", "b",
"y", "y", "y", "y", "y", "y", "y", "y", "y"];
const moves = {
u: [0, 1, 2, 9, 10, 11, 18, 19, 20],
r: [2, 5, 8, 11, 14, 17, 20, 23, 26],
d: [18, 19, 20, 27, 28, 29, 36, 37, 38],
l: [0, 3, 6, 9, 12, 15, 18, 21, 24],
f: [6, 7, 8, 15, 16, 17, 24, 25, 26],
b: [0, 1, 2, 27, 28, 29, 33, 34, 35]
};
const rotate = (indices, dir) => {
const oldState = [...state];
indices.forEach((i, idx) => {
const j = indices[(idx + (dir === "cw" ? 2 : 1)) % indices.length];
state[j] = oldState[i];
});
update();
};
const update = () => {
faces.forEach((face, idx) => {
face.style.backgroundColor = state[idx];
});
if (isSolved()) {
alert("Congratulations! You solved the Rubik's cube!");
}
};
const isSolved = () => {
return state.every((color, idx) => {
return color === state[idx % 9] && color === state[idx % 9 + 9] && color === state[idx % 9 + 18];
});
};
const shuffle = () => {
for (let i = 0; i < 20; i++) {
const faceKeys = Object.keys(moves);
const randomFace = faceKeys[Math.floor(Math.random() * faceKeys.length)];
const randomDir = ["cw", "ccw"][Math.floor(Math.random() * 2)];
rotate(moves[randomFace], randomDir);
}
};
const undo = () => {
state = undoStack.pop();
update();
};
const reset = () => {
state = ["w", "w", "w", "w", "w", "w", "w", "w", "w",
"o", "o", "o", "o", "o", "o", "o", "o", "o",
"r", "r", "r", "r", "r", "r", "r", "r", "r",
"g", "g", "g", "g", "g", "g", "g", "g", "g",
"b", "b", "b", "b", "b", "b", "b", "b", "b",
"y", "y", "y", "y", "y", "y", "y", "y", "y"];
update();
};
const undoStack = [];
const facesForUndo = [];
facesForUndo.push([...faces.map(face => face.style.backgroundColor)]);
faces.forEach(face => {
face.addEventListener("click", () => {
const indices = moves[face.id];
if (facesForUndo.length === 0 || !facesForUndo[facesForUndo.length - 1].every((color, idx) => {
return color === faces[idx].style.backgroundColor;
})) {
undoStack.push([...state]);
facesForUndo.push([...faces.map(face => face.style.backgroundColor)]);
}
rotate(indices, "cw");
});
face.addEventListener("contextmenu", (e) => {
e.preventDefault();
const indices = moves[face.id];
if (facesForUndo.length === 0 || !facesForUndo[facesForUndo.length - 1].every((color, idx) => {
return color === faces[idx].style.backgroundColor;
})) {
undoStack.push([...state]);
facesForUndo.push([...faces.map(face => face.style.backgroundColor)]);
}
rotate(indices, "ccw");
});
});
reset();
</script>
</body>
</html>
```
このプログラムは、6面それぞれに対して、シンプルなJavaScriptの操作を実装しています。面を選択し、右クリックまたは左クリックで時計回り・反時計回りを選択することができ、キューブをシャッフル、リセット、アンドゥ機能があります。
また、右下に配置したボタンは、シャッフル、リセット、アンドゥの機能を実装しています。