  public Command movePieces(Map map, Point p) {
    PieceIterator it = DragBuffer.getBuffer().getIterator();
    if (!it.hasMoreElements()) {
      return null;
    }
    Point offset = null;
    Command comm = new NullCommand();
    BoundsTracker tracker = new BoundsTracker();
    HashMap<Point,List<GamePiece>> mergeTargets =
      new HashMap<Point,List<GamePiece>>();
    while (it.hasMoreElements()) {
      dragging = it.nextPiece();
      tracker.addPiece(dragging);
      GamePiece[] draggedPieces;
      if (dragging instanceof Stack) {
        int size = ((Stack) dragging).getPieceCount();
        draggedPieces = new GamePiece[size];
        for (int i = 0; i < size; i++) {
          draggedPieces[i] = ((Stack) dragging).getPieceAt(i);
        }
      }
      else {
        draggedPieces = new GamePiece[]{dragging};
      }
      for (int i = 0; i < draggedPieces.length; i++) {
        KeyBuffer.getBuffer().add(draggedPieces[i]);
      }
      if (offset != null) {
        p = new Point(dragging.getPosition().x + offset.x, dragging.getPosition().y + offset.y);
      }
      List<GamePiece> mergeCandidates = mergeTargets.get(p);
      GamePiece mergeWith = null;
      if (mergeCandidates != null) {
        for (int i = 0, n = mergeCandidates.size(); i < n; ++i) {
          GamePiece candidate = mergeCandidates.get(i);
          if (map.getPieceCollection().canMerge(candidate, dragging)) {
            mergeWith = candidate;
            mergeCandidates.set(i, dragging);
            break;
          }
        }
      }
      if (mergeWith == null) {
        mergeWith = map.findAnyPiece(p, dropTargetSelector);
        if (mergeWith == null && !Boolean.TRUE.equals(dragging.getProperty(Properties.IGNORE_GRID))) {
          p = map.snapTo(p);
        }
        if (offset == null) {
          offset = new Point(p.x - dragging.getPosition().x,
                             p.y - dragging.getPosition().y);
        }
        if (mergeWith != null && map.getStackMetrics().isStackingEnabled()) {
          mergeCandidates = new ArrayList<GamePiece>();
          mergeCandidates.add(mergeWith);
          mergeTargets.put(p, mergeCandidates);
        }
      }
      if (mergeWith == null) {
        comm = comm.append(movedPiece(dragging, p));
        comm = comm.append(map.placeAt(dragging, p));
        if (!(dragging instanceof Stack) && !Boolean.TRUE.equals(dragging.getProperty(Properties.NO_STACK))) {
          Stack parent = map.getStackMetrics().createStack(dragging);
          if (parent != null) {
            comm = comm.append(map.placeAt(parent, p));
          }
        }
      }
      else {
        comm = comm.append(movedPiece(dragging, mergeWith.getPosition()));
        comm = comm.append(map.getStackMetrics().merge(mergeWith, dragging));
      }
      if (map.getMoveKey() != null) {
        applyKeyAfterMove(draggedPieces, comm, map.getMoveKey());
      }
      tracker.addPiece(dragging);
    }
    if (GlobalOptions.getInstance().autoReportEnabled()) {
      Command report = createMovementReporter(comm).getReportCommand();
      report.execute();
      comm = comm.append(report);
    }
    tracker.repaint();
    return comm;
  }

  public Command movePieces(Map map, Point p) {
    PieceIterator it = DragBuffer.getBuffer().getIterator();
    if (!it.hasMoreElements()) {
      return null;
    }
    Point offset = null;
    Command comm = new NullCommand();
    BoundsTracker tracker = new BoundsTracker();
    HashMap<Point,List<GamePiece>> mergeTargets =
      new HashMap<Point,List<GamePiece>>();
    while (it.hasMoreElements()) {
      dragging = it.nextPiece();
      tracker.addPiece(dragging);
      GamePiece[] draggedPieces;
      if (dragging instanceof Stack) {
        int size = ((Stack) dragging).getPieceCount();
        draggedPieces = new GamePiece[size];
        for (int i = 0; i < size; i++) {
          draggedPieces[i] = ((Stack) dragging).getPieceAt(i);
        }
      }
      else {
        draggedPieces = new GamePiece[]{dragging};
      }
      for (int i = 0; i < draggedPieces.length; i++) {
        KeyBuffer.getBuffer().add(draggedPieces[i]);
      }
      if (offset != null) {
        p = new Point(dragging.getPosition().x + offset.x, dragging.getPosition().y + offset.y);
      }
      List<GamePiece> mergeCandidates = mergeTargets.get(p);
      GamePiece mergeWith = null;
      if (mergeCandidates != null) {
        for (int i = 0, n = mergeCandidates.size(); i < n; ++i) {
          GamePiece candidate = mergeCandidates.get(i);
          if (map.getPieceCollection().canMerge(candidate, dragging)) {
            mergeWith = candidate;
            mergeCandidates.set(i, dragging);
            break;
          }
        }
      }
      if (mergeWith == null) {
        mergeWith = map.findAnyPiece(p, dropTargetSelector);
        if (mergeWith == null && !Boolean.TRUE.equals(dragging.getProperty(Properties.IGNORE_GRID))) {
          p = map.snapTo(p);
        }
        if (offset == null) {
          offset = new Point(p.x - dragging.getPosition().x,
                             p.y - dragging.getPosition().y);
        }
        if (mergeWith != null && map.getStackMetrics().isStackingEnabled()) {
          mergeCandidates = new ArrayList<GamePiece>();
          mergeCandidates.add(dragging);
          mergeCandidates.add(mergeWith);
          mergeTargets.put(p, mergeCandidates);
        }
      }
      if (mergeWith == null) {
        comm = comm.append(movedPiece(dragging, p));
        comm = comm.append(map.placeAt(dragging, p));
        if (!(dragging instanceof Stack) && !Boolean.TRUE.equals(dragging.getProperty(Properties.NO_STACK))) {
          Stack parent = map.getStackMetrics().createStack(dragging);
          if (parent != null) {
            comm = comm.append(map.placeAt(parent, p));
          }
        }
      }
      else {
        comm = comm.append(movedPiece(dragging, mergeWith.getPosition()));
        comm = comm.append(map.getStackMetrics().merge(mergeWith, dragging));
      }
      if (map.getMoveKey() != null) {
        applyKeyAfterMove(draggedPieces, comm, map.getMoveKey());
      }
      tracker.addPiece(dragging);
    }
    if (GlobalOptions.getInstance().autoReportEnabled()) {
      Command report = createMovementReporter(comm).getReportCommand();
      report.execute();
      comm = comm.append(report);
    }
    tracker.repaint();
    return comm;
  }
