横書:左→右(上→下)
- 引数x、yは、配列サイズです。 xが列数(横方向)、yが行数(縦方向)、となります。以下の全ての関数で同じです。
- 外側のforループiは、横方向ループです。以下の全ての関数で同じです。
- 内側のforループjは、縦方向ループです。以下の全ての関数で同じです。
i + j*x
で、横方向に1ずつ増加、縦方向にxずつ増加となり、通常の横書き様に並びます。
この式だと、iとjのループの内と外の順番はどちらが先でも問題ありません。
public static int[][] set_left_r(int x, int y){ int[][] temp = new int[x][y]; for(int i=0; i<x; i++){ for(int j=0; j<y; j++){ temp[i][j] = i + j*x; } } return temp; }
例 4×3 (0,0)=0; (1,0)=1; (2,0)=2; (3,0)=3; (0,1)=4; (1,1)=5; (2,1)=6; (3,1)=7; (0,2)=8; (1,2)=9; (2,2)=10; (3,2)=11;
ちょっと脱線
※ この関数だけpublic なのは、初回表示時に、メインとなる別のクラスから呼び出してるため。
見直して今考えると、package private でも十分ではあるか。
横書:右→左(上→下)
(x-i-1) + j*x
で、横方向に(x-1)から1ずつ減少、縦方向にxずつ増加となり、右から左への横書き様に並びます。
private static int[][] set_right_l(int x, int y){ int[][] temp = new int[x][y]; for(int i=0; i<x; i++){ for(int j=0; j<y; j++){ temp[i][j] = (x-i-1) + j*x; } } return temp; }
例 4×3 (0,0)=3; (1,0)=2; (2,0)=1; (3,0)=0; (0,1)=7; (1,1)=6; (2,1)=5; (3,1)=4; (0,2)=11; (1,2)=10; (2,2)=9; (3,2)=8;
横書:左→右→左(上→下)
- 縦のjが奇数か偶数かで、左→右と、右→左を切り換えています。
private static int[][] set_left_return(int x, int y){ int[][] temp = new int[x][y]; for(int i=0; i<x; i++){ for(int j=0; j<y; j++){ if( (j%2) == 0 ){ temp[i][j] = i + j*x; }else{ temp[i][j] = (x-i-1) + j*x; } } } return temp; }
例 4×3 (0,0)=0; (1,0)=1; (2,0)=2; (3,0)=3; (0,1)=7; (1,1)=6; (2,1)=5; (3,1)=4; (0,2)=8; (1,2)=9; (2,2)=10; (3,2)=11;
縦書:上→下(左→右)
i*y + j
で、横方向にyずつ増加、縦方向に1ずつ増加となり、左→右への縦書き様に並びます。
private static int[][] set_down_left(int x, int y){ int[][] temp = new int[x][y]; for(int i=0; i<x; i++){ for(int j=0; j<y; j++){ temp[i][j] = i*y + j; } } return temp; }
例 4×3 (0,0)=0; (1,0)=3; (2,0)=6; (3,0)=9; (0,1)=1; (1,1)=4; (2,1)=7; (3,1)=10; (0,2)=2; (1,2)=5; (2,2)=8; (3,2)=11;
縦書:上→下(右→左)
(x-i-1)*y + j
で、横方向に(x-1)*yからyずつ減少、縦方向に1ずつ増加となり、通常の右からの縦書き様に並びます。
private static int[][] set_down_right(int x, int y){ int[][] temp = new int[x][y]; for(int i=0; i<x; i++){ for(int j=0; j<y; j++){ temp[i][j] = (x-i-1)*y + j; } } return temp; }
例 4×3 (0,0)=9; (1,0)=6; (2,0)=3; (3,0)=0; (0,1)=10; (1,1)=7; (2,1)=4; (3,1)=1; (0,2)=11; (1,2)=8; (2,2)=5; (3,2)=2;
縦書:上→下→上(左→右)
- 横のiが奇数か偶数かで、上→下と、下→上を切り換えています。
private static int[][] set_down_return(int x, int y){ int[][] temp = new int[x][y]; for(int i=0; i<x; i++){ for(int j=0; j<y; j++){ if( (i%2) == 0 ){ temp[i][j] = i*y + j; }else{ temp[i][j] = i*y + (y-j-1); } } } return temp; }
例 4×3 (0,0)=0; (1,0)=5; (2,0)=6; (3,0)=11; (0,1)=1; (1,1)=4; (2,1)=7; (3,1)=10; (0,2)=2; (1,2)=3; (2,2)=8; (3,2)=9;
渦巻き:右回転
- 開始位置は、左上(0,0)から、右回転します(0行目を右へ並べてから、右端を下にならべていく)。外から内部へ向かっていく渦巻きです。
- ループは、並べる数値順として、数値を置く位置(i,j)を求めていく方法を取っています。
- kは、方向転換の回数をカウントします。switch 文でkのカウントにより、位置をずらす方向を切り換えています。 kの始まりを1とすることで、開始時に右方向へ位置がずれていくようになります。
- line_minとline_lenとで、何時方向転換するか(kを増やすか)を決めます。 一周したら、内側へ向かうように工夫しています。
private static int[][] set_spiral_right(int x, int y){ int[][] temp = new int[x][y]; int n = 0; // number counter int i=0, j=0; // dimention position temp[i][j] = n; int k = 1; // rotate line counter int line_min = n; // line start number int line_len = x - k/2; // line size while( n < (x*y-1) ){ n ++; if( (n - line_min) >= line_len){ k++; line_min = n; line_len = (k%2 == 1)? (x-k/2) : (y-k/2); if( line_len<=0 )break; } switch ( k%4 ){ case 0: // 余り0: y方向up j -- ; break; case 1: // 余り1: x 方向 → i ++; break; case 2: // 余り2: y 方向 down j ++; break; case 3: // 余り3: x 方向 ← i --; } temp[i][j] = n; } return temp; }
例 4×3 (0,0)=0; (1,0)=1; (2,0)=2; (3,0)=3; (0,1)=9; (1,1)=10; (2,1)=11; (3,1)=4; (0,2)=8; (1,2)=7; (2,2)=6; (3,2)=5;
渦巻き:左回転
- 開始位置は、左上(0,0)から、左回転します(0列目を下へ並べてから、下端を左→右にならべていく)。外から内部へ向かっていく渦巻きです。
- ループは、並べる数値順として、数値を置く位置(i,j)を求めていく方法を取っています。
- 右回転の関数と比べて、逆回転になるので、switch 文内や、line_len を決める式が異なってきます。
private static int[][] set_spiral_left(int x, int y){ int[][] temp = new int[x][y]; int n = 0; // number counter int i=0, j=0; // dimention position temp[i][j] = n; int k = 1; // rotate line counter int line_min = n; // line start number int line_len = y - k/2; // line size while( n < (x*y-1) ){ n ++; if( (n - line_min) >= line_len){ k++; line_min = n; line_len = (k%2 == 0)? (x-k/2) : (y-k/2); if( line_len<=0 )break; } switch ( k%4 ){ case 0: // 余り0: x方向 ← -- i -- ; break; case 1: // 余り1: y 方向 down ++ j ++; break; case 2: // 余り2: x 方向 → ++ i ++; break; case 3: // 余り3: y 方向 up -- j --; } temp[i][j] = n; } return temp; }
例 4×3 (0,0)=0; (1,0)=9; (2,0)=8; (3,0)=7; (0,1)=1; (1,1)=10; (2,1)=11; (3,1)=6; (0,2)=2; (1,2)=3; (2,2)=4; (3,2)=5;
おまけ:内側から外へ渦巻きを作るには?
内側の開始点を求めるのは、ちょっと面倒ですね。でも、数値順に位置を計算しているのだから、大きい方から逆順に数値を入れていけば、最後に入れたところが0の入る位置。
ということは、入れる数値nを最大値(x*y-1)から初めて、while条件式は(n>0)とし、 ループ内では、nをデクリメントすれば、内→外渦巻きのできあがり。
おまけ2:完成パターンどうしは互いに並べ換え可能なのか
15ゲームの不可能性の評価というページを見つけたので、完成パターンどうしが互いに並べ換え可能なのかを検証してみました。
理論の詳細は、上記ページをじっくり読んで頂くとして、簡単には、
「標準状態(横書:左→右(上→下)パターン)と比べて、 各ピースの位置が互いに入れ換え状態(1→2、2→1、互換という)のものがいくつあるか、 偶数組(偶置換)であれば標準状態に戻すことが出来、 奇数組(奇置換)であれば不可能である」というものです。
つまり、今回作成の完成パターン同士では、並べ換え可能なパターンは2グループに分かれるということになります。 また、ピース数によっても、互換となる組合わせが変わります。