@Override
  public void chooseMove() {
    long startTime = System.nanoTime();
    Move bestMove = null;
    double bestScore = 0;

    // We need to initialize alpha and beta -infinity and + infinity respectively.
    double alpha = Double.NEGATIVE_INFINITY;
    double beta = Double.POSITIVE_INFINITY;

    int maxDepth;
    for (maxDepth = 1; (System.nanoTime() - startTime) <= maxTime; maxDepth++) {
      List<Move> legalMoves = GameState2P.getLegalMoves(state, index);
      bestScore = 0;

      for (Move m : legalMoves) {
        // If we run out time we break out.
        if ((System.nanoTime() - startTime) >= maxTime) {
          break;
        }

        GameState2P next = m.doMove(state);
        double score = getMinScoreAlphaBeta(next, maxDepth, alpha, beta);

        if (bestMove == null || score >= bestScore) {
          bestMove = m;
          bestScore = score;
        }
      }
    }

    System.out.println("Depth: " + maxDepth + " trans score: " + bestScore);
    GameState2P newState = bestMove.doMove(state);
    game.doMove(index, newState);
  }
  /*
   * Consider all possible moves by our opponent
   */
  private double getMinScoreAlphaBeta(
      final GameState2P state, int depth, double alpha, double beta) {
    double res = Double.POSITIVE_INFINITY;
    double score;

    // We try to get the current opponent state from the transposition table.
    TranspositionEntry entry = minTable.getEntryFromGameState(state);

    // Get the opponent's moves.
    final List<Move> opponentMoves = GameState2P.getLegalMoves(state, indexOpponent);

    // Check if current opp state existed before in table and that the depth is less than the depth
    // stored in the entry. We need to check the states as well, as it is possible that two state
    // has the same hashcode.
    // Then return the previous best minimax score for this state.
    if (null != entry && state.equals(entry.getGameState2P()) && depth <= entry.getDepth()) {
      res = entry.getMinimax();
    }

    if (depth == 0 || state.isGameOver()) {
      res = state.evaluateState(index);
    } else {

      Comparator<Move> comparator =
          new Comparator<Move>() {
            @Override
            public int compare(Move move1, Move move2) {
              // Check if the current opponent moves exist in the transposition table. If it doesn't
              // set minimax
              // to positive infinity, in order words the worst result for the opponent.
              int lhs =
                  (null != minTable.getEntryFromGameState(move1.doMove(state)))
                      ? (int) minTable.getEntryFromGameState(move1.doMove(state)).getMinimax()
                      : (int) Double.POSITIVE_INFINITY;
              int rhs =
                  (null != minTable.getEntryFromGameState(move2.doMove(state)))
                      ? (int) minTable.getEntryFromGameState(move2.doMove(state)).getMinimax()
                      : (int) Double.POSITIVE_INFINITY;

              // If right is lower swap.
              if (lhs > rhs) {
                return 1;
              } else if (lhs == rhs) {
                return 0;
              } else {
                return -1;
              }
            }
          };

      // For the minimax score we need to sort the opponent's moves by their scores lowest first, in
      // order to optimise
      // the search.
      Collections.sort(opponentMoves, comparator);

      // The opponent will go through their moves and try to get the best minimax score for them.
      // Once a promising child move is found store it with the minimax value in the transposition
      // table.
      for (Move move : opponentMoves) {
        GameState2P next = move.doMove(state);
        score = getMaxScoreAlphaBeta(next, depth - 1, alpha, beta);
        res = Math.min(res, score);
        beta = Math.min(beta, score);
        if (beta <= alpha) {
          //
          minTable.addEntry(next, res, depth);
          break;
        }
      }
    }
    return res;
  }
  @Override
  public void doMove(
      String s,
      Table table,
      WhitePlayer whitePlayer,
      BlackPlayer blackPlayer,
      ArrayList<SaveState> saveStateArrayList,
      SaveState previousState,
      TextArea status)
      throws IOException, Mate, CheckIsOpen, CastlingDone, ChangePawn {

    // BufferedReader reader=new BufferedReader(new InputStreamReader(System.in));
    while (!s.equals("exit")) {
      saveStateArrayList.add(new SaveState(table, whitePlayer, blackPlayer));
      previousState = saveStateArrayList.get(saveStateArrayList.size() - 1);
      boolean nextToBlack = false;

      System.out.println("White player moves: " + s);
      // s = reader.readLine();

      try {

        new WhiteMove(s).move(table, whitePlayer, blackPlayer);

        Cell myKingCell =
            table.getOpponentKingCell(
                WHITE, whitePlayer.getChessItemsMap(), blackPlayer.getChessItemsMap(), table);

        // is Check to my king?
        if (Move.isInAllItemsOfAvailableCellListBlack(
            myKingCell, blackPlayer.getChessItemsMap(), table)) {
          System.out.println("Black player will Announce you Check and Mate");
          // Undo Last Move
          previousState.undoHere(table, whitePlayer, blackPlayer);
          // Undo Last Move
          table.toString();
          throw new CheckIsOpen();
        }
        // table.toString();
        nextToBlack = true;
      } catch (PlayerSameChessItem playerSameChessItem) {
        System.out.println("Source & Target are the same");

      } catch (EmptySourceCell emptySourceCell) {
        System.out.println("Source Cell is empty");

      } catch (InvalidSource invalidSource) {
        System.out.println("Source is invalid");
      } catch (NoCell noCell) {

      } catch (InvalidMoveString invalidMoveString) {
        System.out.println("String is invalid");

      } catch (InvalidMove invalidMove) {

      } catch (NoAvailableCells noAvailableCells) {
        System.out.println("No Available Cells");
      } /*catch (Check check) {

        }*/
      if (nextToBlack) {
        // is Check or not?
        Cell kingCell =
            table.getOpponentKingCell(
                BLACK, whitePlayer.getChessItemsMap(), blackPlayer.getChessItemsMap(), table);
        if (Move.isInAllItemsOfAvailableCellListWhite(
            kingCell, whitePlayer.getChessItemsMap(), table)) {
          try {
            throw new Check();
          } catch (Check check) {
            System.out.println("Check to Black Army");
            // is Mate or not?
            ArrayList<BlackTestMove> blackTestMoves = new ArrayList<>();
            saveStateArrayList.add(new SaveState(table, whitePlayer, blackPlayer));
            previousState = saveStateArrayList.get(saveStateArrayList.size() - 1);

            for (SortedMap.Entry<String, ChessItem> item :
                blackPlayer.getChessItemsMap().entrySet()) {
              if (item.getValue() instanceof BlackKing) {
                try {
                  for (Cell cell :
                      new BlackKingMoves(table.getCellByString(item.getKey()), table, true)
                          .getMoves()) {
                    blackTestMoves.add(
                        new BlackTestMove(
                            item.getValue().toString(), item.getKey(), cell.toString()));
                  }
                } catch (NoAvailableCells noAvailableCells) {

                }
              }
              if (item.getValue() instanceof BlackQueen) {
                try {
                  for (Cell cell :
                      new BlackQueenMoves(table.getCellByString(item.getKey()), table).getMoves()) {
                    blackTestMoves.add(
                        new BlackTestMove(
                            item.getValue().toString(), item.getKey(), cell.toString()));
                  }
                } catch (NoAvailableCells noAvailableCells) {

                }
              }
              if (item.getValue() instanceof BlackBishop) {
                try {
                  for (Cell cell :
                      new BlackBishopMoves(table.getCellByString(item.getKey()), table)
                          .getMoves()) {
                    blackTestMoves.add(
                        new BlackTestMove(
                            item.getValue().toString(), item.getKey(), cell.toString()));
                  }
                } catch (NoAvailableCells noAvailableCells) {

                }
              }
              if (item.getValue() instanceof BlackKnight) {
                try {
                  for (Cell cell :
                      new BlackKnightMoves(table.getCellByString(item.getKey()), table)
                          .getMoves()) {
                    blackTestMoves.add(
                        new BlackTestMove(
                            item.getValue().toString(), item.getKey(), cell.toString()));
                  }
                } catch (NoAvailableCells noAvailableCells) {

                }
              }
              if (item.getValue() instanceof BlackPawn) {
                try {
                  for (Cell cell :
                      new BlackPawnMoves(table.getCellByString(item.getKey()), table).getMoves()) {
                    blackTestMoves.add(
                        new BlackTestMove(
                            item.getValue().toString(), item.getKey(), cell.toString()));
                  }
                } catch (NoAvailableCells noAvailableCells) {

                }
              }
              if (item.getValue() instanceof BlackRook) {
                try {
                  for (Cell cell :
                      new BlackRookMoves(table.getCellByString(item.getKey()), table).getMoves()) {
                    blackTestMoves.add(
                        new BlackTestMove(
                            item.getValue().toString(), item.getKey(), cell.toString()));
                  }
                } catch (NoAvailableCells noAvailableCells) {

                }
              }
            }

            ArrayList<BlackTestMove> availableMoves = new ArrayList<>();
            try {

              if (blackTestMoves.size() > 0) {
                for (BlackTestMove move : blackTestMoves) {
                  if (move.getChessItem().equals(Black.KING)) {
                    new BlackMove(move.getSource(), move.getDestination())
                        .move(table, whitePlayer, blackPlayer);

                    if (!(Move.isInAllItemsOfAvailableCellListWhite(
                        table.getCellByString(move.getDestination()),
                        whitePlayer.getChessItemsMap(),
                        table))) {
                      availableMoves.add(move);
                    }
                    previousState.undoHere(table, whitePlayer, blackPlayer);
                  } else {
                    new BlackMove(move.getSource(), move.getDestination())
                        .move(table, whitePlayer, blackPlayer);

                    if (!(Move.isInAllItemsOfAvailableCellListWhite(
                        kingCell, whitePlayer.getChessItemsMap(), table))) {
                      availableMoves.add(move);
                    }
                    previousState.undoHere(table, whitePlayer, blackPlayer);
                  }
                }
                if (availableMoves.size() == 0) {
                  throw new Mate(BLACK);
                } else {
                  status.appendText("Available moves for black player are:\n");
                  System.out.println("Available moves for black player are:");
                  for (BlackTestMove move : availableMoves) {
                    status.appendText(move.toString() + "\n");
                    System.out.println(move.toString());
                  }
                }
              }
            } catch (NoAvailableCells noAvailableCells) {

            } catch (NoCell noCell) {

            } catch (InvalidMove invalidMove) {

            } catch (PlayerSameChessItem playerSameChessItem) {

            } catch (EmptySourceCell emptySourceCell) {

            } catch (InvalidSource invalidSource) {

            }
          }
        }
        saveState = new SaveState(table, whitePlayer, blackPlayer);
        table.toString();
        break;
      }
    }
  }