import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;

public class Main {
    public static void main(String[] args) {

        /*
        Change the values for the first 3 variables, which are your servant's deck,
        the number of cards in the pool and the card combination you need
         */
        String servant_deck = "bbaaq";
        int cards_pool = 15;
        String hand_combination = "ba";


        int buster_deck = 0;
        int arts_deck = 0;
        int quick_deck = 0;
        int buster_hand = 0;
        int arts_hand = 0;
        int quick_hand = 0;

        char[] deck_array = servant_deck.toLowerCase().toCharArray();
        Arrays.sort(deck_array);
        for (int i = 0; i < deck_array.length; i++) {
            switch (deck_array[i]) {
                case 'b':
                    buster_deck++;
                    break;
                case 'a':
                    arts_deck++;
                    break;
                case 'q':
                    quick_deck++;
                    break;
            }
        }

        char[] hand_combination_array = hand_combination.toLowerCase().toCharArray();
        for (int i = 0; i < hand_combination_array.length; i++) {
            switch (hand_combination_array[i]) {
                case 'b':
                    buster_hand++;
                    break;
                case 'a':
                    arts_hand++;
                    break;
                case 'q':
                    quick_hand++;
                    break;
            }
        }

        int cards_remaining = Math.max(cards_pool - 5, 5);

        int hand = hand_combination_array.length;

        float max_combinations = factorial(cards_pool) / (factorial(cards_pool - 5) * factorial(5));
        float buster_combinations = factorial(buster_deck) / (factorial(buster_deck - buster_hand) * factorial(buster_hand));
        float arts_combinations = factorial(arts_deck) / (factorial(arts_deck - arts_hand) * factorial(arts_hand));
        float quick_combinations = factorial(quick_deck) / (factorial(quick_deck - quick_hand) * factorial(quick_hand));

        float draw_combinations = buster_combinations * arts_combinations * quick_combinations * (float) (factorial(cards_remaining) / (factorial(cards_remaining - 5 + hand) * factorial(5 - hand))) / max_combinations;

        System.out.println("ONLY " + hand_combination.toUpperCase() + " out of a " + servant_deck.toUpperCase() + " deck: ");
        System.out.println(draw_combinations * 100 + "%");


        //removing different cards
        StringBuilder deck_no_diffs = new StringBuilder();
        boolean[] present_in_target = new boolean[256];

        for (char c : hand_combination.toCharArray()) {
            present_in_target[c] = true;
        }

        for (char c : deck_array) {

            if (present_in_target[c]) {
                deck_no_diffs.append(c);
            }
        }
        //removing duplicates
        List<Character> deck_list = new ArrayList<>();

        for (char c : deck_no_diffs.toString().toCharArray()) {
            deck_list.add(c);
        }

        for (int i = 0; i < hand_combination_array.length; i++) {
            for (int j = 0; j < deck_list.size(); j++)
                if (deck_list.get(j) == hand_combination_array[i]) {
                    deck_list.remove(j);
                    break;
                }
        }

        HashSet<String> combinations = new HashSet<>();
        for (int i = 0; i < deck_list.size(); i++) {
            combinations.add("" + deck_list.get(i) + hand_combination);
        }
        String comb_deck = "";
        for (int i = 0; i < deck_list.size(); i++) {
            comb_deck += deck_list.get(i);
        }
        combinations.add(comb_deck + hand_combination);
        combinations.add(hand_combination);

        System.out.println("AT LEAST " + hand_combination.toUpperCase() + " out of a " + servant_deck.toUpperCase() + " deck: ");
        System.out.println(hypergeometricCalculation(combinations, deck_array, cards_pool) * 100 + "%");
    }

    public static long factorial(int n) {
        long fact = 1;
        for (int i = 2; i <= n; i++) {
            fact = fact * i;
        }
        return fact;
    }

    public static char[] removeCards(char arr[]) {
        Arrays.sort(arr);
        char[] temp = new char[arr.length];
        int j = 0;
        for (int i = 0; i < arr.length - 1; i++)
            if (arr[i] != arr[i + 1])
                temp[j++] = arr[i];
        temp[j++] = arr[arr.length - 1];
        return temp;
    }


    public static float hypergeometricCalculation(HashSet<String> set, char[] deck_array, int cards_pool) {
        int buster_deck;
        int arts_deck;
        int quick_deck;
        int buster_hand;
        int arts_hand;
        int quick_hand;
        float total = 0;

        List<String> combinations = new ArrayList<>();
        combinations.addAll(set);

        for (int i = 0; i < combinations.size(); i++) {
            buster_deck = 0;
            arts_deck = 0;
            quick_deck = 0;
            buster_hand = 0;
            arts_hand = 0;
            quick_hand = 0;
            int cards_remaining = Math.max(cards_pool, 5);

            for (int j = 0; j < deck_array.length; j++) {
                switch (deck_array[j]) {
                    case 'b':
                        buster_deck++;
                        break;
                    case 'a':
                        arts_deck++;
                        break;
                    case 'q':
                        quick_deck++;
                        break;
                }
            }
            char[] hand_combination_array = combinations.get(i).toLowerCase().toCharArray();
            Arrays.sort(hand_combination_array);
            for (int j = 0; j < hand_combination_array.length; j++) {
                switch (hand_combination_array[j]) {
                    case 'b':
                        buster_hand++;
                        break;
                    case 'a':
                        arts_hand++;
                        break;
                    case 'q':
                        quick_hand++;
                        break;
                }
            }


            for (char j : removeCards(hand_combination_array)) {
                switch (j) {
                    case 'b':
                        cards_remaining -= buster_deck;
                        break;
                    case 'a':
                        cards_remaining -= arts_deck;
                        break;
                    case 'q':
                        cards_remaining -= quick_deck;
                        break;
                }
            }

            int hand = hand_combination_array.length;

            float max_combinations = factorial(cards_pool) / (factorial(cards_pool - 5) * factorial(5));
            float buster_combinations = factorial(buster_deck) / (factorial(buster_deck - buster_hand) * factorial(buster_hand));
            float arts_combinations = factorial(arts_deck) / (factorial(arts_deck - arts_hand) * factorial(arts_hand));
            float quick_combinations = factorial(quick_deck) / (factorial(quick_deck - quick_hand) * factorial(quick_hand));
            float draw_combinations = (buster_combinations * arts_combinations * quick_combinations * (float) (factorial(cards_remaining) / (factorial(cards_remaining - 5 + hand) * factorial(5 - hand))) / max_combinations);
            total = total + draw_combinations;


        }

        return total;
    }
}






 
by