/**
  * Method for figureing out if a node is fully expanded yet or terminal
  *
  * @param n
  * @return
  */
 public Node treePolicy(Node n) {
   // Node current = n;
   while (!n.isTerminal()) { // Non terminal
     if (!n.getExpanded()) { // Not fully expanded
       return n.expand();
     } else {
       n = bestChild(n);
     }
   }
   return n;
 }
 public static void main(String[] args) {
   Node root = new Node(1);
   root.left = new Node(2);
   root.right = new Node(3);
   root.left.left = new Node(4);
   root.left.right = new Node(5);
   root.left.right.left = new Node(7);
   root.left.left.left = new Node(6);
   root.right.right = new Node(8);
   root.right.right.left = new Node(9);
   print(root);
 }
  /**
   * Method which simulates the game from a given node/state
   *
   * @param n
   * @return
   */
  private int defaultPolicy(Node n) {
    Game currentState = n.getState().copy();
    int d = 0;

    while (!isStateTerminal(currentState) && d != depth) {
      MOVE localCurrentDirection = null;
      if (d == 0
          || currentState.isJunction(
              currentState.getPacmanCurrentNodeIndex())) { // is in a junction
        MOVE[] moves = currentState.getPossibleMoves(currentState.getPacmanCurrentNodeIndex());
        int i = rng.nextInt(moves.length);
        localCurrentDirection = moves[i];
        while (moves[i] == localCurrentDirection.opposite()) {
          i = rng.nextInt(moves.length);
        }
        localCurrentDirection = moves[i];
      }

      currentState.advanceGame(
          localCurrentDirection,
          controller.Controller.getGhostController().getMove(currentState, 0));
      d++;
    }

    if (isStateTerminal(currentState)) {
      return -1;
    }
    return currentState.getScore();
  }
  /**
   * Method for finding the best child to explore
   *
   * @param n
   * @return
   */
  private Node bestChild(Node n) {
    Node best = null;
    double bestValue = Double.NEGATIVE_INFINITY;
    for (Node c : n.getChildren().values()) {
      double uctValue =
          (c.getTotalValue() / c.getVisits())
              + COEFFICIENT * Math.sqrt((2 * Math.log(n.getVisits())) / (c.getVisits()));

      uctValue +=
          rng.nextDouble()
              * 1e-6; // In case of same uctValue, this random will remove bias towards first
                      // checked child
      if (bestValue < uctValue) {
        bestValue = uctValue;
        best = c;
      }
    }
    // TESTING
    if (best == null) {
      System.out.println("BUGS");
    }
    //
    return best;
  }
 /**
  * Backup method
  *
  * @param next
  * @param score
  */
 private void backup(Node next, int score) {
   Node current = next;
   while (current != null) {
     current = current.visit(score);
   }
 }