private MoveInfo IterativeDeepeningRoot2(GameState position) {

    // These are used by time control to decide how much time to use
    current_best_score = -SCORE_INFINITY;
    current_search_depth = 0;

    ArimaaMove best_move = null;
    this.initial_position = position;
    GameState initial_gs = position;
    resetStats();
    enable_search();
    GameState new_position = new GameState();

    // do eval preprocessing at the root
    eval.PreProcessRootPosition(position);

    MoveList root_moves = genRootMoves(initial_gs);
    ProcessRootMoves(initial_position, root_moves);

    int offset = setup_search(initial_gs);
    LogFile.message("Score Offset: " + offset);

    // Search until time is expired!
    try {
      for (int depth = initial_search_depth; depth <= max_search_depth; depth++) {
        current_search_depth = depth;
        int iteration_best_score = -SCORE_INFINITY;
        int new_beta = SCORE_INFINITY;

        root_moves.sort();

        // Iterate thru all root moves
        for (ArimaaMove move : root_moves) {

          int temp_score;
          if (move.move_ordering_value >= 30000 ||
            move.move_ordering_value <= -30000) {
            temp_score = move.move_ordering_value;
          } else {

            // Get a score for this move
            new_position.play(move, initial_position);
            temp_score = SearchPosition(new_position, depth,
              iteration_best_score, new_beta);
          }

          move.move_ordering_value = temp_score;

          // We have a new best move
          // Save the best move and score
          if (temp_score > iteration_best_score) {
            iteration_best_score = temp_score;
            current_best_score = temp_score;
            best_move = move;

            // This is a *LOWER* bound on the score!
            // There could be more moves to search!!!
            // Record in hash so PV can be figured out
            hash_table.RecordHash(initial_position.getPositionHash(), depth,
              iteration_best_score,
              HashTable.LOWER_BOUND, move, false);

            // Display search status
            String text = "D:" + Util.Pad(2, depth);
            text += " " + Util.toTimeString(time_control.getElapsedSearchTime());
            text += " " + Util.LeftJustify(8, convertScore(iteration_best_score));
            text += "     " + getPV(initial_position);
            LogFile.message(text);

            // report the results of the move
            MoveInfo mi = new MoveInfo();
            mi.eval_score = current_best_score;
            mi.move_text = gen_turn.getOfficialArimaaNotation(initial_position,
              best_move);
            mi.pv = getPV(position);
            mi.nodes_searched = this.ab_nodes_searched;
            mi.ply = depth;
            mi.search_time_ms = time_control.getElapsedSearchTime();
            thread_mi = mi;

            // If we have a winning mate score we are done!
            if (iteration_best_score >= SCORE_FORCED_WIN) {
              break;
            }
          }

        } // Root move loop
        // Prepare iteration status report
        // Display search status
        String text = "F:" + Util.Pad(2, depth);
        text += " " + Util.toTimeString(time_control.getElapsedSearchTime());
        text += " " + Util.LeftJustify(7, convertScore(current_best_score));
        text += "     ";
        text += " Nodes: " + ab_nodes_searched;
        text += " QNodes: " + q_nodes_searched;
        text += " kNPS: " +
          ab_nodes_searched /
          (time_control.getElapsedSearchTime() + 1);

        LogFile.message(text);

        // If we have a mate score we are done!
        if (isMateScore(iteration_best_score)) {
          break;
        }

      }

    } catch (AbortSearchException ex) {
      LogFile.message("Search Aborted!");
    }

    engine_search_completed = true;
    return thread_mi;
  }