Tukubaiで作るルービックキューブプログラム

レシピの概要

Open usp Tukubai※1で開発された3x3のルービックキューブシミュレータ。usp Tukubaiが業務システムの構築のみならず数理的操作にも使用出来ることを示すサンプル。詳しい解説はUSP MAGAZINE Vol.5を参照のこと※2

遊び方

プログラムをダウンロードし、同梱されているrubik-cubeプログラムを実行するとルービックキューブインターフェースが表示される。6つある面が横一列に表示されており、左から順にFore(正面)、Up(上面)、Right(右面)、Back(背面)、Down(底面)、Left(左面)となる。

ルービックキューブインターフェース

キー入力待ちになっているので、面の頭文字のどれか1文字を小文字(f,u,r,b,d,l)、または大文字(F,U,R,B,D,L)で入力する。小文字の場合は時計回り、大文字の場合は反時計回りで面が90度回転する。回転に合わせて隣接する4つの面の要素も適宜移動する。

完了するとこのようになる

終了する場合はqを入力。実行中のプロンプトに表示されているアルファベットの羅列は解答を表しており、1文字ずつその順番通りに入力すると誰でも簡単に完成させることができる。

プログラム

ダウンロード
RUBIK-CUBE.tar.bz2 [MD5: 91f6ef865349d02b690ee903593d564d]
#!/bin/sh
# 変数定義(一時ファイルパス/着色データ)
tmp=tmp
F=`printf '033[43mF033[00m'`
U=`printf '033[46mU033[00m'`
R=`printf '033[41mR033[00m'`
B=`printf '033[45mB033[00m'`
D=`printf '033[44mD033[00m'`
L=`printf '033[42mL033[00m'`

# 関数のインクルード
. rubik-cube.func

# 初期化/シャッフル/答え作り(シャッフルの逆操作を記録)
init
arg=$(echo "$1" | grep '^[0-9]*$' | grep -v '^0*$')
rand ${arg:-10} 2> $tmp-ans
ans=$(tail -r $tmp-ans | yarr -d | tr a-zA-Z A-Za-z)

# ゲームの開始
n=1
while true; do

echo " Fore    Up      Right   Back    Down    Left"
echo " ---------------------------------------------"
ycat -3 F U R B D L | ycat -1 /dev/null - |
sed -e "s/F/$F/g; s/U/$U/g; s/R/$R/g"           -e "s/B/$B/g; s/D/$D/g; s/L/$L/g"
echo "<-f F-> <-u U-> <-r R-> <-b B-> <-d D-> <-l L->"
echo -n "${ans}[$n]> "
if [ -n "$(printenv SHELL | grep "bash")"  ]; then
read -n 1 scr
echo
else
read scr
fi
scr=$(echo "_$scr" | tr -Cd fFbBuUdDrRlLq | awk '{print substr($0,1,1)}')
[ "$scr" = "q" ] && break
eval ${scr} 2> /dev/null
n=$((n+1))

done

rm F U R B D L $tmp-*
exit 0

rubik-cubeプログラム※3

# RUBIK-CUBE エンジン
init ()    # 色が揃ったキューブをセットする
{
for surf in F U B D R L; do
yes "$surf" |
head -9     |
yarr -3     > $surf
done
}

r ()  # R面(Right)の90度時計回り
{
self 3 F | cat           > $tmp-f
self 3 U | tail -r       > $tmp-u
self 1 B | cat           > $tmp-b
self 1 D | tail -r       > $tmp-d
delf 3 F | ycat - $tmp-d > $tmp-w; mv $tmp-w F
delf 3 U | ycat - $tmp-f > $tmp-w; mv $tmp-w U
delf 1 B | ycat $tmp-u - > $tmp-w; mv $tmp-w B
delf 1 D | ycat $tmp-b - > $tmp-w; mv $tmp-w D
tateyoko R | self 3 2 1  > $tmp-w; mv $tmp-w R
}

R ()  # R面(Right)の90度反時計回り
{
self 3 F | tail -r       > $tmp-f
self 3 U | cat           > $tmp-u
self 1 B | tail -r       > $tmp-b
self 1 D | cat           > $tmp-d
delf 3 F | ycat - $tmp-u > $tmp-w; mv $tmp-w F
delf 3 U | ycat - $tmp-b > $tmp-w; mv $tmp-w U
delf 1 B | ycat $tmp-d - > $tmp-w; mv $tmp-w B
delf 1 D | ycat $tmp-f - > $tmp-w; mv $tmp-w D
tateyoko R | tail -r     > $tmp-w; mv $tmp-w R
}

l ()  # L面(Left)の90度時計回り
{
self 1 F | tail -r       > $tmp-f
self 1 U | cat           > $tmp-u
self 3 B | tail -r       > $tmp-b
self 3 D | cat           > $tmp-d
delf 1 F | ycat $tmp-u - > $tmp-w; mv $tmp-w F
delf 1 U | ycat $tmp-b - > $tmp-w; mv $tmp-w U
delf 3 B | ycat - $tmp-d > $tmp-w; mv $tmp-w B
delf 3 D | ycat - $tmp-f > $tmp-w; mv $tmp-w D
tateyoko L | self 3 2 1  > $tmp-w; mv $tmp-w L
}

L ()  # L面(Left)の90度反時計回り
{
self 1 F | cat           > $tmp-f
self 1 U | tail -r       > $tmp-u
self 3 B | cat           > $tmp-b
self 3 D | tail -r       > $tmp-d
delf 1 F | ycat $tmp-d - > $tmp-w; mv $tmp-w F
delf 1 U | ycat $tmp-f - > $tmp-w; mv $tmp-w U
delf 3 B | ycat - $tmp-u > $tmp-w; mv $tmp-w B
delf 3 D | ycat - $tmp-b > $tmp-w; mv $tmp-w D
tateyoko L | tail -r     > $tmp-w; mv $tmp-w L
}

f ()  # F面(Front)の90度時計回り
{
tail -1 U | tarr              > $tmp-u
self  1 R | yarr              > $tmp-r
tail -1 D | tarr | tail -r    > $tmp-d
self  3 L | yarr | self 3 2 1 > $tmp-l
ctail -1 U | cat - $tmp-l     > $tmp-w; mv $tmp-w U
delf 1 R   | ycat $tmp-u -    > $tmp-w; mv $tmp-w R
ctail -1 D | cat - $tmp-r     > $tmp-w; mv $tmp-w D
delf 3 L   | ycat - $tmp-d    > $tmp-w; mv $tmp-w L
tateyoko F | self 3 2 1       > $tmp-w; mv $tmp-w F
}

F ()  # F面(Front)の90度反時計回り
{
tail -1 U | tarr | tail -r    > $tmp-u
self  1 R | yarr              > $tmp-r
tail -1 D | tarr              > $tmp-d
self  3 L | yarr | self 3 2 1 > $tmp-l
ctail -1 U | cat - $tmp-r     > $tmp-w; mv $tmp-w U
delf 1 R   | ycat $tmp-d -    > $tmp-w; mv $tmp-w R
ctail -1 D | cat - $tmp-l     > $tmp-w; mv $tmp-w D
delf 3 L   | ycat - $tmp-u    > $tmp-w; mv $tmp-w L
tateyoko F | tail -r          > $tmp-w; mv $tmp-w F
}

b ()  # B面(Back)の90度時計回り
{
head -1 U | tarr | tail -r    > $tmp-u
self 1 L  | yarr | self 3 2 1 > $tmp-l
head -1 D | tarr              > $tmp-d
self 3 R  | yarr              > $tmp-r
tail -n +2 U | cat $tmp-r -   > $tmp-w; mv $tmp-w U
delf 1 L     | ycat $tmp-u -  > $tmp-w; mv $tmp-w L
tail -n +2 D | cat $tmp-l -   > $tmp-w; mv $tmp-w D
delf 3 R     | ycat - $tmp-d  > $tmp-w; mv $tmp-w R
tateyoko B | self 3 2 1       > $tmp-w; mv $tmp-w B
}

B ()  # B面(Back)の90度反時計回り
{
head -1 U | tarr              > $tmp-u
self 1 L  | yarr | self 3 2 1 > $tmp-l
head -1 D | tarr | tail -r    > $tmp-d
self 3 R  | yarr              > $tmp-r
tail -n +2 U | cat $tmp-l -   > $tmp-w; mv $tmp-w U
delf 1 L     | ycat $tmp-d -  > $tmp-w; mv $tmp-w L
tail -n +2 D | cat $tmp-r -   > $tmp-w; mv $tmp-w D
delf 3 R     | ycat - $tmp-u  > $tmp-w; mv $tmp-w R
tateyoko B | tail -r          > $tmp-w; mv $tmp-w B
}

u ()  # U面(under)の90度時計回り
{
head -1 R                   > $tmp-r
head -1 F                   > $tmp-f
head -1 L                   > $tmp-l
head -1 B                   > $tmp-b
tail -n +2 R | cat $tmp-b - > $tmp-w; mv $tmp-w R
tail -n +2 F | cat $tmp-r - > $tmp-w; mv $tmp-w F
tail -n +2 L | cat $tmp-f - > $tmp-w; mv $tmp-w L
tail -n +2 B | cat $tmp-l - > $tmp-w; mv $tmp-w B
tateyoko U | self 3 2 1     > $tmp-w; mv $tmp-w U
}

U ()  # U面(under)の90度反時計回り
{
head -1 R                   > $tmp-r
head -1 F                   > $tmp-f
head -1 L                   > $tmp-l
head -1 B                   > $tmp-b
tail -n +2 R | cat $tmp-f - > $tmp-w; mv $tmp-w R
tail -n +2 F | cat $tmp-l - > $tmp-w; mv $tmp-w F
tail -n +2 L | cat $tmp-b - > $tmp-w; mv $tmp-w L
tail -n +2 B | cat $tmp-r - > $tmp-w; mv $tmp-w B
tateyoko U | tail -r        > $tmp-w; mv $tmp-w U
}

d ()  # D面(down)の90度時計回り
{
tail -1 R                 > $tmp-r
tail -1 F                 > $tmp-f
tail -1 L                 > $tmp-l
tail -1 B                 > $tmp-b
ctail -1 R | cat - $tmp-f > $tmp-w; mv $tmp-w R
ctail -1 F | cat - $tmp-l > $tmp-w; mv $tmp-w F
ctail -1 L | cat - $tmp-b > $tmp-w; mv $tmp-w L
ctail -1 B | cat - $tmp-r > $tmp-w; mv $tmp-w B
tateyoko D | self 3 2 1 > $tmp-w; mv $tmp-w D
}

D ()  # D面(down)の90度反時計回り
{
tail -1 R                 > $tmp-r
tail -1 F                 > $tmp-f
tail -1 L                 > $tmp-l
tail -1 B                 > $tmp-b
ctail -1 R | cat - $tmp-b > $tmp-w; mv $tmp-w R
ctail -1 F | cat - $tmp-r > $tmp-w; mv $tmp-w F
ctail -1 L | cat - $tmp-f > $tmp-w; mv $tmp-w L
ctail -1 B | cat - $tmp-l > $tmp-w; mv $tmp-w B
tateyoko D | tail -r      > $tmp-w; mv $tmp-w D
}

# ランダムに指定回数キューブを回転させる
rand ()
{
fnc="fFbBuUdDrRlL"
for i in $(yes | head -$1); do
rnd=$(cat /dev/urandom    |
cut -b 1-4                |
head -1                   |
od -D                     |
awk 'NR==1{print $2%12+1}')
proc=$(awk 'BEGIN{print substr("'$fnc'",'$rnd',1)}')
echo $proc 1>&2   # 手続きを保存する
eval $proc        # 関数の実行
done
}

rubik-cube.funcルービックキューブエンジン※3

ルービックキューブプログラムの見どころ

ルービックキューブを3x3の行列6つから構成されているものと捉え、行列操作によって回転をシミュレーションしている。

ここで掲載したプログラムが特徴的なのは、キューブの回転操作(行列操作)の実装に、SQLのSELECT句に相当するselfdelfといったTukubaiコマンドを使っている点にある。データベース操作も行列操作も数理的に見ると類似した概念であり、こういった操作が可能となる。

回転関数は12通り(6面x2つの回転方向)あるが、どれ一つとっても変数をまったく使っていない。他のプログラミング言語であれば二次元配列を使うところを、Tukubaiではテキストファイルを使って操作を実現している。

ユニケージユニバーサル・シェル・プログラミング研究所の登録商標。

usp Tukubaiユニバーサル・シェル・プログラミング研究所の登録商標。

※1 Open usp Tukubaiは最新バージョンを採用のこと。古いバージョンでは適切に動作しない可能性がある。

※2 USP MAGAZINE Vol.5より加筆修正後転載。

※3 本ページで公開されているプログラムとそれに付随するデータの著作権およびライセンスは、特に断りがない限りOpen usp Tukubai本体と同じMITライセンスに準拠するものとする。

Last modified: 2014-01-13 00:00:00