Advertisement
Guest User

SlidingPuzzleSolver.java

a guest
Sep 12th, 2011
467
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 15.17 KB | None | 0 0
  1. import java.io.FileReader;
  2. import java.io.BufferedReader;
  3. import java.util.PriorityQueue;
  4. import java.util.HashMap;
  5. import java.util.Arrays;
  6.  
  7. public class SlidingPuzzleSolver {
  8.     private static String solve(int w, int h, byte[] start, byte[] goal){
  9.         Board.init(w, h, start, goal);
  10.  
  11.         for(int mode = 0; mode < Board.NUMMODES; mode++){
  12.             MyQueue[] q = new MyQueue[2];
  13.             q[0] = new MyQueue();
  14.             q[1] = new MyQueue();
  15.             Board board;
  16.  
  17.             Board.setWeight(mode);
  18.             q[0].offer(new Board(start, true));
  19.             q[1].offer(new Board(goal, false));
  20.  
  21.             try{
  22.                 while (true) {
  23.                     for (int i = 0; i < 2; i++) {
  24.                         do {
  25.                             board = q[i].poll();
  26.                         } while (board.obsolete);
  27.                         if (q[1 - i].get(board) != null) {
  28.                             String res;
  29.                             if (i == 0) {
  30.                                 res = board.moves() +
  31.                                         reverse(q[1].get(board).moves());
  32.                             } else {
  33.                                 res = q[0].get(board).moves() +
  34.                                         reverse(board.moves());
  35.                             }
  36.                             return res;
  37.                         }
  38.                         for (int d = 0; d < 4; d++) {
  39.                             q[i].offer(board.move(d));
  40.                         }
  41.                     }
  42.                 }
  43.             } catch(OutOfMemoryError e){
  44.             }
  45.         }
  46.         return "";
  47.     }
  48.  
  49.     private static String reverse(String str){
  50.         String res = "";
  51.         for(int i = str.length() - 1; i >= 0; i--){
  52.             char c = str.charAt(i);
  53.             if(c == 'L') res += 'R';
  54.             else if(c == 'R') res += 'L';
  55.             else if(c == 'U') res += 'D';
  56.             else if(c == 'D') res += 'U';
  57.         }
  58.         return res;
  59.     }
  60.  
  61.     public static void main(String[] args){
  62.         if(args.length != 1){
  63.             System.err.println("usage: java SlidingPuzzle inputfile");
  64.             return;
  65.         }
  66.  
  67.         try {
  68.             int LX, RX, UX, DX, N;
  69.             BufferedReader input = new BufferedReader(new FileReader(args[0]));
  70.             String[] str;
  71.  
  72.             str = input.readLine().split(" ");
  73.             if(str.length != 4){
  74.                 throw new RuntimeException("line 1 must have exactly 4 integers.");
  75.             }
  76.             LX = Integer.parseInt(str[0]);
  77.             RX = Integer.parseInt(str[1]);
  78.             UX = Integer.parseInt(str[2]);
  79.             DX = Integer.parseInt(str[3]);
  80.             str = input.readLine().split(" ");
  81.             if(str.length != 1){
  82.                 throw new RuntimeException("line 2 must be a single integer.");
  83.             }
  84.             N = Integer.parseInt(str[0]);
  85.  
  86.             for(int n = 0; n < N; n++){
  87.                 int width, height;
  88.                 str = input.readLine().split(",");
  89.                 if(str.length != 3){
  90.                     throw new RuntimeException("Incorrect format at line " + (3 + n));
  91.                 }
  92.                 width = Integer.parseInt(str[0]);
  93.                 height = Integer.parseInt(str[1]);
  94.                 if(str[2].length() != width * height){
  95.                     throw new RuntimeException("Incorrect format at line " + (3 + n));
  96.                 }
  97.                 byte[] start = new byte[(width + 1) * (height + 2)];
  98.                 byte[] goal = new byte[start.length];
  99.                 Arrays.fill(goal, (byte)(width * height));
  100.                 Arrays.fill(start, (byte)(width * height));
  101.                 for(int i = 0; i < height; i++){
  102.                     for(int j = 0; j < width; j++){
  103.                         goal[(i + 1) * (width + 1) + j + 1] = (byte)((i * width + j + 1) % (width * height));
  104.                     }
  105.                 }
  106.                 for(int k = 0; k < width * height; k++){
  107.                     char c = str[2].charAt(k);
  108.                     int index = (k / width + 1) * (width + 1) + (k % width + 1);
  109.                     if(c == '=') start[index] = goal[index] = start[0];
  110.                     else if(c >= '0' && c <= '9') start[index] = (byte)(c - '0');
  111.                     else start[index] = (byte)(c - 'A' + 10);
  112.                 }
  113.                 String res = solve(width, height, start, goal);
  114.                 System.out.println(res);
  115.             }
  116.             input.close();
  117.         } catch(Exception e){
  118.             e.printStackTrace();
  119.         }
  120.     }
  121. }
  122.  
  123. class MyQueue {
  124.     private PriorityQueue<Board> queue = new PriorityQueue<Board>();
  125.     private HashMap<Board, Board> set = new HashMap<Board, Board>();
  126.  
  127.     boolean offer(Board board){
  128.         if(board == null) return false;
  129.         Board b = get(board);
  130.         if(b != null){
  131.             if(board.compareTo(b) < 0){
  132.                 b.obsolete = true;
  133.                 set.remove(b);
  134.             } else {
  135.                 return false;
  136.             }
  137.         }
  138.         set.put(board, board);
  139.         return queue.offer(board);
  140.     }
  141.  
  142.     Board poll(){
  143.         return queue.poll();
  144.     }
  145.  
  146.     Board get(Board board){
  147.         return set.get(board);
  148.     }
  149. }
  150.  
  151. class Board implements Comparable<Board> {
  152.     static int width, height;
  153.     static int[] dir;
  154.     static char[] letter = {'L', 'U', 'R', 'D'};
  155.     static int[][] dist;
  156.     static int[][] goalPos;
  157.     static int[][] weight;
  158.     static int[] ways;
  159.     static byte wall;
  160.     static byte[][] goals = new byte[2][];
  161.  
  162.     byte[] panels;
  163.     Board prev;
  164.     int lastMove;
  165.     int space;
  166.     int g;
  167.     int f;
  168.     boolean obsolete = false;
  169.     boolean fromStart;
  170.  
  171.     Board(byte[] panels, int space, Board prev, int lastMove, boolean fromStart){
  172.         this.panels = panels;
  173.         this.space = space;
  174.         this.prev = prev;
  175.         this.lastMove = lastMove;
  176.         this.fromStart = fromStart;
  177.         g = prev.g + 1;
  178.         f = eval();
  179.     }
  180.  
  181.     Board(byte[] panels, boolean fromStart){
  182.         this.panels = panels;
  183.         for(int i = 0; i < panels.length; i++){
  184.             if(panels[i] == 0){
  185.                 space = i;
  186.                 break;
  187.             }
  188.         }
  189.         prev = null;
  190.         lastMove = -1;
  191.         this.fromStart = fromStart;
  192.         g = 0;
  193.         f = eval();
  194.     }
  195.  
  196.     static void init(int width, int height, byte[] start, byte[] goal){
  197.         Board.width = width;
  198.         Board.height = height;
  199.         wall = (byte)(width * height);
  200.         dir = new int[4];
  201.         dir[0] = -1;
  202.         dir[1] = -(width + 1);
  203.         dir[2] = 1;
  204.         dir[3] = width + 1;
  205.         goalPos = new int[2][width * height + 1];
  206.         for(int i = 0; i < start.length; i++){
  207.             if(start[i] != wall)
  208.                 goalPos[1][start[i]] = i;
  209.         }
  210.         for(int i = 1; i < goalPos[0].length; i++){
  211.             int x = (i - 1) % width;
  212.             int y = (i - 1) / width;
  213.             goalPos[0][i] = (y + 1) * (width + 1) + (x + 1);
  214.         }
  215.         goalPos[0][0] = (height + 1) * (width + 1) - 1;
  216.         dist = new int[start.length][start.length];
  217.         setDist(start);
  218.         weight = new int[2][width * height + 1];
  219.         setWeight(mode);
  220.         ways = new int[start.length];
  221.         for(int i = 0; i < ways.length; i++){
  222.             if(start[i] != wall){
  223.                 for(int d = 0; d < dir.length; d++){
  224.                     if(start[i + dir[d]] != wall) ways[i]++;
  225.                 }
  226.             }
  227.         }
  228.         goals[0] = goal;
  229.         goals[1] = start;
  230.     }
  231.  
  232.     private static void setDist(byte[] start){
  233.         final int INF = 10000;
  234.         for(int i = 0; i < dist.length; i++){
  235.             Arrays.fill(dist[i], INF);
  236.             for(int j = 0; j < dist[i].length; j++){
  237.                 if(j == i) dist[i][j] = 0;
  238.                 else if(start[i] == wall || start[j] == wall)
  239.                     dist[i][j] = INF;
  240.             }
  241.         }
  242.         for(int k = 0; k < (width + height) * 4; k++){
  243.             for(int i = 0; i < dist.length; i++){
  244.                 for(int j = 0; j < dist[i].length; j++){
  245.                     if(start[i] != wall && start[j] != wall){
  246.                         dist[i][j] = Math.min(dist[i][j], dist[i][j + 1] + 1);
  247.                         dist[i][j] = Math.min(dist[i][j], dist[i][j - 1] + 1);
  248.                         dist[i][j] = Math.min(dist[i][j], dist[i][j + width + 1] + 1);
  249.                         dist[i][j] = Math.min(dist[i][j], dist[i][j - width - 1] + 1);
  250.                     }
  251.                 }
  252.             }
  253.         }
  254.         for(int i = 0; i < dist.length; i++){
  255.             for(int j = 0; j < dist[i].length; j++){
  256.                 if(dist[i][j] == INF) dist[i][j] = 0;
  257.             }
  258.         }
  259.     }
  260.  
  261.     static final int NUMMODES = 8;
  262.     static int mode = 0;
  263.     static void setWeight(int mode){
  264.         Board.mode = mode;
  265.         weight[0][0] = 0;
  266.         weight[1][0] = 0;
  267.         for(int i = 1; i < weight[0].length; i++){
  268.             int x = (i - 1) % width;
  269.             int y = (i - 1) / width;
  270.  
  271.             if(mode == 0){ // uniform
  272.                 weight[0][i] = 1;
  273.                 weight[1][i] = 1;
  274.             } else if(mode == 1){ // step
  275.                 weight[0][i] = dist[goalPos[1][0]][i] < (width + height) / 2 ? 1 : 2;
  276.                 weight[1][i] = dist[goalPos[0][0]][i] < (width + height) / 2 ? 1 : 2;
  277.             } else if(mode <= 4){ // uniform
  278.                 weight[0][i] = mode;
  279.                 weight[1][i] = mode;
  280.             } else if(mode == 5){ // vertical
  281.                 weight[0][i] = height - y;
  282.                 weight[1][i] = 1;
  283.             } else if(mode == 6){ // horizontal
  284.                 weight[0][i] = width - x;
  285.                 weight[1][i] = 1;
  286.             } else if(mode == 7){ // diagonal
  287.                 weight[0][i] = width + height - 1 - (x + y);
  288.                 weight[1][i] = 1;
  289.             }
  290.         }
  291.     }
  292.  
  293.     Board move(int d){
  294.         if(panels[space + dir[d]] == wall || (d ^ 2) == lastMove) return null;
  295.         byte[] nextPanels = Arrays.copyOf(panels, panels.length);
  296.         nextPanels[space] = panels[space + dir[d]];
  297.         nextPanels[space + dir[d]] = 0;
  298.         return new Board(nextPanels, space + dir[d], this, d, fromStart);
  299.     }
  300.  
  301.     String moves(){
  302.         String res = "";
  303.         Board board = this;
  304.         while(board.lastMove >= 0){
  305.             res = letter[board.lastMove] + res;
  306.             board = board.prev;
  307.         }
  308.         return res;
  309.     }
  310.  
  311.     private int eval(){ // 評価関数
  312.         int h = 0; // 評価値
  313.         int target = fromStart ? 0 : 1; // 探索の向き
  314.         for(int i = 1; i < panels.length; i++){ // ボードの各パネルについて
  315.             if(panels[i] != wall && panels[i] != 0){ // 文字のパネルなら
  316.                 int hi = 0; // i 番目のパネルの評価値
  317.                 hi += dist[i][goalPos[target][panels[i]]]; // 壁を考慮した最短距離
  318.  
  319.                 // 隣接するパネルの入れ替えに対するペナルティ
  320.                 if(dist[i][goalPos[target][panels[i]]] == 1 && // 目標位置までの距離が 1 で、かつ、
  321.                         goalPos[target][panels[goalPos[target][panels[i]]]] == i){ // 目標位置にあるパネルの目標位置が i ならペナルティ
  322.                     hi += 1;
  323.                     if(ways[i] == 2) hi += 2; // 位置 i から動かせる方向が 2 通りしかない場合は、遠回りが必要なので追加ペナルティ
  324.                 }
  325.  
  326.                 // 目標状態において隣接するはずのパネルが隣接していない場合のペナルティ
  327.                 if(ways[goalPos[target][panels[i]]] >= 2){ // 何のための条件だったか忘れた
  328.                     int nn = 0; // 目標状態と現在の状態の両方で隣接しているパネルの数だったはずだけど結局全部数えていない
  329.                     for(int d1 = 0; d1 < dir.length; d1++){ // 現在の状態の 4 方向と、
  330.                         if(nn == 0 && panels[i + dir[d1]] != wall){ // 空白と壁を除外して、
  331.                             for(int d2 = 0; d2 < dir.length; d2++){ // 目標状態の 4 方向を総当たり
  332.                                 if(panels[i + dir[d1]] == space || panels[i + dir[d1]] == goals[target][goalPos[target][panels[i]] + dir[d2]]){
  333.                                     // 空白が隣接している、または両方の状態で同じパネルが隣接していればカウント
  334.                                     // 今気が付いたけど、 space は空白の位置を表すので正しくは i + dir[d1] == space
  335.                                     nn++;
  336.                                     break;
  337.                                 }
  338.                             }
  339.                         }
  340.                     }
  341.                     if(nn == 0){ // 隣接しているパネルがなければペナルティ
  342.                         hi += 1;
  343.                         if(mode > 0) hi += 2; // mode == 0 (重みなし)で解けなかった場合に追加、これもある種の重み付け
  344.                     }
  345.                 }
  346.  
  347.                 // 位置 i のパネルは目標位置にあるが、周囲のパネルを揃えるために一度動かさなければならない場合のペナルティ
  348.                 // 隣接する位置 j のパネルが目標状態と異なり、 j から動かせる方向が(i を含めて) 2 通りしかないとき、
  349.                 // 位置 i のパネルを少なくとも一度動かす必要がある
  350.                 if(goalPos[target][panels[i]] == i){ // 位置 i のパネルが目標位置にある場合
  351.                     for(int d = 0; d < dir.length; d++){
  352.                         int j = i + dir[d]; // i に隣接する位置
  353.                         if(ways[j] == 2 && // 位置 j から動かせる方向が 2 通りで、
  354.                                 panels[j] != 0 && // j にあるパネルが空白でなく、
  355.                                 goalPos[target][0] != j && // 目標状態の空白の位置が j でなく、
  356.                                 goalPos[target][panels[j]] != j){ // j にあるパネルの目標位置が j でない場合
  357.                             hi += 2;
  358.                             if(ways[i] == 2) hi += 2; // 位置 i から動かせる方向も 2 通りしかなければ追加ペナルティ
  359.                             break;
  360.                         }
  361.                     }
  362.                 }
  363.                 h += hi * weight[target][panels[i]]; // 重み付け
  364.             }
  365.         }
  366.         return g + h;
  367.     }
  368.  
  369.     public int compareTo(Board b){
  370.         if(f > b.f) return 1;
  371.         if(f < b.f) return -1;
  372.         return 0;
  373.     }
  374.  
  375.     public boolean equals(Object o){
  376.         return java.util.Arrays.equals(((Board)o).panels, panels);
  377.     }
  378.  
  379.     public int hashCode(){
  380.         int res = 1;
  381.         int m = 37;
  382.         for(int i = 0; i < height; i++){
  383.             for(int j = 0; j < width; j++){
  384.                 res += panels[(i + 1) * (width + 1) + (j + 1)];
  385.                 res *= m;
  386.             }
  387.         }
  388.         return res;
  389.     }
  390. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement