このページは,英語で書いた a C program solving the 15 puzzle の日本語訳として置いたもので, 15 パズルを解く C プログラムを提供するものだったが, 検索で上のほうにくるようになったので, 実用的な解き方を求めてきた人をがっかりさせないよう, それをここに記しておくことにした。
15 個のコマを下のループに沿って動かす。
↓ | ← | ↓ | ← |
↓ | ↑ | ← | ↑ |
↓ | → | ↓ | ↑ |
→ | ↑ | → | ↑ |
ゲームの目的を,このループに沿って 1, 2, 3, ..., 15 と並べること,に取り換えてもよい。 (仮の番号をコマの上にはり,目的を達成してからそれをはがせばよい。)
このループに沿って 15 個のコマを動かしている限り,何も変わらない。
11 | 2 | ↓ | ← |
9 | ← | ↑ | |
↓ | → | ↓ | ↑ |
→ | ↑ | → | ↑ |
上のようになったとき,「9 を左に動かす」という, 例外的な動きが考えられる。 もしもこれを実行すると,9 は 11 と 2 とを飛び越えることになる。
(これが可能なところは,ほかに 5 か所がある。どこで考えても,同じである。)
これを,「飛び越えられる二つのコマのうち少なくとも片方が, 飛び越えるコマよりも大きければ,飛び越す」というルールの元で,実行する。 ただし,「1 は飛び越えてはいけない」「1 を飛び越えてはいけない」 というルールも追加する。
すると,何も考えずにこれをくり返してゆくうち, コマは 1, 2, 3, ..., 15, または 1, 3, 2, ..., 15 の順に並ぶ。
(次の行から, a C program solving the 15 puzzle の日本語版が始まる。)
このページを読む前に, 《すのものの「15 パズル(およびその拡張)を解く」》 と 《すのものの「風変わりなソートアルゴリズム(15 パズルに関連)」》 をお読みいただきたい。
15 個のコマを下のループに沿って動かす。
↓ | ← | ↓ | ← |
↓ | ↑ | ← | ↑ |
↓ | → | ↓ | ↑ |
→ | ↑ | → | ↑ |
ゲームの目的を,このループに沿って 1, 2, 3, ..., 15 と並べること,に取り換えてもよい。
マスと配列 a[ ] の添え字との対応は以下の通りとする。
a[1] a[2] a[5] a[6] a[0] a[3] a[4] a[7] a[15] a[12] a[11] a[8] a[14] a[13] a[10] a[9]
i を 0, 2, 4, 8, 10, 12 のどれかとする。 もしも a[i] が空いている場合, 次の例外的な手を指すことができる。 すなわち,a[i+3] を水平に動かし, a[i+1] と a[i+2] を飛び越させる。 この手は,a[i+1] と a[i+2] の少なくとも片方が a[i+3] より大きく, コマ 1 がこれら三つのコマに現れていないときにおこなう。
#include <stdio.h> #include <stdlib.h> #include <time.h> #define N 16 void initialize(void); void shuffle(void); void show(void); void loop(void); int a[N]; int z; /* コマ 0(=空白)の位置 */ int main() { initialize(); shuffle(); loop(); } void initialize(void) { int i; for (i = 0; i < N; i++) { a[i] = i; } } void shuffle(void) { /* コマをシャッフル */ int i; srand((unsigned)time(NULL)); for (i = N; i > 0; i--) { int tmp; int r; r = rand() % i; tmp = a[i-1]; a[i-1] = a[r]; a[r] = tmp; } for (i = 0; i < N; i++) { if (a[i] == 0) { z = i; } } } void show(void) { /* 配列を印字 */ int i; for (i = 0; i < N; i++) { if (a[i] == 0) { printf(" "); /* 0 means the empty square */ } else { printf(" %2d", a[i]); } } putchar('\n'); } void loop(void) { int i; for ( ; ; ) { show(); switch (z) { case 0: case 2: case 4: case 8: case 10: case 12: if (a[z+1] != 1 && a[z+2] != 1 && a[z+3] != 1 && (a[z+1] > a[z+3] || a[z+2] > a[z+3])) { a[z] = a[z+3], a[z+3] = 0; z = z+3; } else { a[z] = a[z+1], a[z+1] = 0; z = z+1; } break; case 15: a[15] = a[0], a[0] = 0; z = 0; break; default: a[z] = a[z+1], a[z+1] = 0; z++; } for (i = 4; i < N && a[i] == i; i++) { ; } if (i == N && a[0] == 0 && a[1] == 1) { show(); break; } } }
このプログラムは,次のどちらかを表示して止まる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
または
1 3 2 4 5 6 7 8 9 10 11 12 13 14 15