この記事はBeeXのアドベントカレンダーの記事です。みんな真面目なネタを書くかもしれないので、私は早めに軽いネタを書いておこうと思います。少し前に書いたお遊びプログラムのメモです。
私は10年に1回ぐらいの頻度でルービックキューブをネタにしたプログラムを書いています。
ブラウザ上で動かすことができます。マウスでぐりぐりすると、見る角度だけ変えられます。キューブの絵の下にある青いボタンが回転操作をするためのボタンです。(本当はマウスでキューブを回転操作できるとよかったのですが、そこまでは到達せず)
以下のリンクで直接試すことができます。
JavaScriptで書きました。突貫で作っています。コードが汚いのはご容赦ください。three.jsという3Dオブジェクトを描くJavaScriptのライブラリを利用しています。
また10年後にルービックキューブのなにかを書きたくなると思うので、本記事のこれ以降は10年後の自分に向けたメモです。
キューブ配置のデータ構造
各面の9個のセルの色を6面、計54個の情報を配列として持たせます。要素数54の配列は展開図にするとこのような順番に並べます。
色はルービックキューブがそろったときの状態です。シャッフルされると色はバラバラになってますが、配列の要素の位置は固定です。
配列の要素は色情報が主ですが、このサンプルプログラムではthree.jsのオブジェクトもここで保持させました。
採用しなかった出た構造
最初は、2面以上あるキューブはいつも必ず一体となって動くので、各キューブがどこにあるのかを保持しようと考えていました。例えば以下の図でいうと青と赤のキューブは、ルービックキューブをどう操作しようが必ずいっしょにくっついて移動します。なので青と赤を別々にせずに1つのオブジェクトとして保持しようとしました。しかし、位置情報や回転操作が複雑になりそうで、結局上記54要素の配列で表現することにしました。
回転操作情報
X軸、Y軸、Z軸を適当に決めます。今回は、上のルービックキューブの絵でいうと、赤面の方向をX軸、黄色の方向をY軸、青の方向をZ軸としました。
回転操作を表現するには、まず回転の軸をx, y, zのいずれかで指定します。
次に回転の向きを指定します。各軸で正の方向から見て時計回りを+1、反時計回りを-1とします。回転できる層が3つありますので、それぞれの層に対して+1または-1または0を指定することにします。層は各軸の負の方向から並べます。
この説明ですとわかりづらいのですが、ルービックキューブの回転記号で表すと次のようになります。
R
: {axis: "x", direction: 0, 0, +1}
R'
: {axis: "x", direction: 0, 0, -1}
L
: {axis: "x", direction: -1, 0, 0}
L'
: {axis: "x", direction: +1, 0, 0}
U
: {axis: "y", direction: 0, 0, +1}
U'
: {axis: "y", direction: 0, 0, -1}
F
: {axis: "z", direction: 0, 0, +1}
F'
: {axis: "z", direction: 0, 0, -1}
M
: {axis: "x", direction: 0, -1, 0}
M'
: {axis: "x", direction: 0, +1, 0}
90度の回転までしか表現しません。180度の回転は、回転操作2回分とします。
回転操作はキューで保存します。キューの先頭にある回転操作情報が現在アニメーション中のものです。回転アニメーションが完了したらキューの先頭を削除して次のアニメーションに進みます。キューが空になるとアニメーションが停止します。
回転操作ボタンを押すと、キューの最後に回転操作情報が追加されます。
回転アニメーション中
回転操作キューの先頭がアニメーション中の回転操作です。
回転中は、
- 要素数54の配列情報
- 回転操作情報(キューの先頭)
- 時刻情報から計算できる回転角度
から3Dオブジェクトの位置や角度を計算します。ここでは三角関数や行列で自力で計算しました。
90度まで回転が完了したら、要素数54のキューブ配置情報を再配置して完了です。
回転操作による再配置
要素数54の配置情報は展開図に基づいており、軸や層による対称性に乏しいため、回転操作ごとの再配置ルールを地道に書きました。ここがコーディングするうえでは面倒でしたし、デバッグもたいへんでした。
回転方向+1と-1とありますが、+1だけ実装して、-1は+1を3回繰り返すことにしています。
半自動で揃えるためのボタン
ルービックキューブを揃えるために次になにをしたらいいのかをある程度自動で判定しています。たくさんある青いボタン群の下半分がそれです。初心者向けのルービックキューブの揃え方を調べて、それを自動で判定させられるように大量のif文を書きました。揃えるための回転操作を判定して、それを回転操作情報のキューの最後に追加します。
手早く実装したために、ルービックキューブの手数はとても多くなっています。
アニメーション中も次の操作ボタンを押せるようになっていますが、これはアニメーションが完了した状態の配置状態を先に作っておいて、それをもとに判定しています。
以上。