0

Currently working on a card dealing project. I want a method that deals poker hands. It should randomly pick n(5) elements and return these in a collection(?). Also desired to use Random class to achieve randomness - school orders...

public ArrayList<PlayingCard> dealHand(int n){
        ArrayList<PlayingCard> hand = new ArrayList<>();
        Random random = new Random();
        for (int i = 0; i < n; i++) {
            PlayingCard card = this.deck.get(random.nextInt(this.deck.size()));
            if (!hand.contains(card)){
                hand.add(card);
            }
        }
        return hand;
    }

Assignment: "Create a "dealHand (int n)" method in the DeckOfCards class that randomly picks n cards from the deck and returns them in a collection. "N" is a number between 1 and 52 that is submitted as a parameter to the assign function. This feature can be used, for example, to draw n random cards from the deck. You again choose which class / interface from the Java library you use as the return type of the method."

How do I make this method fulfill the requirements mentioned earlier, using streams, filters and what not (functional programming and lambda)?

Thanks

1 Answers1

0

EDIT following the comment "Just tested, and one card got pulled twice... Need to pick 5 different, random cards."

One approach would be to use streams, and generate random numbers, more specifically primitive streams.

There are some ways of generating random numbers, I think that it looks cleaner it using ThreadLocalRandom, for more information about the difference of generating random numbers with Random.java and ThreadLocalRandom, there's this article talking about it

differente between Random class and ThreadLocalRandom class

For example, in your case you could use the ints() method, that has different signatures.

  1. we can bound the random numbers to be in the range of the deck list, so we can randomly get any card inside of it, so we would need a list with these indexes
  2. we can generate an unlimited stream of pseudorandom int values
  3. filter them to only those who are in the range of the deck indexes list
  4. discard the duplicate ones
  5. limit them to the n bound that you're going to pass wo dealHand(int n)

It fits this signature of ints()

ThreadLocalRandom.java

 /**
 * Returns an effectively unlimited stream of pseudorandom {@code
 * int} values, each conforming to the given origin (inclusive) and bound
 * (exclusive).
 *
 * @implNote This method is implemented to be equivalent to {@code
 * ints(Long.MAX_VALUE, randomNumberOrigin, randomNumberBound)}.
 *
 * @param randomNumberOrigin the origin (inclusive) of each random value
 * @param randomNumberBound the bound (exclusive) of each random value
 * @return a stream of pseudorandom {@code int} values,
 *         each with the given origin (inclusive) and bound (exclusive)
 * @throws IllegalArgumentException if {@code randomNumberOrigin}
 *         is greater than or equal to {@code randomNumberBound}
 * @since 1.8
 */
public IntStream ints(int randomNumberOrigin, int randomNumberBound) {
    ...
}

Implementing it on your example

    public static List<PlayingCard> dealHand(int n){
        // list with indexes of the deck list
        List<Integer> deckIndexes = deck
                .stream()
                .map(deck::indexOf)
                .collect(toList());

        return ThreadLocalRandom
                .current()
                .ints(0, deck.size())
                .filter(deckIndexes::contains)
                .distinct()
                .limit(n)
                .mapToObj(deck::get)
                .collect(toList());
    }
  • .map(deck::indexOf) maps the deck list into a list with all of its indexes

  • .ints() returns a IntStream with the values bounded by 0 and deck.size() and unlimited size

  • filter(deckIndexes::contains) filters the list to a list with values in the bound of the deckIndexes list values

  • distinct() returns a stream of non duplicate values of the just filtered stream

  • mapToObj() is a method from IntStream.java that recieves a IntFunction functional interface, that needs to be a lambda that takes an int as parameter and a R of return type

  • limit(n) truncate the stream to be no longer than n size

  • .collect() is the terminal operation that returns a Collection(of List in this case)

Syou said you wanted lambdas, you can substitute the method references used above to

        .map(card -> deck.indexOf(card))

        ...

        .filter(randomInt -> deckIndexes.contains(randomInt))

        ...

        .mapToObj(deckIndex -> deck.get(deckIndex))
Pedro Luiz
  • 321
  • 1
  • 5
  • Does this solution work if a card gets picked twice? Meaning only 4 cards, or less, would be returned. – Erik Skjellevik Mar 19 '22 at 15:39
  • Just tested, and one card got pulled twice... Need to pick 5 different, random cards. – Erik Skjellevik Mar 19 '22 at 17:51
  • Oh, that example is a little bit tricky so I didn't notice it, since sometimes that you test it, it works as expected, sorry. I edited my answer with this requirement... can you have a look ? – Pedro Luiz Mar 20 '22 at 02:33
  • How do I implement the toList method? Create a method toList(), or change it out with ArrayList :: new? – Erik Skjellevik Mar 20 '22 at 14:31
  • that toList() method is a static method from Collectors.java class, you can use it with 1. Collector.toList() or 2. toList(), but in the second one you would have to import it as a static method on you program, something like: import static java.util.stream.Collectors.toList; Did that solution work for you now ? – Pedro Luiz Mar 21 '22 at 01:32