#include <bits/stdc++.h>
using namespace std;
struct Pos {
int r, c;
bool operator==(const Pos &o) const { return r == o.r && c == o.c; }
};
struct State {
Pos player, box;
int g, h;
bool operator>(const State &o) const { return g + h > o.g + o.h; }
};
// key lưu trạng thái player + box
struct StateKey {
Pos p, b;
bool operator==(const StateKey &o) const {
return p.r == o.p.r && p.c == o.p.c && b.r == o.b.r && b.c == o.b.c;
}
};
struct StateKeyHash {
size_t operator()(const StateKey &k) const {
return ((k.p.r * 31 + k.p.c) << 16) ^ (k.b.r * 31 + k.b.c);
}
};
int R = 16, C = 16;
vector<string> grid;
Pos myPos;
vector<Pos> goalsA, goalsB;
vector<vector<bool>> isDeadCorner;
vector<vector<int>> distGoalA;
int dr[4] = {-1, 1, 0, 0};
int dc[4] = {0, 0, -1, 1};
char moveChar[4] = {'U', 'D', 'L', 'R'};
bool avoidB = true;
bool inBounds(int r, int c) {
return r >= 0 && r < R && c >= 0 && c < C;
}
bool isWall(int r, int c) {
if (!inBounds(r, c)) return true;
if (grid[r][c] == '#') return true;
if (avoidB && grid[r][c] == 'b') return true;
return false;
}
bool isGoalA(const Pos &p) {
for (auto &g : goalsA) if (p == g) return true;
return false;
}
bool isGoalB(const Pos &p) {
for (auto &g : goalsB) if (p == g) return true;
return false;
}
void computeDeadCorners() {
isDeadCorner.assign(R, vector<bool>(C, false));
for (int r = 0; r < R; r++) {
for (int c = 0; c < C; c++) {
if (grid[r][c] == '#' || isGoalA({r, c})) continue;
bool up = isWall(r - 1, c), down = isWall(r + 1, c);
bool left = isWall(r, c - 1), right = isWall(r, c + 1);
if ((up && left) || (up && right) || (down && left) || (down && right)) {
isDeadCorner[r][c] = true;
}
}
}
}
void precomputeDistGoalA() {
distGoalA.assign(R, vector<int>(C, 1e9));
for (int r = 0; r < R; r++) {
for (int c = 0; c < C; c++) {
for (auto &g : goalsA) {
distGoalA[r][c] = min(distGoalA[r][c], abs(r - g.r) + abs(c - g.c));
}
}
}
}
string aStarBox(Pos startPlayer, Pos startBox) {
priority_queue<State, vector<State>, greater<State>> pq;
unordered_map<StateKey, StateKey, StateKeyHash> parent;
unordered_map<StateKey, char, StateKeyHash> moveTaken;
unordered_map<StateKey, int, StateKeyHash> bestDistToGoal;
auto heuristic = [&](const Pos &player, const Pos &box) {
return distGoalA[box.r][box.c] * 3 + abs(player.r - box.r) + abs(player.c - box.c);
};
auto boxDist = [&](const Pos &b) {
return distGoalA[b.r][b.c];
};
pq.push({startPlayer, startBox, 0, heuristic(startPlayer, startBox)});
bestDistToGoal[{startPlayer, startBox}] = boxDist(startBox);
while (!pq.empty()) {
State cur = pq.top(); pq.pop();
if (isGoalA(cur.box)) {
string path;
StateKey key = {cur.player, cur.box};
while (parent.count(key)) {
path.push_back(moveTaken[key]);
key = parent[key];
}
reverse(path.begin(), path.end());
return path;
}
if (cur.g > 40) continue; // giới hạn độ sâu
for (int k = 0; k < 4; k++) {
Pos newPlayer = {cur.player.r + dr[k], cur.player.c + dc[k]};
if (isWall(newPlayer.r, newPlayer.c)) continue;
if (newPlayer == cur.box) { // push
Pos newBox = {cur.box.r + dr[k], cur.box.c + dc[k]};
if (isWall(newBox.r, newBox.c) || (isDeadCorner[newBox.r][newBox.c] && !isGoalA(newBox)) || isGoalB(newBox))
continue;
int ndist = boxDist(newBox);
StateKey key = {newPlayer, newBox};
bool leavingGoal = isGoalA(cur.player) || isGoalA(newPlayer);
if (!bestDistToGoal.count(key) || ndist < bestDistToGoal[key] ||
(leavingGoal && ndist > 0 && ndist <= bestDistToGoal[key])) {
bestDistToGoal[key] = ndist;
pq.push({newPlayer, newBox, cur.g + 1, heuristic(newPlayer, newBox)});
parent[key] = {cur.player, cur.box};
moveTaken[key] = moveChar[k];
}
}
else { // walk
StateKey key = {newPlayer, cur.box};
int ndist = boxDist(cur.box);
bool leavingGoal = isGoalA(cur.player) || isGoalA(newPlayer);
if (!bestDistToGoal.count(key) || ndist < bestDistToGoal[key] ||
(leavingGoal && ndist > 0 && ndist <= bestDistToGoal[key])) {
bestDistToGoal[key] = ndist;
pq.push({newPlayer, cur.box, cur.g + 1, heuristic(newPlayer, cur.box)});
parent[key] = {cur.player, cur.box};
moveTaken[key] = moveChar[k];
}
}
}
}
return "";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
grid.resize(R);
for (int i = 0; i < R; i++) {
cin >> grid[i];
for (int j = 0; j < C; j++) {
if (grid[i][j] == 'a') myPos = {i, j};
if (grid[i][j] == 'A') goalsA.push_back({i, j});
if (grid[i][j] == 'B') goalsB.push_back({i, j});
}
}
computeDeadCorners();
precomputeDistGoalA();
vector<Pos> boxes;
for (int r = 0; r < R; r++)
for (int c = 0; c < C; c++)
if (grid[r][c] == 'X') boxes.push_back({r, c});
// Ưu tiên box gần nhất (player→box + box→goal)
sort(boxes.begin(), boxes.end(), [&](const Pos &p1, const Pos &p2) {
int d1 = abs(myPos.r - p1.r) + abs(myPos.c - p1.c) + distGoalA[p1.r][p1.c];
int d2 = abs(myPos.r - p2.r) + abs(myPos.c - p2.c) + distGoalA[p2.r][p2.c];
return d1 < d2;
});
string chosenPath;
for (auto &box : boxes) {
if (isDeadCorner[box.r][box.c] && !isGoalA(box)) continue;
if (abs(myPos.r - box.r) + abs(myPos.c - box.c) > 20) continue;
string path = aStarBox(myPos, box);
if (!path.empty()) {
chosenPath = path;
break;
}
}
if (!chosenPath.empty()) {
cout << chosenPath[0] << "\n";
return 0;
}
// fallback
vector<int> dirs = {0, 1, 2, 3};
random_shuffle(dirs.begin(), dirs.end());
for (int k : dirs) {
int nr = myPos.r + dr[k], nc = myPos.c + dc[k];
if (!isWall(nr, nc)) {
cout << moveChar[k] << "\n";
return 0;
}
}
cout << "U\n";
return 0;
}