ルービックキューブっぽいものをシミュレートする Perl スクリプト
某問題のために作ったものです。シミュレートするだけで、解いてくれたりはしません。
かなりのやっつけ仕事でしたので、対称性とか全然考えていない汚いコードです。
R での回転操作と、Y 軸周りと Z 軸周りの持ち替え操作だけを定義して、他の操作はその組み合わせで実現しています。一部の記号に対応する操作が分からなかったため、試行錯誤できるような作りになっています。
#!/usr/bin/perl use strict; # キューブの初期配置を、前、上、右、後、下、左の順で # dump_cube の表示順とは異なりますので要注意 my @cube_str = ( "YSHYCKZRL", "HNDKNSMWB", "GMTRBWTKT", "iiiiiiiii", "aaaaaaaaa", "_________" ); #my @cube_str = ( "123456789", "abcdefghi", "ABCDEFGHI", "#########", "XXXXXXXXX", "*********" ); #my @cube_str = ( "#########", "XXXXXXXXX", "*********", "123456789", "abcdefghi", "ABCDEFGHI" ); # キューブを動かす指令 my $command = "R<b>T>L>F>L<Z<Y>H<F<H>f>R<F<R>Y<H<F<h>F>H<F<H>F>Y<Y<F<H<F>H<F<H>F>Y>H>F<H<F>Y>Y>L>F>H>F<H<F>H>F<H<R>h>L<R<B<F<h>B>F>h>"; #my $command = "X>X<X<X>Y>Y<Y<Y>Z>Z<Z<Z>R>R<R<R>r>r<r<r>L>L<L<L>l>l<l<l>F>F<F<F>f>f<f<f>B>B<B<B>b>b<b<b>T>T<T<T>t>t<t<t>H>H<H<H>h>h<h<h>"; # 詳しくは、ルービックキューブの回転記号についてググってください。 # 持ち替えは Y>:Y軸に90度回転, Z<:Z軸に270度回転 としています。 # H と T は、何に割当たるか不明なオペレーションなので、 # 現在は、とりあえず H を U に、T を D に割り当てています。 # 小文字は2回回す or 2列回転させる。現在は、2回回すに割り当てています。 # 記号の操作への割り当て # functor_2 をかぶせると、その操作を2回行うようになります。 # functor_w をかぶせると、いわゆる Rw や Bw のような、2列回す操作となります。 my %functions = ( X => \&func_X, Y => \&func_Y, Z => \&func_Z, R => \&func_R, r => functor_2(\&func_R), # 小文字は2回繰り返す L => \&func_L, l => functor_2(\&func_L), # 小文字は2回繰り返す F => \&func_F, f => functor_2(\&func_F), # 小文字は2回繰り返す B => \&func_B, b => functor_2(\&func_B), # 小文字は2回繰り返す # 割り当てが不明な記号 H => functor_1(\&func_U), h => functor_2(\&func_U), # 小文字は2回繰り返す T => functor_1(\&func_D), t => functor_2(\&func_D), # 小文字は2回繰り返す ); ############ 以下、書き換える必要はありません ############ my @cube; my @cube2; foreach my $str ( @cube_str ) { my @chars = split "", $str; my @plane = (); for ( my $i = 0; $i < 3; $i++ ) { my @line = (); for ( my $j = 0; $j < 3; $j++ ) { push @line, shift @chars; } push @plane, \@line; } push @cube, \@plane; } dump_cube(); while ( $command ne "" ) { my $c = substr $command, 0, 1; my $dir = substr $command, 1, 1; $command = substr $command, 2; print "==============\n"; print "Command: $c($dir)\n"; $functions{$c}->(($dir eq "<") ? "\'" : ""); dump_cube(); } exit(); ####### sub get_rep { my $option = shift; my $repeat = 1; # 逆回しの場合 if ($option =~ /\'/) { $repeat = 3; } # 複数回回す場合 if ($option =~ /(\d+)/) { $repeat = $repeat*$1; } return $repeat; } # 表記を揃えたいときに使う何もしない functor sub functor_1 { my $func = shift; return $func; } # 2回回す際にかぶせる functor sub functor_2 { my $func = shift; return sub { my $option = shift; $func->($option . "2"); } } # 2列回す際にかぶせる functor sub functor_w { my $func = shift; return sub { my $option = shift; $func->($option . "w"); } } sub func_X { my $option = shift; my $repeat = get_rep($option); for (my $i = 0; $i < $repeat; $i++) { func_Y(); func_Z(); func_Y(); func_Y(); func_Y(); } } sub func_Y { my $option = shift; my $repeat = get_rep($option); for (my $i = 0; $i < $repeat; $i++) { clone_cube(); # 全ての面を書き換えるので本当は不要 $cube2[0] = $cube[2]; $cube2[1] = rot_R($cube[1]); $cube2[2] = $cube[3]; $cube2[3] = $cube[5]; $cube2[4] = rot_L($cube[4]); $cube2[5] = $cube[0]; commit_cube(); } } sub func_Z { my $option = shift; my $repeat = get_rep($option); for (my $i = 0; $i < $repeat; $i++) { clone_cube(); # 全ての面を書き換えるので本当は不要 $cube2[0] = rot_R($cube[0]); $cube2[1] = rot_R($cube[5]); $cube2[2] = rot_R($cube[1]); $cube2[3] = rot_L($cube[3]); $cube2[4] = rot_R($cube[2]); $cube2[5] = rot_R($cube[4]); commit_cube(); } } sub func_R { my $option = shift; my $repeat = get_rep($option); for (my $i = 0; $i < $repeat; $i++) { clone_cube(); for ( my $j = 0; $j < 3; $j++ ) { $cube2[0][$j][2] = $cube[4][$j][2]; # 2列回す場合 $cube2[0][$j][1] = $cube[4][$j][1] if ($option =~ /w/); } for ( my $j = 0; $j < 3; $j++ ) { $cube2[1][$j][2] = $cube[0][$j][2]; # 2列回す場合 $cube2[1][$j][1] = $cube[0][$j][1] if ($option =~ /w/); } for ( my $j = 0; $j < 3; $j++ ) { $cube2[3][2-$j][0] = $cube[1][$j][2]; # 2列回す場合 $cube2[3][2-$j][1] = $cube[1][$j][1] if ($option =~ /w/); } for ( my $j = 0; $j < 3; $j++ ) { $cube2[4][$j][2] = $cube[3][2-$j][0]; # 2列回す場合 $cube2[4][$j][1] = $cube[3][2-$j][1] if ($option =~ /w/); } $cube2[2] = rot_R($cube[2]); commit_cube(); } } sub func_B { my $option = shift; func_Y(1); func_R($option); func_Y(3); } sub func_U { my $option = shift; func_Z(1); func_R($option); func_Z(3); } sub func_D { my $option = shift; func_Z(3); func_R($option); func_Z(1); } sub func_L { my $option = shift; func_Y(2); func_R($option); func_Y(2); } sub func_F { my $option = shift; func_Y(3); func_R($option); func_Y(1); } sub func_Omega { # 真ん中だけを回転させるω操作 my $option = shift; my $repeat = get_rep($option); for (my $i = 0; $i < $repeat; $i++) { func_U("w"); func_U(3); } } sub clone_cube { for ( my $p = 0; $p < 6; $p++ ) { $cube2[$p] = []; for ( my $i = 0; $i < 3; $i++ ) { $cube2[$p][$i] = [@{$cube[$p][$i]}]; } } } sub commit_cube { for ( my $p = 0; $p < 6; $p++ ) { $cube[$p] = []; for ( my $i = 0; $i < 3; $i++ ) { $cube[$p][$i] = [@{$cube2[$p][$i]}]; } } } sub rot_R { my $plane = shift; my @result; for ( my $i = 0; $i < 3; $i++ ) { $result[$i] = []; for ( my $j = 0; $j < 3; $j++ ) { $result[$i][$j] = $plane->[2-$j][$i]; } } return \@result; } sub rot_L { my $plane = shift; return rot_R(rot_R(rot_R($plane))); } sub dump_cube { # キューブを出力します。 # 正面、右、後、左、上、下の順。 # 上下は、正面の状態からX軸で±90度回転させたときに見える向き # 左右後は、Y軸でぐるぐる回したときに見える向き my @dump_order = (0, 2, 3, 5, 1, 4); for ( my $i = 0; $i < 3; $i++ ) { print " | "; for ( my $p = 0; $p < 6; $p++ ) { for ( my $j = 0; $j < 3; $j++ ) { print $cube[$dump_order[$p]][$i][$j]; } print " | "; } print "\n"; } }