#include #include #include #include #include #include #include #include #include #include using namespace std; typedef long long ll; typedef ll S; struct VEC { int x, y; VEC() : x(0), y(0){} VEC(int x, int y) : x(x), y(y){} VEC& operator+=(const VEC& rh){ x += rh.x; y += rh.y; return *this; } const VEC operator+(const VEC& rh) const{ VEC v(*this); return v += rh; } bool operator<(const VEC& rh) const{ return y != rh.y ? y < rh.y : x < rh.x; } bool operator==(const VEC& rh) const{ return x == rh.x && y == rh.y; } }; struct STATE { VEC rocks[3]; VEC player; STATE(){ memset(this, 0, sizeof(STATE)); } STATE(S s){ rocks[0] = VEC((int)(s >> 0) & 0xf, (int)(s >> 4) & 0xf); rocks[1] = VEC((int)(s >> 8) & 0xf, (int)(s >> 12) & 0xf); rocks[2] = VEC((int)(s >> 16) & 0xf, (int)(s >> 20) & 0xf); player = VEC((int)(s >> 24) & 0xf, (int)(s >> 28) & 0xf); sort(rocks, rocks + 3); } S get(){ sort(rocks, rocks + 3); return ((ll)rocks[0].x << 0) | ((ll)rocks[0].y << 4) | ((ll)rocks[1].x << 8) | ((ll)rocks[1].y << 12) | ((ll)rocks[2].x << 16) | ((ll)rocks[2].y << 20) | ((ll)player.x << 24) | ((ll)player.y << 28); } int findRock(VEC v){ return (int)distance(rocks, find(rocks, rocks + 3, v)); } bool finished(const set& repos){ bool result = true; for (int i = 0; i < 3 && result; ++i){ result = repos.count(rocks[i]) != 0; } return result; } }; enum OBJECT { EMPTY, WALL }; static const int SIZE = 16; OBJECT FIELD[SIZE][SIZE]; static const VEC DIRECTION[4] = { VEC(1, 0), VEC(0, 1), VEC(-1, 0), VEC(0, -1) }; static string LETTERS[256]; int main() { //ifstream cin("kawaki.in.txt"); int width, height; while (cin >> width >> height && width && height){ fill_n((OBJECT*)FIELD, sizeof(FIELD) / sizeof(OBJECT), WALL); STATE initState; int rockIndex = 0; set repos; string line; getline(cin, line); for (int y = 0; y < height; ++y){ getline(cin, line); for (int x = 0; x < width; ++x){ char c = line[x]; switch(c){ case '.': FIELD[x][y] = EMPTY; break; case '#': break; case '*': initState.rocks[rockIndex++] = VEC(x, y); FIELD[x][y] = EMPTY; break; case '@': initState.player = VEC(x, y); FIELD[x][y] = EMPTY; break; case '_': repos.insert(VEC(x, y)); FIELD[x][y] = EMPTY; break; } } } set close; vector open; open.push_back(initState.get()); bool finished = false; int counter = 0; while (!finished && !open.empty()){ vector next; ++counter; //cerr << counter << " "; for (vector::iterator prevState = open.begin(); prevState != open.end() && !finished; ++prevState){ for (int direction = 0; direction < 4 && !finished; ++direction){ STATE currentState(*prevState); const VEC nextPlayer = currentState.player + DIRECTION[direction]; //壁なら処理スキップ if (FIELD[nextPlayer.x][nextPlayer.y] == WALL){ continue; } const int rockIndex = currentState.findRock(nextPlayer); //岩なら岩を押せるかどうか判定 if (rockIndex < 3){ const VEC nextRock = currentState.rocks[rockIndex] + DIRECTION[direction]; if (FIELD[nextRock.x][nextRock.y] == WALL || currentState.findRock(nextRock) != 3){ continue; } //押す currentState.rocks[rockIndex] = nextRock; } //プレイヤー移動 currentState.player = nextPlayer; //登録 if (!close.count(currentState.get())){ close.insert(currentState.get()); next.push_back(currentState.get()); } //終了判定 if (finished = currentState.finished(repos)){ //finishedState = currentState.get(); } } } swap(open, next); } if (!finished){ cout << "-1" << endl; continue; } cout << counter << endl; } }