//************************************************ //  5パズル/ハッシュ法による幅優先探索 //************************************************ #include #define FALSE 0 #define TRUE 1 #define BOARD_WIDTH 3 // ボード横長さ #define BOARD_SIZE 6 // ボードサイズ //******************************************* // ボードの座標 // +--+--+--+  空きマスの位置へ移動出来る // |0|1|2| 駒の位置をテーブル化しておく // +--+--+--+ 例えば空きマスの位置が0なら // |3|4|5| 移動出来る駒の位置は1と3。 // +--+--+--+  -1はデータの終わり(番兵) //******************************************* int Moval[6][4] = { { 1, 3,-1, 0}, // <-空き位置が0なら移動可能位置は1,3 { 0, 2, 4,-1}, // <-空き位置が1なら移動可能位置は0,2,4 { 1, 5,-1, 0}, // <-空き位置が2なら移動可能位置は1,5 { 0, 4,-1, 0}, // 以下同様に { 1, 3, 5,-1}, { 2, 4,-1, 0}, }; char BOARD[BOARD_SIZE]; // ボード char Start[BOARD_SIZE] = { // 開始局面 4, 5, 3, 1, 2, 0 }; char Finish[BOARD_SIZE] = { // 終了局面 1, 2, 3, 4, 5, 0 }; //******************************************* // ハッシュ表とキューの定義 //******************************************* #define HASH_SIZE 877 // 必要量の2割増し程度(出来れば素数) #define QUEUE_SIZE 720 // 待ち行列キューサイズ(実際の必要量) #define EMPTY -1 // ハッシュ表の要素(バケット)が空の印 typedef struct bucket { char board[BOARD_SIZE]; // ゲームボード int space; // 空きマスの座標(未使用ならEMPTY) struct bucket *prev; // 自分の親局面を示すポインタ } bucket_t; bucket_t HASH[HASH_SIZE]; // ハッシュ表(格納庫) bucket_t *QUEUE[QUEUE_SIZE]; // 待ち行列(キュー) bucket_t *FINISH; // 終了局面の保存番地 int Qhead; // キューの先頭位置 int Qtail; // キューの後ろ位置 //******************************************* // 再帰呼び出しを使って解答手順を表示する //******************************************* int Display(bucket_t *ptr) { int i, count=0; // 親局面があれば親局面を先に表示する(再帰呼び出し) if (ptr->prev) count = Display(ptr->prev); // この局面を表示する for (i=0; iboard[i]) printf(" %d",ptr->board[i]); else printf(" "); } printf(" %d手目\n", count); return count + 1; // 呼び出元の手数を教える } //******************************************* // 現在の局面をハッシュ表に登録する //******************************************* void BucketIn(bucket_t *prev, bucket_t *address, int space) { int i; for (i=0; iboard[i] = BOARD[i]; address->space = space; // 空きマスの座標 address->prev = prev; // 親局面のポインタ } //******************************************* // ハッシュ関数(局面の格納番地を求める) //******************************************* bucket_t *HashFunc(void) { bucket_t *ptr; unsigned long hash; int i; // 局面を数値化しハッシュ値を求める hash = 0; for (i=0; ispace == EMPTY) break; // この場所は未使用 // 同じ局面かをチェック for (i=0; iboard[i] != BOARD[i]) break; if (i == BOARD_SIZE) break; // 同じ局面である hash = (hash + 1) % HASH_SIZE; // 再ハッシュ } return ptr; // 格納場所のポインタを返す } //******************************************* // 待ち行列(キュー)を使った幅優先探索 //******************************************* int BreadthFirst(void) { bucket_t *address1, *address2; int i, space, piece; char n; // ハッシュ表とキューの初期化 for (i=0; iboard[i]; space = address1->space; // 空きマスの座標 // 可能な次の手を総て実行する for (i=0; Moval[space][i]!=-1; i++) { // 番兵まで piece = Moval[space][i]; // 移動させる駒の座標 n = BOARD[piece]; // 移動させる駒の数字 // 局面を生成する BOARD[piece] = 0; BOARD[space] = n; // 始めての局面ならハッシュ表とキューに追加する address2 = HashFunc(); if (address2->space == EMPTY) { BucketIn(address1, address2, piece); // ハッシュ表へ QUEUE[Qtail++] = address2; // キューへ } // 解を発見したか確認(FINISHは終了局面の番地) if (address2 == FINISH) { FINISH->prev = address1; // 親局面ポインタを記録 return TRUE; // 解を発見しました } // 局面を元に戻す BOARD[space] = 0; BOARD[piece] = n; } } return FALSE; // 解がありません } int main(void) { // 幅優先探索を行い解があれば手順を表示 if (BreadthFirst()) Display(FINISH); // 解答手順を表示 else printf("解がありません\n"); return 0; }