ルービックキューブっぽいものをシミュレートする 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";
    }
}