public int searchTree(final int levelsToTop, int alpha, final int beta) {

    if (board.isDraw()) {
      return scorer.drawValue();
    }

    if (levelsToTop == iterativeDepth) {
      return scorer.staticScore(board);
    }

    final MoveContainer moves = moveContainers[levelsToTop];
    final BoardHashEntry hashEntry = hashTable.get(board.getHashCode());

    prioritizeHashEntry(hashEntry, moves);

    board.makeNullMove();
    board.generateValidMoves(moves);

    if (moves.isEmpty()) {
      return scorer.endOfGameValue(board.isInCheck(), levelsToTop);
    }

    for (int m = 0; m < moves.size(); m++) {

      final Move move = moves.get(m);

      board.makeMove(move);

      final int childScore = -searchTree(levelsToTop + 1, -beta, -alpha);

      board.undoMove();

      if (childScore > alpha) {
        // narrowing alpha beta window
        alpha = childScore;

        movePath.markMove(levelsToTop, iterativeDepth, m);

        if (alpha >= beta) {
          // pruned!
          break;
        }
      }
    }

    unprioritizeHashEntry(hashEntry, moves);

    hashTable.set(
        board.getHashCode(),
        alpha,
        0,
        movePath.getRaw(levelsToTop),
        board.getMoveNumber(),
        BoardHashEntry.ValueBounds.PV);

    return alpha;
  }
  @Override
  public MovePath search(
      final Board board, final int depth, final int startAlpha, final int startBeta) {
    this.iterativeDepth = depth;
    this.board = board;

    final int score = searchTree(0, startAlpha, startBeta);
    movePath.setScore(score);
    movePath.setDepth(iterativeDepth);

    EngineUtil.verifyPV(movePath, board, hashTable);

    return movePath;
  }
 @Override
 public Field move(
     Moveable moveable,
     SetterOfPosition setterOfPosition,
     MovePath movePath,
     ResourceHolder resourceHolder)
     throws FieldsNotConnectedException, MoveNotPossibleException, NotEnoughResourceException {
   if (canMove(moveable, movePath, resourceHolder)) {
     resourceHolder.pay(movePath.cost().asPayment());
     setterOfPosition.setPosition(movePath.getTarget());
     return movePath.getTarget();
   } else if (!resourceHolder.canPay(movePath.cost().asPayment())) {
     throw new NotEnoughResourceException(
         resourceHolder.get(MovePoint.class), movePath.cost().getAmount());
   } else {
     throw new MoveNotPossibleException();
   }
 }
  @Override
  public MovePath iterativeSearch(
      final Board board, final int maxLevel, final int startAlpha, final int startBeta) {
    this.board = board;

    for (int i = 1; i <= maxLevel; i++) {
      search(board, i);
      if (movePath.getEndBoardCondition() == BoardCondition.CHECKMATE) {
        break;
      }
    }
    return movePath;
  }
 @Override
 public boolean canMove(Moveable moveable, MovePath movePath, ResourceHolder resourceHolder) {
   if (moveable == null || movePath == null || resourceHolder == null) {
     return false;
   }
   Moveable testMoveable = moveable.cloneMoveable();
   ResourceHolder testResourceHolder = resourceHolder.cloneResourceHolder();
   boolean canMove = true;
   for (Field field : movePath.getPathFields()) {
     try {
       testMoveable.move(field, testResourceHolder);
     } catch (Exception e) {
       canMove = false;
       break;
     }
   }
   return canMove;
 }