public Move get1stMove(
      Game game, CheckersSetup setup, checkersSetup.Board board, ArrayList<Move> choices) {
    // BasicCheckersAIFunctions.delayCompALittle();

    // start fresh after every turn:
    viewedPos = null;
    // TESTING
    numPosRecorded = 0;
    // END TESTING

    numJumpsAlreadyDoneThisTurn = 0;

    BasicCheckersAIFunctions.printAllAIPlayerOptions(setup, isDarkPlayer);

    desiredTurn = getBestMoveBasedOnSearchOfDepthN(setup, isDarkPlayer, this.depth);

    ArrayList<Move> desiredMoves = desiredTurn.getMovesFor1Turn();

    BasicCheckersAIFunctions.PrintDesiredMoves(desiredMoves);

    if (desiredMoves.get(0).isJump()) {
      numJumpsAlreadyDoneThisTurn++;
    }

    // TESTING (Display)
    System.out.println("Number of positions analysed for AI with Tree: " + numPosRecorded);
    if (viewedPos != null) {
      System.out.println("Height of tree: " + viewedPos.getHeightOfTree());
    }
    System.out.println();
    // END TESTING

    return desiredMoves.get(0);
  }
  public Move getNextJumps(
      CheckersSetup setup, checkersSetup.Board board, ArrayList<Move> multiJumpChoices) {
    // BasicCheckersAIFunctions.delayCompALittle();

    // get the next move in the desired multi jump choice:
    Move move = desiredTurn.getMovesFor1Turn().get(numJumpsAlreadyDoneThisTurn);

    // Testing
    BasicCheckersAIFunctions.doSanityCheckMakeSureCompMoveIsRealMove(
        desiredTurn.getMovesFor1Turn().get(numJumpsAlreadyDoneThisTurn), multiJumpChoices);
    // Testing

    numJumpsAlreadyDoneThisTurn++;

    return move;
  }
  // pre: this position MUST be at the beginning of a turn.
  // post: returns the utility of the checkers position for whoever's turn it happens to be.
  public double getEstUtilityOfPosition(
      CheckersSetup setup, boolean isDarkPlayerCurrentTurn, int curdepth) {
    double ret;

    // this key identifies the setup of the board and who's turn it is in 3 numbers:
    int key[] = AnalysedPosition.makeKey(setup, isDarkPlayerCurrentTurn);

    // ret pos!!!!
    // Check to see if the position has already been analysed
    Double alreadyFoundUtility = getUtilityIfAlreadyFound(key, curdepth);

    if (alreadyFoundUtility != null) {
      return new Double(alreadyFoundUtility).doubleValue();
    }
    // end check

    // Find the utility from scratch and recursion:
    if (curdepth == 0) {
      if (isDarkPlayerCurrentTurn) {
        return utilityFunc.getEstimatedUtilityForDarkPlayer(setup, isDarkPlayerCurrentTurn);
      } else {
        return -utilityFunc.getEstimatedUtilityForDarkPlayer(setup, isDarkPlayerCurrentTurn);
      }
    } else {
      ArrayList<checkersSetup.Turn> turnChoices =
          BasicCheckersAIFunctions.getAllPossiblePlayerOptionsFor1Turn(
              setup, isDarkPlayerCurrentTurn);

      if (turnChoices.size() == 0) {
        // horrible position:
        return -BasicCheckersAIFunctions.DARK_WIN_UTILITY;
      }

      setup.playTurn(turnChoices.get(0));
      // op = OPPONENT!
      double opWorstPosUtil =
          getEstUtilityOfPosition(setup, !isDarkPlayerCurrentTurn, curdepth - 1);
      setup.reverseTurn(turnChoices.get(0));

      double currOpPosUtil;

      for (int i = 1; i < turnChoices.size(); i++) {
        setup.playTurn(turnChoices.get(i));

        currOpPosUtil = getEstUtilityOfPosition(setup, !isDarkPlayerCurrentTurn, curdepth - 1);

        if (currOpPosUtil < opWorstPosUtil) {
          opWorstPosUtil = currOpPosUtil;
        }

        setup.reverseTurn(turnChoices.get(i));
      }
      ret = -opWorstPosUtil;
      // End finding utility from scratch

      // TESTING SLOW
      if (debug) {
        sanityTestKeyBeforeAndAfter(setup, isDarkPlayerCurrentTurn, key);
      }
      // END TESTING

      // ADD analysed position to tree:
      addPositionToListOfAnalysedPositions(key, isDarkPlayerCurrentTurn, curdepth, ret);

      return ret;
    }
  }
  // pre: this position MUST be at the beginning of a turn
  // post: returns the best move assuming that the opponent plays perfectly.
  // This search only looks (depth) moves ahead and is very inefficient.
  public checkersSetup.Turn getBestMoveBasedOnSearchOfDepthN(
      CheckersSetup setup, boolean isDarkPlayer, int curdepth) {

    int numAlternativelyGoodMoves = 0;

    if (curdepth > 0) {

      ArrayList<checkersSetup.Turn> turnChoices =
          BasicCheckersAIFunctions.getAllPossiblePlayerOptionsFor1Turn(setup, isDarkPlayer);

      favTurn = new Turn[turnChoices.size()];

      favTurn[0] = turnChoices.get(0);

      setup.playTurn(turnChoices.get(0));
      // note: op = OPPONENT!
      double opWorstPosUtil = getEstUtilityOfPosition(setup, !isDarkPlayer, curdepth - 1);
      setup.reverseTurn(turnChoices.get(0));

      double opPosUtil;

      for (int i = 1; i < turnChoices.size(); i++) {
        setup.playTurn(turnChoices.get(i));

        opPosUtil = getEstUtilityOfPosition(setup, !isDarkPlayer, curdepth - 1);

        if (opPosUtil < opWorstPosUtil) {
          opWorstPosUtil = opPosUtil;
          favTurn[0] = turnChoices.get(i);
          numAlternativelyGoodMoves = 0;

        } else if (opPosUtil == opWorstPosUtil) {
          numAlternativelyGoodMoves++;
          favTurn[numAlternativelyGoodMoves] = turnChoices.get(i);
        }

        setup.reverseTurn(turnChoices.get(i));
      }
      if (opWorstPosUtil <= -BasicCheckersAIFunctions.DARK_WIN_UTILITY) {
        System.out.println("YOU ARE F****D!!");
      }

      // TODO
      // opUtilityOfLastMove = -opWorstPosUtil;
    } else {
      System.out.println("Error: the depth should be bigger than 0!");
      System.exit(1);
      // stub to stop java from complaining:
      favTurn = new Turn[0];
    }

    // if AI is NOT deterministic AND
    //	there's several choices that seem similarly good, the AI randomly choses a move
    if (numAlternativelyGoodMoves > 0 && this.deterministic == false) {
      System.out.println(
          "aitree is randomly choosing beetween "
              + (numAlternativelyGoodMoves + 1)
              + " ways to do his turn");
      numFavMoves = numAlternativelyGoodMoves + 1;
      return favTurn[(int) (Math.random() * (numAlternativelyGoodMoves + 1))];
    } else {
      numFavMoves = 1;
      return favTurn[0];
    }
  }