Package

Source Code of Encoding

/**
*
* @author Liang Qu
*/
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.lang.reflect.InvocationTargetException;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.ListIterator;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.event.MouseInputAdapter;

@SuppressWarnings("serial")
public class JKlotski extends JFrame {
  private JButton[] pieces;
  String startPattern;
  JPanel board;
  JPanel control;
  JTextArea text;
  JButton load_b;
  JButton solve_b;
  JButton next_b;
  JButton previous_b;
  JButton start_b;
  JButton stop_b;
  JLabel label;

  Game g;
  char curr;
  int x;
  int y;

  int pathPos;
  boolean autoShow;

  public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        new JKlotski("2442\n2442\n2332\n2112\n1001\n").setVisible(true);
      }
    });

  }

  private String parse(String str) {

    final char[] trans2 = "bcdef".toCharArray();
    final char[] trans1 = "ghij".toCharArray();

    int index1 = 0;
    int index2 = 0;

    str = str.replaceAll("[\n\r]", "");

    char[] input = str.toCharArray();

    for (int i = 0; i < 20; i++) {
      switch (input[i]) {
      case '4':
        input[i] = input[i + 1] = input[i + 4] = input[i + 5] = 'a';
        break;
      case '1':
        input[i] = trans1[index1++];
        break;
      case '2':
        input[i] = input[i + 4] = trans2[index2++];
        break;
      case '3':
        input[i] = input[i + 1] = (char) (trans2[index2++] - 'a' + 'A');
        break;
      case '0':
        input[i] = ' ';
        break;
      default: // do nothing
      // System.out.println(input[i]);
      }
    }// conversion done

    return new String(input);
  }

  private void update() {
    char[] sID = g.getSpecialID().toCharArray();
    for (int i = 0; i < 20; i++) {
      if (sID[i] != '#') {
        switch(sID[i]) {
        case 'a':
          sID[i+1] = sID[i+4] = sID[i+5] = '#';
          ImageIcon a1 = new ImageIcon("images/a.gif");
          pieces[i].setName("a");
          pieces[i+1].setName("a");
          pieces[i+4].setName("a");
          pieces[i+5].setName("a");
          pieces[i].setBackground(chooseColor("a"));
          pieces[i+1].setBackground(chooseColor("a"));
          pieces[i+4].setBackground(chooseColor("a"));
          pieces[i+5].setBackground(chooseColor("a"));
          pieces[i].setIcon(a1);
          pieces[i+1].setIcon(a1);
          pieces[i+4].setIcon(a1);
          pieces[i+5].setIcon(a1);          break;
        case 'b': case 'c': case 'd': case 'e': case 'f':
          sID[i+4] = '#';
          ImageIcon b1 = new ImageIcon("images/b1.gif");
          ImageIcon b2 = new ImageIcon("images/b2.gif");
          pieces[i].setName("" + sID[i]);
          pieces[i].setBackground(chooseColor("" + sID[i]));
          pieces[i].setIcon(b1);
          pieces[i+4].setName("" + sID[i]);
          pieces[i+4].setBackground(chooseColor("" + sID[i]));
          pieces[i+4].setIcon(b2);
          break;
        case 'B': case 'C': case 'D': case 'E': case 'F':
          sID[i+1] = '#';
          ImageIcon e1 = new ImageIcon("images/e1.gif");
          ImageIcon e2 = new ImageIcon("images/e2.gif");
          pieces[i].setName("" + sID[i]);
          pieces[i].setBackground(chooseColor("" + sID[i]));
          pieces[i].setIcon(e1);
          pieces[i+1].setName("" + sID[i]);
          pieces[i+1].setBackground(chooseColor("" + sID[i]));
          pieces[i+1].setIcon(e2);
          break;
        case 'g': case 'h': case 'i': case 'j':
          ImageIcon g = new ImageIcon("images/g.gif");
          pieces[i].setName("" + sID[i]);
          pieces[i].setBackground(chooseColor("" + sID[i]));
          pieces[i].setIcon(g);
          break;
        default//blank
          pieces[i].setIcon(null);
          pieces[i].setName(" ");
          pieces[i].setBackground(chooseColor(" "));
        }
  //        ImageIcon cup = new ImageIcon("cup.gif");
  //        pieces[i].setIcon(cup);
        pieces[i].setName("" + sID[i]);
        pieces[i].setBackground(chooseColor("" + g.getSpecialID().charAt(i)));
        // buttons[i].repaint();
      }
    }
  }

  private Color chooseColor(String str) {
    str = str.toLowerCase();
    char c = str.charAt(0);
    switch (c) {
    case 'a':
      return Color.RED;
    case 'b':
      return Color.CYAN;
    case 'c':
      return Color.GREEN;
    case 'd':
      return Color.ORANGE;
    case 'e':
      return Color.BLACK;
    case 'f':
      return Color.MAGENTA;
    case 'g':
      return Color.BLUE;
    case 'h':
      return Color.PINK;
    case 'i':
      return Color.GRAY;
    case 'j':
      return Color.YELLOW;
    default:
      return Color.WHITE;
    }
  }

  // public JKlotski (String str) {
  // this();
  // startPattern = parse(str);
  // g = new Game(startPattern);
  // update();
  // }

  public JKlotski(String str) {
    startPattern = parse(str);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setResizable(false);
    setSize(800, 500);
    getContentPane().setLayout(new GridLayout(1, 2));

    board = new JPanel();
    board.setSize(400, 500);
    control = new JPanel();
    control.setSize(400, 500);

    add(board);
    add(control);

    control.setLayout(new GridLayout(5, 4));
    text = new JTextArea();
    text.setText(str);
    load_b = new JButton("Reload");
    solve_b = new JButton("Solve");
    next_b = new JButton("Next");
    previous_b = new JButton("Previous");
    start_b = new JButton("Start");
    stop_b = new JButton("Stop");
    next_b.setEnabled(false);
    previous_b.setEnabled(false);
    start_b.setEnabled(false);
    stop_b.setEnabled(false);
    label = new JLabel();
    control.add(text);
    control.add(load_b);
    control.add(solve_b);
    control.add(label);
    control.add(previous_b);
    control.add(next_b);
    control.add(start_b);
    control.add(stop_b);

    load_b.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        String str = text.getText().replaceAll("[\n\r]", "");
        if (str.matches("[0-4]*") && str.length() == 20) {
          pathPos = 0;
          Game.path.clear();

          startPattern = parse(str);
//          System.out.println(startPattern);
          g = new Game(startPattern);
          update();
          label.setText("Loaded!");
          next_b.setEnabled(false);
          previous_b.setEnabled(false);
          start_b.setEnabled(false);
          stop_b.setEnabled(false);
        } else {
          label.setText("Wrong Format! Try again!");
        }
      }
    });

    solve_b.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        Thread rnb = new Thread() {
          @Override
          public void run() {
                        try {
                            SwingUtilities.invokeAndWait(new Runnable() {

                                public void run() {
                                    solve_b.setEnabled(false);
                                }
                            });
                        } catch (InterruptedException ex) {
                            Logger.getLogger(JKlotski.class.getName()).log(Level.SEVERE, null, ex);
                        } catch (InvocationTargetException ex) {
                            Logger.getLogger(JKlotski.class.getName()).log(Level.SEVERE, null, ex);
                        }
                       
            boolean solvable = Game.search(g);
            if (solvable) {
              pathPos = 0;
              startPattern = g.toString();
              next_b.setEnabled(true);
              previous_b.setEnabled(true);
              start_b.setEnabled(true);
              stop_b.setEnabled(true);
              solve_b.setEnabled(true);
              SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                  Game.path.addFirst(new Game(startPattern)
                      .toString());
                  label.setText("Solved in "
                      + (Game.path.size() - 1)
                      + " steps.");
                }
              });
            } else {
              SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                  label.setText("No solution!");
                }
              });
            }
          }
        };
        rnb.start();
      }
    });

    next_b.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        if (pathPos >= 0 && pathPos < Game.path.size() - 1) {
          g = new Game(Game.path.get(++pathPos));
          update();
        }
      }
    });

    previous_b.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        if (pathPos > 0 && pathPos < Game.path.size()) {
          g = new Game(Game.path.get(--pathPos));
          update();
        }
      }
    });

    start_b.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        autoShow = true;
        Thread show = new Thread() {
          @Override
          public void run() {
            while (autoShow && pathPos < Game.path.size() - 1) {
              g = new Game(Game.path.get(++pathPos));
              try {
                SwingUtilities.invokeLater(new Runnable() {
                  public void run() {
                    update();
                  }
                });
                Thread.sleep(200);
              } catch (Exception ex) {
                ex.printStackTrace();
              }
            }
          }
        };

        show.start();
      }
    });

    stop_b.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        autoShow = false;
      }
    });

    MouseListener mouseListener = new MouseInputAdapter() {
      @Override
      public void mousePressed(MouseEvent me) {
        if (me.getButton() == MouseEvent.BUTTON1) {
          curr = ((JButton) me.getComponent()).getName().charAt(0);
          x = me.getX();
          y = me.getY();
        }
      }

      @Override
      public void mouseReleased(MouseEvent me) {
        if (me.getButton() != MouseEvent.BUTTON1) {
          return;
        }

        int changeX = me.getX() - x;
        int changeY = me.getY() - y;
        D dir = D.E;

        if (changeX == 0 && changeY == 0) {
          return;
        }

        if (Math.abs(changeX) >= Math.abs(changeY)) {
          dir = changeX > 0 ? D.E : D.W;
        } else {
          dir = changeY > 0 ? D.S : D.N;
        }

        Command cmd = new Command(curr, dir);
        if (g.isMovable(cmd)) {
          g.move(cmd);
          update();
        }
      }
    };

    g = new Game(startPattern);
    // startPattern = g.getSpecialID();

    pieces = new JButton[20];
    board.setLayout(new GridLayout(5, 4));

    for (int i = 0; i < 20; i++) {
      pieces[i] = new JButton();
      pieces[i].addMouseListener(mouseListener);
      pieces[i].setEnabled(false);
      pieces[i].setBackground(chooseColor("" + startPattern.charAt(i)));
      pieces[i].setBorderPainted(false);
      pieces[i].setName("" + startPattern.charAt(i));
      board.add(pieces[i]);
    }
   
    update();

    // start a new thread to show the solution automatically
    Thread show = new Thread() {
      @Override
      public void run() {
        while (true) {
          // System.out.println(autoShow);
          while (autoShow && pathPos < Game.path.size() - 1) {
            g = new Game(Game.path.get(++pathPos));
            try {
              SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                  update();
                }
              });
              Thread.sleep(200);
            } catch (Exception ex) {
              ex.printStackTrace();
            }
          }
        }
      }
    };
    show.start();
  }
}

enum D {
  N, E, S, W
}

class Encoding {
  /**
   * general encoding, same shape, same code e.g. abba abba aeea agga g##g
   */
  String general;

  /**
   * special encoding, same block, same code e.g. abbc abbc deef dghf i##j
   */
  String special;

  /**
   * the general encoding of parent. used in backtracking to output the path.
   * assume that in the visited set, general/special is a one-to-one mapping.
   * null means root.
   */
  String parent;

  public Encoding(String g, String s, String p) {
    general = g;
    special = s;
    parent = p;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o)
      return true;
    if (!(o instanceof Encoding))
      return false;

    Encoding ecd = (Encoding) o;

    return (this.general.equals(ecd.general));
  }

  @Override
  public int hashCode() {
    System.out.println(general.hashCode());
    return general.hashCode();
  }

}

class Command {
  char c;
  D d;

  public Command(char c, D d) {
    this.c = c;
    this.d = d;
  }

  public boolean isInverse(Command cmd) {
    if (c != cmd.c) {
      return false;
    }

    return (d == D.E && cmd.d == D.W) || (d == D.S && cmd.d == D.N)
        || (d == D.W && cmd.d == D.E) || (d == D.N && cmd.d == D.S);
  }

  @Override
  public boolean equals(Object o) {
    if (o == this)
      return true;
    if (o instanceof Command) {
      Command cmd = (Command) o;
      return c == cmd.c && d == cmd.d;
    }

    return false;
  }

  @Override
  public int hashCode() {
    int hash = 5;
    return hash;
  }
}

class Game {
  static Map<String, Encoding> visited = new Hashtable<String, Encoding>();

  static LinkedList<Encoding> deque = new LinkedList<Encoding>();

  static LinkedList<String> path = new LinkedList<String>();

  /**
   * Linear representation of the configuration. Easy for input and interface.
   * May out of date after move().
   */
  StringBuffer state;

  /**
   * Matrix representation. Easy for internal computation. Derived from
   * 'state'.
   */
  char[][] board = new char[5][4];

  /**
   * same as in Encoding, for backtracking. null means root.
   */
  String parent = null;

  public String getParent() {
    return parent;
  }

  public void setParent(String parent) {
    this.parent = parent;
  }

  /**
   * update the matrix representation using the state StringBuffer
   */
  private void update() {
    board[0] = state.substring(0, 4).toCharArray();
    board[1] = state.substring(4, 8).toCharArray();
    board[2] = state.substring(8, 12).toCharArray();
    board[3] = state.substring(12, 16).toCharArray();
    board[4] = state.substring(16).toCharArray();
  }

  // public Game() {
  // state = new StringBuffer("baacbaacdEEfdghfi  j");
  // update();
  // }

  public Game(String str) {
    state = new StringBuffer(str);
    update();
  }

  /**
   * Change the current configuration according to 'str'. Used for resuming
   * the configuration.
   *
   * @param str
   */
  public void decode(Encoding ecd) {
    state = new StringBuffer(ecd.special);
    parent = ecd.parent;
    update();
  }

  /**
   * Determines whether the given Command object is applicable to the current
   * board configuration. This is done by checking whether all the sub-pieces
   * labeled by cmd.c are movable, assuming they form a rectangle.
   *
   * @param cmd
   *            Attempting Command object (label name, direction)
   * @return Return true when the block is movable.
   */
  public boolean isMovable(Command cmd) {
    char c = cmd.c;
    D d = cmd.d;

    for (int y = 0; y < 5; y++) {
      for (int x = 0; x < 4; x++) {
        if (board[y][x] != c) {
          continue;
        }

        // check if it's hitting the border
        if (d == D.N && y == 0)
          return false;
        if (d == D.E && x == 3)
          return false;
        if (d == D.S && y == 4)
          return false;
        if (d == D.W && x == 0)
          return false;

        // check if there is a block in the way
        int xx = x;
        int yy = y;
        switch (d) {
        case N:
          yy--;
          break;
        case E:
          xx++;
          break;
        case S:
          yy++;
          break;
        case W:
          xx--;
          break;

        }
        if (board[yy][xx] != ' ' && board[yy][xx] != c) {
          return false;
        }

      }
    }

    // we are outside the loop now, safe to return true;
    return true;
  }

  /**
   * Apply the given Command. Assuming it results a valid configuration. Must
   * be called after calling isMovable() to verify that. This moves all the
   * sub-pieces of a block.
   *
   * Implemented this way, isMovable() is useless(), because invalid movement
   * will result in the same configuration.
   *
   * @param cmd
   */
  public void move(Command cmd) {
    char c = cmd.c;
    D d = cmd.d;

    // save the parent first
    String pnt = getGeneralID();

    switch (d) {
    case E:
      for (int i = 0; i < 5; i++) {
        for (int j = 3; j > 0; j--) {
          // j cannot be 0, c#fg => #cfg, done here
          // cc#d => c#cd => #ccd, done here
          if (board[i][j] == ' ' && board[i][j - 1] == c) {
            board[i][j] = c;
            board[i][j - 1] = ' ';
          }
        }
      }
      break;
    case S:
      /*
       * a a a b => b => # b # b # b b
       */
      for (int j = 0; j < 4; j++) {
        for (int i = 4; i > 0; i--) {
          if (board[i][j] == ' ' && board[i - 1][j] == c) {
            board[i][j] = c;
            board[i - 1][j] = ' ';
          }
        }
      }
      break;
    case W:
      // #aab => a#ab => aa#b
      // j cannot be 3
      for (int i = 0; i < 5; i++) {
        for (int j = 0; j < 3; j++) {
          if (board[i][j] == ' ' && board[i][j + 1] == c) {
            board[i][j] = c;
            board[i][j + 1] = ' ';
          }
        }
      }
      break;
    case N:
      /*
       * i cannot be 4 a a a # b b b => # => b b b #
       */
      for (int j = 0; j < 4; j++) {
        for (int i = 0; i < 4; i++) {
          if (board[i][j] == ' ' && board[i + 1][j] == c) {
            board[i][j] = c;
            board[i + 1][j] = ' ';

          }
        }
      }
      break;
    }

    parent = pnt;
  }

  public String getSpecialID() {
    // build special
    char[] s = new char[20];
    for (int i = 0; i < 5; i++) {
      for (int j = 0; j < 4; j++) {
        s[i * 4 + j] = board[i][j];
      }
    }

    return new String(s);
  }

  public String getGeneralID() {
    char[] s = new char[20];
    for (int i = 0; i < 5; i++) {
      for (int j = 0; j < 4; j++) {
        char c = board[i][j];
        switch (c) {
        case 'c':
        case 'd':
        case 'e':
        case 'f':
          c = 'b';
          break;
        case 'C':
        case 'D':
        case 'E':
        case 'F':
          c = 'B';
          break;

        case 'h':
        case 'i':
        case 'j':
          c = 'g';
          break;
        default:
          // c = c;
        }
        s[i * 4 + j] = c;
      }
    }
    return new String(s);
    // //build general
    // String s = getSpecialID();
    // String g = s.replaceAll("c", "a");
    // g = g.replaceAll("d", "a");
    // g = g.replaceAll("f", "a");
    // g = g.replaceAll("h", "g");
    // g = g.replaceAll("i", "g");
    // g = g.replaceAll("j", "g");
    //
    // return g;
  }

  @Override
  public String toString() {
    String str = "";

    for (int i = 0; i < 5; i++) {
      for (int j = 0; j < 4; j++) {
        str += board[i][j];
      }
      // str += '\n';
    }
    return str;

  }

  private LinkedList<Encoding> getAllMoves() {
    LinkedList<Encoding> oneMove = new LinkedList<Encoding>();
    Encoding current = new Encoding(getGeneralID(), getSpecialID(), getParent());
   
    for (char c = 'a'; c <= 'j'; c++) {
      for (D d : D.values()) {
        Command cmd = new Command(c, d);
        if (isMovable(cmd)) {
          move(cmd);
          oneMove.addLast(new Encoding(getGeneralID(), getSpecialID(), getParent()));
          decode(current);
        }
      }
    }

    for (char c = 'B'; c <= 'F'; c++) {
      for (D d : D.values()) {
        Command cmd = new Command(c, d);
        if (isMovable(cmd)) {
          move(cmd);
          oneMove.addLast(new Encoding(getGeneralID(), getSpecialID(), getParent()));
          decode(current);
        }
      }
    }

    return oneMove;
  }
 
  private LinkedList<Encoding> getAllMoves2() {
    LinkedList<Encoding> twoMove = new LinkedList<Encoding>();
    Encoding current = new Encoding(getGeneralID(), getSpecialID(), getParent());
    Encoding afterOneMove = null;
    Encoding afterTwoMoves = null;
   
    for (char c = 'a'; c <= 'j'; c++) {
      for (D d : D.values()) {
        Command cmd = new Command(c, d);
        if (isMovable(cmd)) {
          move(cmd);
          afterOneMove = new Encoding(getGeneralID(), getSpecialID(), getParent());
          for (D d2 : D.values()) {
            cmd = new Command(c, d2);
            if (isMovable(cmd)) {
              move(cmd);
              afterTwoMoves = new Encoding(getGeneralID(), getSpecialID(), current.general);
              if (!current.equals(afterTwoMoves)) {
                twoMove.addLast(afterTwoMoves);
              }
              decode(afterOneMove);
            }
          }
          decode(current);
        }
      }
    }

    for (char c = 'B'; c <= 'F'; c++) {
      for (D d : D.values()) {
        Command cmd = new Command(c, d);
        if (isMovable(cmd)) {
          move(cmd);
          afterOneMove = new Encoding(getGeneralID(), getSpecialID(), getParent());
          for (D d2 : D.values()) {
            cmd = new Command(c, d2);
            if (isMovable(cmd)) {
              move(cmd);
              afterTwoMoves = new Encoding(getGeneralID(), getSpecialID(), current.general);
              if (!current.equals(afterTwoMoves)) {
                twoMove.addLast(afterTwoMoves);
              }
              decode(afterOneMove);
            }
          }
          decode(current);
        }
      }
    }

    return twoMove;
  }
  public static boolean search(Game g) {
    g.parent = null;
    deque.clear();
    path.clear();
    visited.clear();

    Encoding ecd = new Encoding(g.getGeneralID(), g.getSpecialID(), g
        .getParent());
    deque.addLast(ecd);
    visited.put(g.getGeneralID(), ecd);

    while (!deque.isEmpty()) {

      Encoding current = deque.pollFirst();
      g.decode(current);

      if (g.escaped()) {
        // System.out.println("Solved!\n");

        while (g.parent != null) {
          path.addFirst(g.toString());
          current = visited.get(g.parent);
          g.decode(current);
        }
       
//        System.out.println(visited.size());
//        Set<String> keySet = visited.keySet();
//        Iterator<String> setIt = keySet.iterator();
//        while (setIt.hasNext()) {
//          System.out.println(setIt.next().hashCode());
//        }
        return true;
      }

      LinkedList<Encoding> oneMoves = g.getAllMoves();
      ListIterator<Encoding> itr = oneMoves.listIterator();
      while (itr.hasNext()) {
        Encoding next = itr.next();
        if (!visited.containsKey(next.general)) {
          deque.addLast(next);
          visited.put(next.general, next);
        }
      }

      LinkedList<Encoding> twoMoves = g.getAllMoves2();
      ListIterator<Encoding> itr2 = twoMoves.listIterator();
      while (itr2.hasNext()) {
        Encoding next = itr2.next();
        if (!visited.containsKey(next.general)) {
          deque.addLast(next);
          visited.put(next.general, next);
        }
      }
    }
   
    // System.out.println("No Solution!\n");
    return false;

  }

  public boolean escaped() {
    return (board[4][1] == 'a' && board[4][2] == 'a');
  }

  // public static void main(String[] args) {
  // Game g = new Game();
  // search(g);
  //
  // int step = 0;
  // while (!path.isEmpty()) {
  // step++;
  // System.out.println("step: " + step);
  // System.out.println(path.removeFirst());
  // }
  //
  // }

}
TOP

Related Classes of Encoding

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.