Sorting a hand of poker

I am trying to sort a hand of poker based on their rank and suits, but it is still not fully sorted, here are the code, Note that the suits is sorted, but not the ranks

My hand before the sort:

1: KING of HEARTS 2: FOUR of DIAMONDS 3: SEVEN of CLUBS 4: KING of CLUBS 5: THREE of HEARTS 6: FIVE of DIAMONDS 7: TWO of CLUBS 8: KING of DIAMONDS 9: FOUR of SPADES 10: THREE of DIAMONDS

After the sort:

1: FOUR of SPADES 2: KING of HEARTS 3: THREE of HEARTS 4: TWO of CLUBS 5: KING of CLUBS 6: SEVEN of CLUBS 7: KING of DIAMONDS 8: FIVE of DIAMONDS 9: FOUR of DIAMONDS 10: THREE of DIAMONDS

Card object

public int compareTo(Card that)
{
    if(this.suit.ordinal() > that.suit.ordinal())
    {
        return 1;
    }
    if(this.suit.ordinal() < that.suit.ordinal()) 
    {
        return -1;
    }       

    int rank1 = (this.rank.ordinal() + 11) % 13; //A>K
    int rank2 = (that.rank.ordinal() + 11) % 13;

    if(rank1 > rank2) return 1;
    if(rank1 < rank2) return -1;
    return 0;
}

Player Object

public void tryTest()
{
    Card temp = new Card();
    for(int i=0;i<countCard;i++)
    {
        for(int j=0;j<countCard;j++)
        {
            if(playerHand[i].compareTo(playerHand[j]) > 0)
            {
                temp=this.playerHand[j];
                this.playerHand[j] = this.playerHand[i];
                this.playerHand[i] = temp;
            }
        }
    }
}

Enum Ranks

ACE,
TWO,
THREE,
FOUR,
FIVE,
SIX,
SEVEN,
EIGHT,
NINE,
TEN,
JACK,
QUEEN,
KING;

Enum Suits

DIAMONDS,
CLUBS,
HEARTS,
SPADES;

Upvotes: 4

Views: 1789

Answers (2)

Mr. Polywhirl
Mr. Polywhirl

Reputation: 48600

If you want to sort a poker hand, you can sort hands like this.

As long as your Rank enum is ordered from lowest to highest e.g. TWO to ACE then your comparison of the ordinal() values should work.

public static void sortHandBySuitThenRank(List<Card> hand) {
    hand.sort(Comparator.comparing(Card::getSuit).thenComparing(Card::getRank));
}

public static void sortHandByRankThenSuit(List<Card> hand) {
    hand.sort(Comparator.comparing(Card::getRank).thenComparing(Card::getSuit));
}

Here is a demo of the code in action:

package org.example.cards;

import java.util.*;
import java.util.stream.Collectors;

import lombok.*;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class Poker {
    public static void main(String[] args) {
        PokerGame game = new PokerGame();

        List.of("Alice", "Bob", "Charlie", "David")
                .forEach(name -> game.addPlayer(new Player(name)));

        game.shuffleDeck();
        game.dealInitialHand();
        game.showHands();
        game.evaluateHands();
        game.showDiscardPile();

        game.determineWinner().ifPresent(winner -> {
            log.info("Winner: {}", winner.getName());
            log.info("Hand: {} ({})", winner.showHand(), winner.evaluateHand().getDescription());
        });
    }

    static class PokerGame {
        private static final int HAND_SIZE = 5;

        private final List<Player> players;
        private final Deck<Card> deck;
        private final List<Card> discardPile;

        public PokerGame() {
            this.players = new ArrayList<>();
            this.deck = new PokerDeck();
            this.discardPile = new ArrayList<>();
        }

        public void addPlayer(Player player) {
            players.add(player);
        }

        public void shuffleDeck() {
            deck.shuffle();
        }

        public void dealInitialHand() {
            for (int i = 0; i < HAND_SIZE; i++) {
                for (Player player : players) {
                    player.drawCard(deck.deal());
                }
            }
        }

        public void discardCard(Card card) {
            discardPile.add(card);
        }

        public void showHands() {
            for (Player player : players) {
                player.sortHand();
                log.debug("{}'s hand: {}", player.getName(), player.showHand());
            }
        }

        public void showDiscardPile() {
            log.debug("Discard pile: {}", PokerUtils.formatHand(discardPile));
        }

        public Optional<Player> determineWinner() {
            return players.stream().max(Comparator.comparing(Player::getHandRank));
        }

        public void evaluateHands() {
            for (Player player : players) {
                log.info("{}'s hand: {} ({})", player.getName(), player.showHand(), player.evaluateHand().getDescription());
                List<Card> cardsToDiscard = player.decideCardsToDiscard();
                log.info("Discarding: {}", PokerUtils.formatHand(cardsToDiscard));
                int cardsToDraw = cardsToDiscard.size();
                for (Card card : cardsToDiscard) {
                    player.discardCard(card);
                    discardCard(card);
                }
                for (int i = 0; i < cardsToDraw; i++) {
                    player.drawCard(deck.deal());
                }
                log.info("New hand: {} ({})", player.showHand(), player.evaluateHand().getDescription());
            }
        }
    }

    static class HandEvaluator {
        public static HandType evaluateHand(List<Card> hand) {
            // Sort the hand by rank
            PokerUtils.sortHandByRankThenSuit(hand);

            // Check for various hand types, starting from the strongest
            if (isRoyalFlush(hand)) return HandType.ROYAL_FLUSH;
            else if (isStraightFlush(hand)) return HandType.STRAIGHT_FLUSH;
            else if (isFourOfAKind(hand)) return HandType.FOUR_OF_A_KIND;
            else if (isFullHouse(hand)) return HandType.FULL_HOUSE;
            else if (isFlush(hand)) return HandType.FLUSH;
            else if (isStraight(hand)) return HandType.STRAIGHT;
            else if (isThreeOfAKind(hand)) return HandType.THREE_OF_A_KIND;
            else if (isTwoPair(hand)) return HandType.TWO_PAIR;
            else if (isOnePair(hand)) return HandType.ONE_PAIR;
            return HandType.HIGH_CARD;
        }

        private static boolean isRoyalFlush(List<Card> hand) {
            return isStraightFlush(hand) && hand.getLast().getRank() == Rank.ACE;
        }

        private static boolean isStraightFlush(List<Card> hand) {
            return isStraight(hand) && isFlush(hand);
        }

        private static boolean isFourOfAKind(List<Card> hand) {
            return getRankCounts(hand).containsValue(4);
        }

        private static boolean isFullHouse(List<Card> hand) {
            Map<Rank, Integer> rankCounts = getRankCounts(hand);
            return rankCounts.containsValue(3) && rankCounts.containsValue(2);
        }

        private static boolean isFlush(List<Card> hand) {
            final Suit firstSuit = hand.getFirst().getSuit();
            return hand.stream().allMatch(card -> card.getSuit() == firstSuit);
        }

        private static boolean isStraight(List<Card> hand) {
            for (int i = 0; i < hand.size() - 1; i++) {
                if (hand.get(i).getRank().ordinal() + 1 != hand.get(i + 1).getRank().ordinal()) {
                    return false;
                }
            }
            return true;
        }

        private static boolean isThreeOfAKind(List<Card> hand) {
            Map<Rank, Integer> rankCounts = getRankCounts(hand);
            return rankCounts.containsValue(3) && !rankCounts.containsValue(2);
        }

        private static boolean isTwoPair(List<Card> hand) {
            return getRankCounts(hand).values().stream().filter(count -> count == 2).count() == 2;
        }

        private static boolean isOnePair(List<Card> hand) {
            Map<Rank, Integer> rankCounts = getRankCounts(hand);
            return rankCounts.containsValue(2) && !rankCounts.containsValue(3);
        }

        private static Map<Rank, Integer> getRankCounts(List<Card> hand) {
            return hand.stream().collect(Collectors.groupingBy(Card::getRank, Collectors.summingInt(e -> 1)));
        }
    }

    @Getter
    static class Player {
        private final String name;
        private final List<Card> hand;

        public Player(String name) {
            this.name = name;
            hand = new ArrayList<>();
        }

        public void drawCard(Card card) {
            hand.add(card);
        }

        public void discardCard(Card card) {
            hand.remove(card);
        }

        public void sortHand() {
            PokerUtils.sortHandBySuitThenRank(hand);
        }

        public String showHand() {
            return PokerUtils.formatHand(hand);
        }

        public HandType evaluateHand() {
            return HandEvaluator.evaluateHand(hand);
        }

        public List<Card> decideCardsToDiscard() {
            return PokerUtils.decideCardsToDiscard(hand).stream()
                    .limit(3)
                    .toList();
        }

        public int getHandRank() {
            return this.evaluateHand().ordinal();
        }
    }

    @NoArgsConstructor(access = AccessLevel.PRIVATE)
    static class PokerUtils {
        public static String formatHand(List<Card> hand) {
            return hand.stream().map(Card::toString).collect(Collectors.joining(" "));
        }

        public static void sortHandBySuitThenRank(List<Card> hand) {
            hand.sort(Comparator.comparing(Card::getSuit).thenComparing(Card::getRank));
        }

        public static void sortHandByRankThenSuit(List<Card> hand) {
            hand.sort(Comparator.comparing(Card::getRank).thenComparing(Card::getSuit));
        }

        public static void shuffle(List<Card> cards) {
            Random rand = new Random();
            for (int currIndex = cards.size() - 1; currIndex > 0; currIndex--) {
                int randIndex = rand.nextInt(currIndex + 1);
                Card randCard = cards.get(randIndex);
                cards.set(randIndex, cards.get(currIndex));
                cards.set(currIndex, randCard);
            }
        }

        public static List<Card> decideCardsToDiscard(List<Card> hand) {
            HandType handType = HandEvaluator.evaluateHand(hand);
            Map<Rank, Long> rankCounts = hand.stream()
                    .collect(Collectors.groupingBy(Card::getRank, Collectors.counting()));

            return switch (handType) {
                case HIGH_CARD -> {
                    final Card highestCard = hand.stream().max(Comparator.comparing(Card::getRank)).orElse(null);
                    yield hand.stream()
                            .filter(card -> !card.equals(highestCard))
                            .toList();
                    // Discard all but the highest card
                }
                case ONE_PAIR -> {
                    final Rank pairRank = rankCounts.entrySet().stream().filter(entry -> entry.getValue() == 2).map(Map.Entry::getKey).findFirst().orElse(null);
                    // Discard the three cards that are not part of the pair
                    yield hand.stream()
                            .filter(card -> !card.getRank().equals(pairRank))
                            .toList();

                }
                case TWO_PAIR -> {
                    final List<Rank> pairRanks = rankCounts.entrySet().stream()
                            .filter(entry -> entry.getValue() == 2)
                            .map(Map.Entry::getKey)
                            .toList();
                    // Discard the fifth card that is not part of the pairs
                    yield hand.stream()
                            .filter(card -> !pairRanks.contains(card.getRank()))
                            .toList();

                }
                case THREE_OF_A_KIND -> {
                    final Rank threeOfAKindRank = rankCounts.entrySet().stream()
                            .filter(entry -> entry.getValue() == 3)
                            .map(Map.Entry::getKey)
                            .findFirst()
                            .orElse(null);
                    // Discard the two cards that are not part of the three of a kind
                    yield hand.stream()
                            .filter(card -> !card.getRank().equals(threeOfAKindRank))
                            .toList();
                }
                case STRAIGHT, FLUSH, FULL_HOUSE, FOUR_OF_A_KIND, STRAIGHT_FLUSH, ROYAL_FLUSH ->
                        Collections.emptyList();
            };
        }
    }

    static class PokerDeck implements Deck<Card> {
        private final List<Card> cards;

        public PokerDeck() {
            cards = new ArrayList<>();
            for (Suit suit : Suit.values()) {
                for (Rank rank : Rank.values()) {
                    cards.add(new PokerCard(suit, rank));
                }
            }
        }

        @Override
        public void shuffle() {
            PokerUtils.shuffle(cards);
        }

        @Override
        public Card deal() {
            return cards.removeFirst();
        }
    }

    @Value
    static class PokerCard implements Card {
        Suit suit;
        Rank rank;

        @Override
        public String toString() {
            return String.format("%s%s", rank.getSymbol(), suit.getSymbol());
        }
    }

    @RequiredArgsConstructor
    @Getter
    enum Suit {
        HEARTS("♥"),
        DIAMONDS("♦"),
        CLUBS("♣"),
        SPADES("♠");

        private final String symbol;
    }

    @RequiredArgsConstructor
    @Getter
    enum Rank {
        TWO("2", 2),
        THREE("3", 3),
        FOUR("4", 4),
        FIVE("5", 5),
        SIX("6", 6),
        SEVEN("7", 7),
        EIGHT("8", 8),
        NINE("9", 9),
        TEN("T", 10),
        JACK("J", 11),
        QUEEN("Q", 12),
        KING("K", 13),
        ACE("A", 14);

        private final String symbol;
        private final int value;
    }

    @RequiredArgsConstructor
    @Getter
    enum HandType {
        HIGH_CARD("High Card"),
        ONE_PAIR("One Pair"),
        TWO_PAIR("Two Pair"),
        THREE_OF_A_KIND("Three of a Kind"),
        STRAIGHT("Straight"),
        FLUSH("Flush"),
        FULL_HOUSE("Full House"),
        FOUR_OF_A_KIND("Four of a Kind"),
        STRAIGHT_FLUSH("Straight Flush"),
        ROYAL_FLUSH("Royal Flush");

        private final String description;
    }

    interface Deck<T extends Card> {
        void shuffle();

        T deal();
    }

    interface Card {
        Suit getSuit();

        Rank getRank();
    }
}

Output:

DEBUG [main] o.e.c.Poker$PokerGame: Alice's hand: T♥ A♥ 4♦ A♦ 5♠
DEBUG [main] o.e.c.Poker$PokerGame: Bob's hand: Q♥ 3♦ T♦ J♦ J♠
DEBUG [main] o.e.c.Poker$PokerGame: Charlie's hand: 9♦ 4♣ 5♣ 7♠ 9♠
DEBUG [main] o.e.c.Poker$PokerGame: David's hand: 8♥ 5♦ 3♠ 6♠ A♠

INFO  [main] o.e.c.Poker$PokerGame: Alice's hand: T♥ A♥ 4♦ A♦ 5♠ (One Pair)
INFO  [main] o.e.c.Poker$PokerGame: Discarding: 4♦ 5♠ T♥
INFO  [main] o.e.c.Poker$PokerGame: New hand: A♥ A♦ 5♥ T♣ K♥ (One Pair)

INFO  [main] o.e.c.Poker$PokerGame: Bob's hand: Q♥ 3♦ T♦ J♦ J♠ (One Pair)
INFO  [main] o.e.c.Poker$PokerGame: Discarding: 3♦ T♦ Q♥
INFO  [main] o.e.c.Poker$PokerGame: New hand: J♦ J♠ 6♦ J♥ 2♣ (Three of a Kind)

INFO  [main] o.e.c.Poker$PokerGame: Charlie's hand: 9♦ 4♣ 5♣ 7♠ 9♠ (One Pair)
INFO  [main] o.e.c.Poker$PokerGame: Discarding: 4♣ 5♣ 7♠
INFO  [main] o.e.c.Poker$PokerGame: New hand: 9♦ 9♠ 4♥ J♣ Q♣ (One Pair)

INFO  [main] o.e.c.Poker$PokerGame: David's hand: 8♥ 5♦ 3♠ 6♠ A♠ (High Card)
INFO  [main] o.e.c.Poker$PokerGame: Discarding: 3♠ 5♦ 6♠
INFO  [main] o.e.c.Poker$PokerGame: New hand: 8♥ A♠ 2♠ 2♥ 7♣ (One Pair)

DEBUG [main] o.e.c.Poker$PokerGame: Discard pile: 4♦ 5♠ T♥ 3♦ T♦ Q♥ 4♣ 5♣ 7♠ 3♠ 5♦ 6♠

INFO  [main] o.e.c.Poker: Winner: Bob
INFO  [main] o.e.c.Poker: Hand: 2♣ 6♦ J♥ J♦ J♠ (Three of a Kind)

Upvotes: 1

vandale
vandale

Reputation: 3650

Look into some standard sorting algorithms. The code you have right now compares everything to everything else, and then swaps them if one is greater than the other. However this could lead to you swapping backwards:

1, 2

2>1 therefore swap

2,1

The closest algorithm to what you have would be Bubble Sort, which uses slightly different indexes:

for(int i=0;i<countCard;i++)
{
    for(int j=0;j<countCard -1;j++)
    {
        if(playerHand[j].compareTo(playerHand[j+1]) > 0)
        {
            temp=this.playerHand[j+1];
            this.playerHand[j+1]= this.playerHand[j];
            this.playerHand[j] = temp;
        }
    }
}

Upvotes: 3

Related Questions