public String solve(int[][] map, int[] mover, Viewer viewer) {
    Node.nodesExpanded = 0;
    String ret = new String();

    PriorityQueue<Node> openList = new PriorityQueue<Node>();

    NodeHashTable closedList = new NodeHashTable(500000);
    NodeHashTable openListHash = new NodeHashTable(50000);

    LinkedList<Node> temp = new LinkedList<Node>();

    Timer timer = new Timer();
    long time = 0;

    State.original_map = makeOriginalMap(map);
    State initialState = new State(reverseMap(map), mover);

    Node initialNode = new Node(null, '-', initialState);

    openList.add(initialNode);
    openListHash.insert(initialNode);

    while (true) {
      if (openList.isEmpty()) {
        System.out.println("No Solution Found\n");
        break;
      }

      Node currentNode = openList.peek();

      if (currentNode.state.equalToMap(map)) {
        System.out.println("Solution Found\n");
        System.out.println("Collisions: " + closedList.getCollisions());
        ret = currentNode.printSolution();

        System.out.println("Price: " + currentNode.state.price);
        System.out.println("Nodes expanded: " + Integer.toString(Node.nodesExpanded));
        System.out.println(
            "Nodes on closedlist: " + Integer.toString(closedList.getNoOfElements()));
        System.out.println("Nodes on Openlist: " + Integer.toString(openList.size()));
        break;
      }
      temp = currentNode.expand();

      while (!temp.isEmpty()) {
        if (!openListHash.contains(temp.getFirst()) && !closedList.contains(temp.getFirst())) {
          openList.add(temp.getFirst());
          openListHash.insert(temp.pollFirst());
        } else {
          temp.removeFirst();
        }
      }
      // System.out.println(Node.nodesExpanded);
      // System.out.println("Price: " + currentNode.state.price);
      // currentNode.state.printMap(currentNode.state.map);
      // System.out.println(closedList.size());
      // System.out.println(openList.size());
      /*
         viewer.updateMap(currentNode.state.map);
         try {
      	Thread.sleep(100);
      } catch (InterruptedException e) {
      	// TODO Auto-generated catch block
      	e.printStackTrace();
      }
        */
      if ((timer.timeSinceStartInNS() - time) > 1000000000) {
        time += 1000000000;
        System.out.println(
            "Time elapsed: "
                + timer.timeSinceStartInSeconds()
                + " seconds \t Nodes expanded: "
                + Node.nodesExpanded
                + "\tOpenlist: "
                + openList.size());
      }

      if (!closedList.contains(currentNode)) closedList.insert(currentNode);
      openList.remove(currentNode);
      openListHash.remove(currentNode);
    }
    return ret;
  }
  public ArrayList<ElementImage> getElementImages(BufferedImage image) {
    Timer timer = new Timer();

    // Reduce image to main data
    // image = Tools.ignoreSurround(image);

    // Erase staves on image
    StavesEraser stavesEraser = new StavesEraser();
    image = stavesEraser.EraseStaves(image);
    timer.step("[Main] Erase staves");

    // Delete staves
    BufferedImage stavesImage = stavesEraser.getStaffs();
    StavesAnalyzer stavesAnalyzer = new StavesAnalyzer(stavesImage);
    timer.step("[Main] Post-computing staves");

    // Get staves for visual
    if (VisualMode.enable) {
      Timer t = new Timer();
      VisualMode.addStaves(stavesImage, image);
      t.step("\t[ColorMode] Complete staves");
      timer.step();
    }

    // Analyze staves
    ArrayList<Staff> staves = stavesAnalyzer.analyzeStaves();
    timer.step("[Main] Analyze staves");

    // Get measure bars
    MeasureFinder measureFinder = new MeasureFinder(staves);
    ArrayList<ElementImage> measureImages = measureFinder.getMeasureImages(image);
    timer.step("[Main] Get measure bars");

    // Delete noise
    image = new NoiseClosing().apply(image);
    image = new NoiseOpening().apply(image);
    timer.step("[Main] Delete noise");

    // Get clefs TODO !
    ClefFinder clefFinder = new ClefFinder(staves, image);
    ArrayList<ElementImage> clefs = clefFinder.findClefs();
    timer.step("[Main] Get clefs");

    // Get rests
    RestFinder restFinder = new RestFinder(staves, image);
    ArrayList<ElementImage> restImages = restFinder.findRests();
    timer.step("[Main] Get rests");

    // Get black notes
    BlackNoteFinder blackNoteFinder = new BlackNoteFinder(staves, image);
    ArrayList<ElementImage> blackNoteImages = blackNoteFinder.getBlackNotesList();
    timer.step("[Main] Get black notes");

    // Get eighth notes
    EighthFinder eighthFinder = new EighthFinder(blackNoteImages, image);
    blackNoteImages = eighthFinder.findEighth();
    timer.step("[Main] Get eighth notes");

    // Get whole notes
    WholeFinder wholeFinder = new WholeFinder(staves, image);
    ArrayList<ElementImage> wholeImages = wholeFinder.findWholes();
    timer.step("[Main] Get whole notes");

    blackNoteImages = mergeElements(blackNoteImages, wholeImages);

    // Add elements and write output (color)
    if (VisualMode.enable) {
      Timer t = new Timer();
      VisualMode.addClefs(clefs);
      VisualMode.addMeasures(measureImages);
      VisualMode.addNotes(blackNoteImages);
      VisualMode.addRests(restImages);
      VisualMode.save();
      t.step("\t[ColorMode] Color elements (Measures, Clefs, Notes, Rests)");
      timer.step();
    }

    ArrayList<ElementImage> noteImages = mergeElements(blackNoteImages, restImages);
    ArrayList<ElementImage> elementImages = mergeElements(noteImages, measureImages);
    addLineCrossing(elementImages);

    return elementImages;
  }