import java.applet.*;
import java.awt.*;
//---------------------------------------------------------
//  倉庫番を解く(Ver2.7)     written by Kenichiro Takahashi
//                     http://www.ic-net.or.jp/home/takaken
//                        (AppletSize width=400 height=364)
//---------------------------------------------------------
public class soko207 extends Applet implements Runnable {

final int X_MAX = 12;              // 横のサイズ
final int Y_MAX = 10;              // 縦のサイズ
final int K_MAX = 9;               // 荷物の最大個数
final int B_SIZE = 109987;         // バケットサイズ
final int Q_SIZE = 100000;         // キューサイズ
final int R_HASH = 491;            // 再ハッシュ増分
final int AREA = X_MAX*Y_MAX;      // マップの面積
final int DD = 30;                 // 単位長さ
final int diff[] = {-1,X_MAX,1,-X_MAX}; // 方向ベクトル

boolean  imgloaded = false;        // 画像読込完了
Graphics grf;                      // 画面定義
Image item[] = new Image[9];       // gif画像

Thread animation = null;           // アニメスレッド
int  IDX;                          // アニメポインタ
boolean animFlag = false;          // アニメモード
boolean editFlag = true;           // 編集モード
int  parts = 4;                    // 選択部品番号
int  ansMsg = 0;                   // 探索結果コード
int  thinkBar = 0;                 // 進行状況表示

int  disp_map[] = new int[AREA];   // 表示マップ
int  edit_map[] = new int[AREA];   // 編集マップ

//------ 以下、電子頭脳の作業用 ------------------

byte BACKET[][] = new byte[B_SIZE][K_MAX+1]; // 1,099,870 byte
int  LINK[]  = new int[B_SIZE];              //   439,948 byte
byte OPTN[]  = new byte[B_SIZE];             //   109,987 byte
int  QUEUE[] = new int[Q_SIZE];              //   400,000 byte

    // BACKET:局面の保存(荷物の位置と人の自由行動域先頭)
    // LINK  :どのパターンから派生したかリンク元アドレス
    // OPTN  :0:未定義  1:通常探索  2:逆解き探索  3,5:凍結しない局所パターン  4,6:凍結する局所パターン
    // QUEUE :幅優先探索用キュー(通常探索は0から,逆解き探索はQ_SIZE-1から逆へ)

int  Qtop1, Qend1, Qadd1, Qlen1;   // 通常探索用キューの各ポインタ
int  Qtop2, Qend2, Qadd2, Qlen2;   // 逆解き探索用キューの各ポインタ
int  chan, kara;                   // 通常探索と逆解き探索の衝突部連結
int  backetCnt, kosu, sift, bbun;
int  push, tesu, posS;

byte  main_map[] = new byte[AREA]; // 探索メインマップ 1:壁 2:荷物
byte  wall_map[] = new byte[AREA]; // 壁マップ         1:壁
byte  dead_map[] = new byte[AREA]; // デッドゾーン     1:デッド
byte  goal_map[] = new byte[AREA]; // ゴールマップ     1:格納地点
byte  pack_map[] = new byte[AREA]; // 開始荷マップ     1:開始時荷物
byte  work_map[] = new byte[AREA]; // 各種作業マップ
byte  hstb[] = new byte[K_MAX+1];  // 局面の圧縮データ(BACKET登録用)

//---------------------------------------------------------
// 初期処理
//---------------------------------------------------------
public void init() {
    setBackground(Color.gray);

    /* ボタン配置 */
    setLayout(new BorderLayout());
    Panel psouth = new Panel();
    psouth.add(new Button("CLEAR"));
    psouth.add(new Button("RESET"));
    psouth.add(new Button("START"));
    add("South", psouth);

    /* エディットマップ初期化 */
    initMap();
}
public void initMap() {             // 周辺を壁にする
    for (int i=0; i<AREA; i++)
        disp_map[i]=edit_map[i]=4;
    for (int y=1; y<Y_MAX-1; y++)
    for (int x=1; x<X_MAX-1; x++)
        disp_map[y*X_MAX+x] = edit_map[y*X_MAX+x] = 0;
}
public void resetMap() {           // 編集マップを表示マップへ
    for (int i=0; i<AREA; i++)
        disp_map[i] = edit_map[i];
}

//---------------------------------------------------------
// アニメーション処理
//---------------------------------------------------------
public void start() {
    if (animation == null) {
        animation = new Thread(this);
        animation.start();
    }
}
public void stop() {
    if (animation != null) {
        animation.stop();
        animation = null;
    }
}
public void run() {
    if (!imgloaded) {
        grf = getGraphics();
        repaint();

        /* gif画像ロード */
        MediaTracker tracker = new MediaTracker(this);
        for (int i=0; i<9; i++) {
            item[i] = getImage(getCodeBase(),"item"+i+".gif");
            tracker.addImage(item[i],0);
        }
        try {
            tracker.waitForAll(0);
            imgloaded = !tracker.isErrorAny();
        } catch (InterruptedException e) {}
        if (!imgloaded) {
            stop();
            grf.drawString("Image Load Error!", 10, 40);
            return;
        }
    }
    repaint();
    while (true) {
        try {
            if (animFlag) {
                int pAv = QUEUE[IDX++];   // キューをアニメ進行に併用
                if (pAv < 0) animFlag=false;
                else Moving(pAv,grf);     // 次のシーンを表示
            }
            animation.sleep(300);
        } catch (InterruptedException e) { stop(); }
    }
}
public void Moving(int pAv, Graphics g) {
    int posa, posb, posc, vect;

    vect = pAv % 10; pAv /= 10;        // 人の向き
    posb = pAv % 1000; pAv /= 1000;    // 移動先
    posa = pAv;                        // 移動元
    posc = posb + (posb - posa);       // 移動先の先

    disp_map[posa] = goal_map[posa];   // 移動元の人を消す
    drawParts(posa,g);
    if (disp_map[posb] > 1) {          // 移動先に荷物があれば
        disp_map[posc] += 2;           // その先に荷物を表示
        drawParts(posc,g);
        push++;                        // 荷押回数カウントアップ
    }
    disp_map[posb] = vect+5;           // 移動先に人を表示
    drawParts(posb,g);

    drawMsg(g);                        // 荷押回数表示
}

//---------------------------------------------------------
// 画面の表示
//---------------------------------------------------------
public void update(Graphics g) {       // ちらつき防止処理
    paint(g);
}
public void paint(Graphics g) {        // アプレット全体表示
    if (!imgloaded) {
        g.drawString("Now Image Loading...", 10, 20);
    } else {
        for (int posi=0; posi<AREA; posi++)          // マップ全体
            drawParts(posi, g);
        for (int i=0; i<6; i++)                      // パーツリスト
            g.drawImage(item[i],X_MAX*DD+6,i*(DD+6)+15,this);
        drawSelect(parts,Color.red,g);               // 選択パーツ□印
        drawBar(g);                                  // 探索進行バー
        drawMsg(g);                                  // メッセージ
    }
}
public void drawParts(int posi, Graphics g) {        // マップ内posi地点の表示
    int x, y, d;

    x = posi % X_MAX;
    y = posi / X_MAX;
    d = disp_map[posi];
    g.drawImage(item[d],x*DD,y*DD,this);
}
public void drawSelect(int p, Color c, Graphics g) { // 選択パーツ□印
    g.setColor(c);
    g.drawRect(X_MAX*DD+3,p*(DD+6)+12,DD+5,DD+5);
    g.drawRect(X_MAX*DD+4,p*(DD+6)+13,DD+3,DD+3);
}
public void drawBar(Graphics g) {                    // 探索進行バー
    g.setColor(Color.white);
    g.fillRect(0,Y_MAX*DD+1,(X_MAX+1)*DD+10,10);
    g.setColor(Color.red);
    for (int i=1; i<=thinkBar; i++)
        g.fillRect((i-1)*20+1,Y_MAX*DD+1,18,10);
}
public void drawMsg(Graphics g) {                    // メッセージ
    g.setColor(Color.white);
    g.fillRect(0,Y_MAX*DD+11,(X_MAX+1)*DD+10,23);
    g.setFont(new Font("Helvetica",Font.PLAIN,14));
    g.setColor(Color.black);
    switch (ansMsg) {
        case 1: g.drawString("Too many packages",DD,Y_MAX*DD+26); break;
        case 2: g.drawString("Strange mapping",  DD,Y_MAX*DD+26); break;
        case 3: g.drawString("Impossible !",     DD,Y_MAX*DD+26); break;
        case 4: g.drawString("Give up !",        DD,Y_MAX*DD+26); break;
        case 5: g.drawString("Now thinking",     DD,Y_MAX*DD+26); break;
        case 6: g.drawString("Push Count "+Integer.toString(push)+" / "+
                    Integer.toString(tesu),DD,Y_MAX*DD+26);
    }
}

//---------------------------------------------------------
// マウスイベント
//---------------------------------------------------------
public boolean mouseDown(Event e, int x, int y) {
    if (animFlag == false) {
        if (editFlag == false) {   // 解答アニメ終了直後ならRESET
            resetMap();
            ansMsg = thinkBar = 0;
            repaint();
            editFlag = true;
        }
        int ex = x / DD;
        int ey = y / DD;
        if (ex>0 && ex<(X_MAX-1) && ey>0 && ey<(Y_MAX-1)) { // 編集マップ部
            disp_map[ey*X_MAX+ex] = edit_map[ey*X_MAX+ex] = parts;
            drawParts(ey*X_MAX+ex,grf);
        } else if (x > X_MAX*DD) {
            int d = (y - 15) / (DD+6);
            if (d>=0 && d<=5 && d!=parts) {                 // 選択パーツ部
                drawSelect(parts,Color.gray,grf);
                parts = d;
                drawSelect(parts,Color.red ,grf);
            }
        }
    }
    return true;
}
public boolean action(Event e, Object arg) {
    if (e.target instanceof Button) {
        animFlag =false;
        if ("CLEAR".equals(arg)) {         // CLEARボタン
            initMap();
            ansMsg = thinkBar = 0;
            repaint();
            editFlag = true;
        } else
        if ("RESET".equals(arg)) {         // RESETボタン
            resetMap();
            ansMsg = thinkBar = 0;
            repaint();
            editFlag = true;
        } else
        if ("START".equals(arg)) {         // STARTボタン
            if (editFlag == false) return false;  // 2度押し防止
            ansMsg = 5;                           // Now thinking 表示
            drawMsg(grf);
            editFlag = false;
            computer();                           // 電子頭脳の起動
            drawMsg(grf);
            if (ansMsg == 6) {                    // 解を発見した
                IDX=0;
                animFlag=true;                    // 解答アニメ開始
            }
        }
    }
    return true;
}

//---------------------------------------------------------
// 以下、電子頭脳
//---------------------------------------------------------
//*********************************************************
//* アクティブゾーン(荷を押さずに自由行動可能域)調査      *
//*********************************************************
byte  h1map[] = new byte[AREA];   // 本探索  /荷押前
byte  h2map[] = new byte[AREA];   // 本探索  /荷押後
byte  b1map[] = new byte[AREA];   // 局所探索/荷押前
byte  b2map[] = new byte[AREA];   // 局所探索/荷押後
int   ac_top;                     // アクティブゾーンの先頭
int   ac_cnt;                     // アクティブゾーンの面積

/* (副関数)アクティブゾーンを再帰でposhから波状に調べる */
void active_zone_sub(byte actv_map[], int posh) {
    int posi, vect;

    actv_map[posh]=1;
    ac_cnt++;                           // 面積更新
    if (posh < ac_top) ac_top=posh;     // 先頭位置の更新
    for (vect=0; vect<4; vect++) {
        posi = posh + diff[vect];
        if (main_map[posi]==0 && actv_map[posi]==0)
            active_zone_sub(actv_map,posi);
    }
}

/* (主関数)アクティブゾーンをposhから調べactv_map[]に記録 */
int active_zone(byte actv_map[], int posh) {
    int i;

    ac_cnt=0;
    ac_top=AREA;
    for (i=X_MAX+1; i<AREA-X_MAX-1; i++) actv_map[i]=0;
    active_zone_sub(actv_map,posh);

    return ac_top;                      // 先頭位置を返す
}

//*********************************************************
//* デッドゾーン(進入禁止域調査)                          *
//*********************************************************
/* (副関数)荷物を置ける位置posiから再帰で波状に調べる */
void dead_zone_sub(int posi) {
    int posj, posk, vect;

    dead_map[posi]=0;
    for (vect=0; vect<4; vect++) {
        posj = posi + diff[vect];
        posk = posj + diff[vect];
        if (dead_map[posj]==1 && wall_map[posj]==0 && wall_map[posk]==0)
            dead_zone_sub(posj);             // 荷物を引っ張れるなら
    }
}

/* (主関数)荷物の進入可能位置を格納地点から調べる */
void dead_zone() {                           // 0:進入可能  1:進入禁止
    int  i;

    for (i=0; i<AREA; i++) dead_map[i]=1;
    for (i=0; i<AREA; i++)                   // 格納地点から調査
        if (goal_map[i]==1 && dead_map[i]==1) dead_zone_sub(i);
}

//*********************************************************
//* フリーズチェック(凍結検査)                            *
//*********************************************************
int  Fflag;  // 特例フラグ(凍結でも関連荷物総てが格納地点ならOK)

/* (副関数)vectの直角方向にpospの荷物を移動可能か */
int  freeze_check_sub(int posp, int vect) {   // 返り値= 0:移動可 1:移動不可
    int pos1, pos2, retn=0;

    if (goal_map[posp] == 0) Fflag=1;  // 特例権利の喪失
    vect = (vect + 1) % 2;             // 方向を直角方向へ変換
    pos1 = posp - diff[vect];
    pos2 = posp + diff[vect];

    if (main_map[pos1]==1 || main_map[pos2]==1) return 1;  // どちらか壁
    if (dead_map[pos1]==1 && dead_map[pos2]==1) return 1;  // どちらもデッド

    main_map[posp] = 1;         // 堂々巡り防止の為荷物を一端壁にする
    if (main_map[pos1]==2 && freeze_check_sub(pos1,vect)==1) retn=1;
    else
    if (main_map[pos2]==2 && freeze_check_sub(pos2,vect)==1) retn=1;
    main_map[posp] = 2;         // 荷物に戻す

    return retn;
}

/* (主関数)pospの荷物は凍結しているか */
int freeze_check(int posp) {    // 返り値= 0:凍結してない  1:凍結している

    Fflag = 0;
    if (freeze_check_sub(posp,0) == 0) return 0;   // 縦方向へ移動可能か
    if (freeze_check_sub(posp,1) == 0) return 0;   // 横方向へ移動可能か
    return Fflag;
}
//*********************************************************
//* 局所探索(閉空間を形成している荷物だけのミニ探索)      *
//*********************************************************
byte taih_map[] = new byte[AREA];   // 本探索マップの退避
int  BQtop, BQend;                  // 局所探索用キューポインタ
int  Bkosu, Bsift, ALL_SP;
int  Btb[] = new int[K_MAX];

//----------------------------------------
// 局所荷物(閉空間を形成している荷物)抽出
//----------------------------------------
/* 閉空間位置poshを塗りつぶしながら再帰で波状に調べる */
int  get_packs_sub(int posh) {
    int posi, vect;

    work_map[posh]=1;
    for (vect=0; vect<4; vect++) {
        posi = posh + diff[vect];
        if (work_map[posi] == 0) {
            if (main_map[posi] == 0) {          // 空間の場合
                if (get_packs_sub(posi) == 1) return 1;
            } else if (main_map[posi] == 2) {   // 荷物の場合
                work_map[posi]=1;        // 2度抽出しないように
                Btb[Bkosu++] = posi;     // 荷物の位置を記録
                if (Bkosu == bbun+1) return 1;  // 多すぎ中止
            }
        }
    }
    return 0;
}
/* (主関数)poshに属する閉空間に隣接する荷物を抽出する */
int  get_packs(int posh) {
    int i;

    Bkosu=0;
    for (i=X_MAX+1; i<AREA-X_MAX-1; i++) work_map[i]=0; // 作業域初期化
    return get_packs_sub(posh);
}

//----------------------------------------
// ハッシュ(局所探索用)
//----------------------------------------
int Bhash_func(int post, int muk) {
    int  i, j, hash, retn;
    int data=0;

    /* データ圧縮 */
    for (i=j=0; j<Bkosu; i++)
        if (main_map[i] == 2) hstb[j++]=(byte)i;
    hstb[j++]=(byte)post;
    while (j <= kosu) hstb[j++]=0;

    /* 全ゴール */
    if (muk == 1) {
        for (i=0; i<Bkosu; i++)
            if (goal_map[hstb[i]] != 1) break;
        if (i == Bkosu) return 3;              // 3:全ゴールならOK
    } else {
        for (i=0; i<Bkosu; i++)
            if (pack_map[hstb[i]] != 1) break;
        if (i == Bkosu) return 5;              // 5:全ゴールならOK
    }

    /* ハッシュ値を求める */
    for (i=Bkosu; i>=0; i--)
        data = (data << Bsift) + hstb[i];
    if (data < 0) data *= -1;
    hash = (int)(data % B_SIZE);

    /* バケット内を探す */
    for (;;) {
        if (BACKET[hash][0] == 0) break;
        for (i=0; i<=kosu; i++)
            if (BACKET[hash][i] != hstb[i]) break;
        if (i > kosu) {
            if ((retn = OPTN[hash]) == 0) return 0; // 0:続行
            if ((retn>>2) == muk-1) return retn;    // 3,5:OK 4,6:NO
        }
        hash = (hash + R_HASH) % B_SIZE;
    }

    /* バケットに記録 */
    for (i=0; i<=kosu; i++)
        BACKET[hash][i]=hstb[i];
    QUEUE[BQend++]=hash;

    if (BQend-Qadd1>1000) return (muk==1)? 3: 5;    // 3,5:打切りOK

    return 0;                                       // 0:続行
}

//----------------------------------------
// 幅優先探索(局所探索用)
//----------------------------------------
int btb[] = new int[K_MAX];
int Bsearch(int all_space, int muk) {
    int i, adr, posp, posi, posj, pack, vect, post;

    while (BQtop < BQend) {
        adr=QUEUE[BQtop++];
        for (i=0; i<Bkosu; i++)
            main_map[btb[i]=BACKET[adr][i]]=2;
        active_zone(b1map,BACKET[adr][Bkosu]);

        for (pack=0; pack<Bkosu; pack++) {
            posp = btb[pack];                     // 荷物の位置
            for (vect=0; vect<4; vect++) {
                posi = posp + diff[vect];         // 荷物の手前
                if (b1map[posi] != 1) continue;
                if (muk == 1) {
                    posj = posp - diff[vect];     // 荷物の先
                    if (main_map[posj]==0 && dead_map[posj]==0) {
                        main_map[posj]=2;
                        main_map[posp]=0;
                        if (freeze_check(posj) == 0) {
                            post = active_zone(b2map,posp);
                            if (ac_cnt == all_space) return 3;       // 3:OK(閉空間で無くなった)
                            if (Bhash_func(post,muk) == 3) return 3; // 3:OK(全ゴールor過去の記録より)
                        }
                        main_map[posj]=0;
                        main_map[posp]=2;
                    }
                } else {
                    posj = posi + diff[vect];     // 荷物の前の前
                    if (main_map[posj] == 0) {
                        main_map[posp]=0;
                        main_map[posi]=2;
                        post = active_zone(b2map,posj);
                        if (ac_cnt == all_space) return 5;       // 5:OK(閉空間で無くなった)
                        if (Bhash_func(post,muk) == 5) return 5; // 5:OK(全ゴールor過去の記録より)
                        main_map[posp]=2;
                        main_map[posi]=0;
                    }
                }
            }
        }
        for (i=0; i<Bkosu; i++) main_map[btb[i]]=0;
    }
    return (muk == 1)? 4: 6;                                    // 4,6:NO(凍結する)
}

//----------------------------------------
// 局所探索主処理(0:凍結しない  1:凍結する)
//----------------------------------------
int check1(int posp, int posh, int muk) {
    int i, vect, posi=0, post, retn;

    /* pospの荷物が閉空間に接しているか? */
    for (vect=0; vect<4; vect++) {
        posi = posp + diff[vect];
        if (main_map[posi]==0 && h2map[posi]==0) break;
    }
    if (vect == 4) return 0;

    if (muk == 1) {
        if (get_packs(posi) == 1) return 0; // 局所荷物を抽出(多すぎの場合中止)
    } else {
        if (get_packs(posh) == 1) return 0; // 局所荷物を抽出(多すぎの場合中止)
    }

    /* 本探索マップの退避と局所みにマップ作成 */
    for (i=X_MAX+1; i<AREA-X_MAX-1; i++) {
        taih_map[i]=main_map[i]; main_map[i]=wall_map[i];
    }
    for (i=0; i<Bkosu; i++) main_map[Btb[i]]=2;

    BQtop = BQend = Qadd1;
    Bsift = 24 / Bkosu;
    post = active_zone(b1map,posh);
    retn = Bhash_func(post,muk);    // 0でない場合は過去の記録より即決
                                    // 3:凍結しない  4:凍結する
    if (retn == 0) {

        /* 探索処理 */
        for (i=0; i<Bkosu; i++) main_map[Btb[i]]=0;
        retn = Bsearch(ALL_SP - Bkosu,muk);  // 引数=閉空間が解放された場合の空間面積

        /* 結果の書き込み */
        OPTN[QUEUE[Qadd1]]=(byte)retn;   // 3,5:OK  4,6:NO
        for (i=Qadd1+1; i<BQend; i++)    // 作業に使用したバケット消去
            BACKET[QUEUE[i]][0]=0;

        if ((++backetCnt)%5000 == 0) {    // 探索進行バー
            thinkBar++; drawBar(grf);
        }
    }

    /* 本探索マップ復元 */
    for (i=X_MAX+1; i<AREA-X_MAX-1; i++)
        main_map[i]=taih_map[i];

    return (retn==3 || retn==5)? 0: 1;
}

//*********************************************************
//* 本探索                                                *
//*********************************************************
//----------------------------------------
// ハッシュ法(本探索用)
//----------------------------------------
int hash_func(int post, int adr, int muk) {
    int  i, j, hash, data=0;

    /* 局面圧縮 */
    for (i=j=0; j<kosu; i++)
        if (main_map[i] == 2) hstb[j++]=(byte)i;
    hstb[kosu]=(byte)post;

    /* ハッシュ値を求める */
    for (i=kosu; i>=0; i--)
        data = (data << sift) + hstb[i];
    hash = data % B_SIZE;

    /* バケット内を探す */
    for (;;) {
        if (BACKET[hash][0] == 0) break;
        for (i=0; i<=kosu; i++)
            if (BACKET[hash][i] != hstb[i]) break;
        if (i > kosu) {
            if (OPTN[hash] == muk) return -1;  // 同形あり/探索続行
            chan=hash;  kara=adr;  return  0;  // 衝突地点発見
        }
        hash = (hash + R_HASH) % B_SIZE;
    }

    /* 新規パターン記録 */
    for (i=0; i<=kosu; i++)
        BACKET[hash][i]=hstb[i];
    LINK[hash]=adr;                     // リンク元
    OPTN[hash]=(byte)muk;               // 探索方向
    if (muk == 1) QUEUE[Qadd1++]=hash;
    else          QUEUE[Qadd2--]=hash;

    if ((++backetCnt)%5000 == 0) {      // 探索進行バー
        thinkBar++; drawBar(grf);
    }
    if (backetCnt >= Q_SIZE) return 4;  // 不足
    return -1;                          // 探索続行
}

//----------------------------------------
// 幅優先探索(本探索用)
//----------------------------------------
int postb[] = new int[K_MAX];

/* 通常探索 */
int search1() {
    int i, adr, posp, posi, posj, post, pack, vect;

    while (Qtop1 < Qend1) {
        adr=QUEUE[Qtop1++];
        for (i=0; i<kosu; i++)
            main_map[postb[i]=BACKET[adr][i]]=2;
        active_zone(h1map,BACKET[adr][kosu]);

        for (pack=0; pack<kosu; pack++) {
            posp = postb[pack];                   // 荷物の位置
            for (vect=0; vect<4; vect++) {
                posi = posp + diff[vect];         // 荷物の手前
                if (h1map[posi] != 1) continue;
                posj = posp - diff[vect];         // 荷物の先
                if (main_map[posj]==0 && dead_map[posj]==0) {
                    main_map[posj]=2;
                    main_map[posp]=0;
                    if (freeze_check(posj) == 0) {      // フリーズチェック
                        post = active_zone(h2map,posp);
                        if (check1(posj,posp,1) == 0)     // 局所探索
                            if ((i=hash_func(post,adr,1)) >= 0) return i;
                    }
                    main_map[posj]=0;
                    main_map[posp]=2;
                }
            }
        }
        for (i=0; i<kosu; i++) main_map[postb[i]]=0;
    }
    Qlen1 = Qadd1 - Qend1;
    Qtop1 = Qend1;
    Qend1 = Qadd1;
    if (Qlen1 == 0) return 3;    // 解が無かった
    return -1;                   // 探索続行
}

/* 逆解き探索 */
int search2() {
    int i, adr, posp, posi, posj, post, pack, vect;

    while (Qend2 < Qtop2) {
        adr=QUEUE[Qtop2--];
        for (i=0; i<kosu; i++)
            main_map[postb[i]=BACKET[adr][i]]=2;
        active_zone(h1map,BACKET[adr][kosu]);

        for (pack=0; pack<kosu; pack++) {
            posp = postb[pack];                  // 荷物の位置
            for (vect=0; vect<4; vect++) {
                posi = posp + diff[vect];        // 荷物の手前
                if (h1map[posi] != 1) continue;
                posj = posi + diff[vect];        // 荷物の前の前
                if (h1map[posj] == 1) {
                    main_map[posp]=0;
                    main_map[posi]=2;
                    post = active_zone(h2map,posj);
                    if (check1(posi,posj,2) == 0)     // 局所探索
                        if ((i=hash_func(post,adr,2)) >= 0) return i;
                    main_map[posp]=2;
                    main_map[posi]=0;
                }
            }
        }
        for (i=0; i<kosu; i++) main_map[postb[i]]=0;
    }
    Qlen2 = Qend2 - Qadd2;
    Qtop2 = Qend2;
    Qend2 = Qadd2;
    if (Qlen2 == 0) return 3;    // 解が無かった
    return -1;                   // 探索続行
}

//***************************************
//* 倉庫番を解く                        *
//***************************************
int puzzle() {
    int i, j, k, posh=0, retn;

    /* 編集マップ読み取り */
    for (i=j=k=kosu=0; i<AREA; i++) {
        switch (edit_map[i]) {
            case 0: wall_map[i]=0; goal_map[i]=0; pack_map[i]=0; main_map[i]=0;              break;
            case 1: wall_map[i]=0; goal_map[i]=1; pack_map[i]=0; main_map[i]=0;         k++; break;
            case 2: wall_map[i]=0; goal_map[i]=0; pack_map[i]=1; main_map[i]=2; kosu++;      break;
            case 3: wall_map[i]=0; goal_map[i]=1; pack_map[i]=1; main_map[i]=2; kosu++; k++; break;
            case 4: wall_map[i]=1; goal_map[i]=0; pack_map[i]=0; main_map[i]=1;              break;
            case 5: wall_map[i]=0; goal_map[i]=0; pack_map[i]=0; main_map[i]=0; posS=i; j++; break;
        }
    }
    if (k==0 || k!=kosu || j!=1) return 2;  // 2:マップが異常/
    if (kosu > K_MAX) return 1;             // 1:荷物が多すぎ/

    /* 作業領域初期化 */
    for (i=0; i<B_SIZE; i++) BACKET[i][0]=0;
    Qtop1 = Qend1 = Qadd1 = 0;
    Qtop2 = Qend2 = Qadd2 = Q_SIZE-1;
    backetCnt = 0;
    sift = 24 / kosu;
    bbun = kosu / 2;
    if (bbun < 2) bbun=0;
    if (bbun > 6) bbun=6;

    /* ダメを詰める(3方が壁に囲まれたゴールも無く人もいない空間をつぶす) */
    k=1;
    while (k == 1) {
        k=0;
        for (posh=0; posh<AREA; posh++) {
            if (main_map[posh]==0 && goal_map[posh]==0 && posh!=posS) {
                for (i=j=0; i<4; i++)
                    if (wall_map[posh+diff[i]]==1) j++;
                if (j > 2) { wall_map[posh]=main_map[posh]=1; k=1; }
            }
        }
    }

    /* 開始型登録 */
    posh = active_zone(h1map,posS);
    hash_func(posh,-1,1);
    for (i=0; i<AREA; i++)  main_map[i]=wall_map[i];
    Qlen1 = Qadd1 - Qend1;
    Qtop1 = Qend1;
    Qend1 = Qadd1;

    /* 完了型登録(人の位置により複数ある場合がある) */
    active_zone(h1map,posh);
    ALL_SP = ac_cnt;
    for (i=0; i<AREA; i++)
        if (goal_map[i] == 1) main_map[i]=2;
    for (i=0; i<AREA; i++) h2map[i]=0;
    for (i=0; i<AREA; i++) {
        if (main_map[i]==0 && h1map[i]==1 && h2map[i]==0) {
            active_zone(h2map,i);
            for (j=0; h2map[j]==0; j++);
            if (j == i)
                if (hash_func(i,-1,2) == 0) return 0; // 初めから完成
        }
    }
    for (i=0; i<AREA; i++)  main_map[i]=wall_map[i];
    Qlen2 = Qend2 - Qadd2;
    Qtop2 = Qend2;
    Qend2 = Qadd2;

    /* デッドゾーン調査 */
    dead_zone();

    /* 間口幅調整式逆解き挟みうち法 */
    do {
        if (Qlen1 < Qlen2) retn=search1();  // 通常探索
        else               retn=search2();  // 逆解き探索
    } while (retn == -1);
    return retn;
}

//***************************************
//* javaGUIと電子頭脳のインターフェース *
//***************************************
/* 2地点間の最短ルートを見つける為の歩数マップを作る */
void root(int pos1, byte step) {
    int pos2, vect;

    work_map[pos1]=step++;
    for (vect=0; vect<4; vect++) {
        pos2 = pos1 + diff[vect];
        if (main_map[pos2]==0 && work_map[pos2]>step)
            root(pos2,step);
    }
}
void computer() {
    int ret, next, prev, idx, adr, i, vect, step;
    int pos0, pos1=0, pos2=0, posh=0, posa, posb=0;

    thinkBar = push = 0;

    ret = puzzle();
    if (ret == 0) {
        /* リンクポインタを解答手順向きに統一する */
        if (OPTN[chan] == 1) {
            next=kara; adr=chan;
        } else {
            next=chan; adr=kara;
        }
        for ( ; adr>=0; adr=prev) {
            prev=LINK[adr];  LINK[adr]=next;  next=adr;
        }

        /* 解答アニメ用データを作る               */
        /*   QUEUE[]をアニメ用データに併用        */
        /*   main_map[]を荷を押す前の局面に使用   */
        /*   dead_map[]を荷を押した後の局面に併用 */
        adr=QUEUE[0];
        for (i=0; i<AREA; i++) main_map[i]=wall_map[i];
        for (i=0; i<kosu; i++) main_map[BACKET[adr][i]] = 2;
        posh = posS;    // 最初の人の位置
        idx=0;
        /* リンクポインタを辿る */
        for (adr=LINK[adr],tesu=0; adr>=0; adr=LINK[adr],tesu++) {
            for (i=0; i<AREA; i++) dead_map[i]=wall_map[i];
            for (i=0; i<kosu; i++) dead_map[BACKET[adr][i]] = 2;
            for (i=0; i<AREA; i++) {
                if (main_map[i]==2 && dead_map[i]==0) pos1=i; // 押す前の位置
                if (main_map[i]==0 && dead_map[i]==2) pos2=i; // 押した後の位置
                work_map[i] = (byte)AREA;
            }
            pos0 = pos1 - ( pos2 - pos1);                     // 押す前の人の位置
            root(pos0,(byte)1);                        // 歩数マップを作る
            work_map[pos1]=0;
            /* 歩数マップを見ながら最短ルートを歩く */
            for (posa=posh,step=work_map[posh]-1; step>=0; posa=posb,step--) {
                for (vect=0; vect<4; vect++) {
                    posb = posa + diff[vect];
                    if (work_map[posb] == step) break;
                }
                QUEUE[idx++] = posa*10000 + posb*10 + vect;   // 登録
            }

            /* 次の局面変化の為の準備 */
            for (i=0; i<AREA; i++)
                main_map[i]=dead_map[i];
            posh=pos1;
        }
        QUEUE[idx]=-1;           // 終端を(-1)で閉じる
        ansMsg=6;
    } else {
        ansMsg=ret;
    }
}
}