Machine Coding Problem

Deck of Cards / Poker

maco60macoAllgamesrule-enginehand-evaluationsortingkickers-tie-breaking
Commonly Asked By:SonyMicrosoftZynga

Functional Scope (In-Scope)

  • Complete Playing structures: Encapsulate Rank, Suit, and immutable Card representations cleanly.
  • Fisher-Yates Shuffling: Distribute and randomize card array indexes uniformly.
  • Poker Hand Classification: Evaluate 5-card combinations into ranked bands (Royal Flush, Straight Flush, Four of a Kind, etc.).
  • Tie-Breaking Kickers logic: Resolve matching hands by evaluating outstanding high card kickers.

Explicit Boundaries (Out-of-Scope)

  • No Pot betting loops: Excludes tracking chips balances or dealer bets calculations.
  • No Multi-player Lobby: Bypasses live networks connections or server lobbies.

Clean reference designs demonstrating modular shufflers in Java and Python:

// ─── JAVA BLUEPRINT ──────────────────────────────────────────────────────────
import java.util.*;
import java.util.stream.Collectors;

enum Suit { SPADES, HEARTS, DIAMONDS, CLUBS }

enum Rank {
    TWO(2), THREE(3), FOUR(4), FIVE(5), SIX(6), SEVEN(7), EIGHT(8), NINE(9), TEN(10),
    JACK(11), QUEEN(12), KING(13), ACE(14);

    private final int value;
    Rank(int val) { this.value = val; }
    public int getValue() { return value; }
}

enum HandRank {
    HIGH_CARD(1), ONE_PAIR(2), TWO_PAIR(3), THREE_OF_A_KIND(4), STRAIGHT(5),
    FLUSH(6), FULL_HOUSE(7), FOUR_OF_A_KIND(8), STRAIGHT_FLUSH(9), ROYAL_FLUSH(10);

    private final int score;
    HandRank(int score) { this.score = score; }
    public int getScore() { return score; }
}

class Card implements Comparable<Card> {
    private final Rank rank;
    private final Suit suit;

    public Card(Rank rank, Suit suit) {
        this.rank = rank;
        this.suit = suit;
    }

    public Rank getRank() { return rank; }
    public Suit getSuit() { return suit; }

    @Override
    public int compareTo(Card other) {
        return Integer.compare(this.rank.getValue(), other.rank.getValue());
    }

    @Override
    public String toString() { return rank + " of " + suit; }
}

class Deck {
    private final List<Card> cards = new ArrayList<>();

    public Deck() { reset(); }

    public synchronized void reset() {
        cards.clear();
        for (Suit s : Suit.values()) {
            for (Rank r : Rank.values()) {
                cards.add(new Card(r, s));
            }
        }
    }

    public synchronized void shuffle() {
        Random rand = new Random();
        for (int i = cards.size() - 1; i > 0; i--) {
            int j = rand.nextInt(i + 1);
            Card temp = cards.get(i);
            cards.set(i, cards.get(j));
            cards.set(j, temp);
        }
    }

    public synchronized List<Card> deal(int count) {
        List<Card> hand = new ArrayList<>();
        for (int i = 0; i < count; i++) {
            if (!cards.isEmpty()) {
                hand.add(cards.remove(cards.size() - 1));
            }
        }
        return hand;
    }
}

class PokerHand implements Comparable<PokerHand> {
    private final List<Card> cards;
    private HandRank handRank;
    private List<Integer> tieBreakers; // Sorted representation for kicker checks

    public PokerHand(List<Card> cards) {
        if (cards.size() != 5) throw new IllegalArgumentException("Must have exactly 5 cards.");
        this.cards = new ArrayList<>(cards);
        Collections.sort(this.cards);
        evaluate();
    }

    public HandRank getHandRank() { return handRank; }
    public List<Card> getCards() { return cards; }

    private void evaluate() {
        boolean isFlush = cards.stream().map(Card::getSuit).distinct().count() == 1;
        boolean isStraight = checkStraight();

        Map<Integer, Long> freq = cards.stream()
                .collect(Collectors.groupingBy(c -> c.getRank().getValue(), Collectors.counting()));

        List<Map.Entry<Integer, Long>> sortedFreq = freq.entrySet().stream()
                .sorted((e1, e2) -> {
                    int c = Long.compare(e2.getValue(), e1.getValue());
                    if (c != 0) return c;
                    return Integer.compare(e2.getKey(), e1.getKey());
                }).collect(Collectors.toList());

        tieBreakers = sortedFreq.stream().map(Map.Entry::getKey).collect(Collectors.toList());

        // Straight Adjustment for Ace-low (A, 2, 3, 4, 5)
        if (isStraight && cards.get(4).getRank() == Rank.ACE && cards.get(0).getRank() == Rank.TWO) {
            tieBreakers = Arrays.asList(5, 4, 3, 2, 1); // Rank the 5 high straight
        }

        if (isFlush && isStraight) {
            if (cards.get(0).getRank() == Rank.TEN) {
                handRank = HandRank.ROYAL_FLUSH;
            } else {
                handRank = HandRank.STRAIGHT_FLUSH;
            }
        } else if (sortedFreq.get(0).getValue() == 4) {
            handRank = HandRank.FOUR_OF_A_KIND;
        } else if (sortedFreq.get(0).getValue() == 3 && sortedFreq.get(1).getValue() == 2) {
            handRank = HandRank.FULL_HOUSE;
        } else if (isFlush) {
            handRank = HandRank.FLUSH;
            // Overwrite tieBreakers to sort card ranks descending
            tieBreakers = cards.stream().map(c -> c.getRank().getValue())
                    .sorted(Comparator.reverseOrder()).collect(Collectors.toList());
        } else if (isStraight) {
            handRank = HandRank.STRAIGHT;
        } else if (sortedFreq.get(0).getValue() == 3) {
            handRank = HandRank.THREE_OF_A_KIND;
        } else if (sortedFreq.get(0).getValue() == 2 && sortedFreq.get(1).getValue() == 2) {
            handRank = HandRank.TWO_PAIR;
        } else if (sortedFreq.get(0).getValue() == 2) {
            handRank = HandRank.ONE_PAIR;
        } else {
            handRank = HandRank.HIGH_CARD;
            tieBreakers = cards.stream().map(c -> c.getRank().getValue())
                    .sorted(Comparator.reverseOrder()).collect(Collectors.toList());
        }
    }

    private boolean checkStraight() {
        // Ace-low straight edge case
        if (cards.get(0).getRank() == Rank.TWO && cards.get(1).getRank() == Rank.THREE &&
            cards.get(2).getRank() == Rank.FOUR && cards.get(3).getRank() == Rank.FIVE &&
            cards.get(4).getRank() == Rank.ACE) {
            return true;
        }
        for (int i = 0; i < 4; i++) {
            if (cards.get(i).getRank().getValue() + 1 != cards.get(i + 1).getRank().getValue()) {
                return false;
            }
        }
        return true;
    }

    @Override
    public int compareTo(PokerHand other) {
        int rankCompare = Integer.compare(this.handRank.getScore(), other.handRank.getScore());
        if (rankCompare != 0) return rankCompare;

        // Kickers check
        for (int i = 0; i < this.tieBreakers.size(); i++) {
            int comp = Integer.compare(this.tieBreakers.get(i), other.tieBreakers.get(i));
            if (comp != 0) return comp;
        }
        return 0;
    }

    @Override
    public String toString() {
        return handRank + " [" + cards.stream().map(Card::toString).collect(Collectors.joining(", ")) + "]";
    }
}

public class Main {
    public static void main(String[] args) {
        Deck deck = new Deck();
        deck.shuffle();

        List<Card> player1Cards = Arrays.asList(
                new Card(Rank.ACE, Suit.HEARTS), new Card(Rank.KING, Suit.HEARTS),
                new Card(Rank.QUEEN, Suit.HEARTS), new Card(Rank.JACK, Suit.HEARTS),
                new Card(Rank.TEN, Suit.HEARTS)
        );

        List<Card> player2Cards = Arrays.asList(
                new Card(Rank.ACE, Suit.SPADES), new Card(Rank.ACE, Suit.CLUBS),
                new Card(Rank.KING, Suit.DIAMONDS), new Card(Rank.KING, Suit.SPADES),
                new Card(Rank.TEN, Suit.CLUBS)
        );

        PokerHand hand1 = new PokerHand(player1Cards);
        PokerHand hand2 = new PokerHand(player2Cards);

        System.out.println("Player 1 Hand: " + hand1);
        System.out.println("Player 2 Hand: " + hand2);

        int comparison = hand1.compareTo(hand2);
        if (comparison > 0) {
            System.out.println("Player 1 Wins!");
        } else if (comparison < 0) {
            System.out.println("Player 2 Wins!");
        } else {
            System.out.println("Split Pot (Tie)!");
        }
    }
}