Design poker-like hand comparison with custom ranking
Company: Rippling
Role: Software Engineer
Category: Software Engineering Fundamentals
Difficulty: hard
Interview Round: Technical Screen
## Problem
Design an object-oriented solution to compare two players' hands in a card game similar to poker.
You are given two hands (each a fixed number of cards; assume 5 unless you state otherwise). Each hand has a **hand type** (e.g., three-of-a-kind, pair, full house, etc.). Your design must compare hands, decide a winner (or a tie), and remain **extensible** so that new hand types and new ruleset orderings can be added without rewriting the comparison engine.
The deliverable is a **design plus reference implementation sketch**: sketch the class/module breakdown (key classes, interfaces, responsibilities), describe the comparison logic (including tie-break rules and how you represent a hand's "comparison key"), and explain how the extension mechanism in Part 2 fits in.
### Constraints & Assumptions
- **Hand size**: assume 5 cards per hand unless you state otherwise; do not hard-code the size into the core logic.
- **Card ranks** are totally ordered (e.g., `2..A`, with ace high by default). Suits exist and may be referenced if needed.
- A single standard deck: no two cards share the same (rank, suit) identity, but rank collisions across suits are expected (that is what produces pairs, trips, etc.).
- **Type precedence is per-ruleset**, not global: the same registered types may rank differently in different games.
- The deliverable is a design plus reference implementation sketch, **not** a hardened production library; favor clarity and extensibility over micro-optimization.
### Clarifying Questions to Ask
- Is the hand size always 5, or should the design support other fixed sizes (e.g., 7-card games)?
- Does `add_type` receive only a `matches`-style detector, or may I also supply a per-type scoring/tie-break function? (This materially changes the design.)
- Is a single standard deck assumed, or can hands contain duplicate card identities or wild cards / jokers?
- How should ace be treated — strictly high, or should an ace-low straight (the `A-2-3-4-5` "wheel") be supported?
- On an exact tie (identical strength), is the result a tie/split, or does the ruleset ever break ties by suit?
- Is `evaluation_orders` guaranteed to cover every hand a player could hold (i.e., is there always a catch-all type), or must I handle "matches no type"?
---
### Part 1 — Comparison and ranking
Implement the core comparison so that, given two hands, you can return which one is larger or whether they tie.
1. **Compare two hands** and return which one is larger (or if they tie).
2. If the two hands have **different types**, the winner is decided by the **type ranking** (e.g., full house > three-of-a-kind, etc.).
3. If the two hands have the **same type**, break ties by comparing the relevant card ranks in order.
- Example: for three-of-a-kind, `8883x` should beat `7773x` because the 8-trip outranks the 7-trip (assume remaining kickers are compared after the trips if needed).
```hint Reduce comparison to one comparable value
Cross-type comparison and same-type tie-breaking feel like two different problems. Can a hand's full strength instead be reduced to **one comparable value**, so that both cases collapse into a single comparison? Think about what that value would have to encode.
```
```hint Making same-type tie-breaks generic
You could write bespoke tie-break logic for each type (pair, two-pair, trips, full house, quads), but that's a lot of near-duplicate code. Is there **one rule over a hand's repeated ranks** that produces the right ordering for all of them at once — including the `8883x` vs `7773x` case? What property of the matched cards does every count-based tie-break actually key on?
```
#### What This Part Should Cover
```premium-lock What This Part Should Cover
```
### Part 2 — Extension mechanism
Make the type system pluggable so new hand types and new rulesets do not require touching the engine.
4. Implement `add_type(type_id: string, matches_function: function)` that registers a new hand type and how to detect it.
5. The main `evaluate` function takes an extra parameter `evaluation_orders: array<string>` that specifies the **type precedence** (strongest to weakest) used when comparing hands, allowing different games/rulesets to be expressed as data rather than code.
6. Explain how `add_type(...)` and `evaluation_orders` work **together** to classify a hand and place it in the ranking.
```hint Separate "what exists" from "what ranks higher"
There are two distinct facts here: *which types exist and how to recognize them*, versus *which type beats which in this particular game*. Which of these belongs in the registry, and which should be supplied per call so a new game needs no code change? Keeping them apart is the point of the exercise.
```
```hint Is a boolean enough to break ties?
A boolean `matches_function` can tell you a hand *is* a three-of-a-kind — but is that enough to decide a winner **between two** three-of-a-kinds? If you think it isn't, say so explicitly, and consider what the registration API would need to give you to close the gap.
```
```hint When more than one predicate fires
A loosely written predicate (e.g. "contains a triple") can match a full house *and* a three-of-a-kind at the same time. If two registered types both claim the same hand, what makes your classification **deterministic** rather than arbitrary? Think about how `evaluation_orders` and the matching loop could interact to settle it.
```
#### What This Part Should Cover
```premium-lock What This Part Should Cover
```
### What a Strong Answer Covers
```premium-lock What a Strong Answer Covers
```
### Follow-up Questions
- How does your design change for **Texas Hold'em**, where each player forms the best 5-card hand out of 7 available cards?
- A hand satisfies **multiple** type predicates at once (e.g., a permissive "contains a triple"). How does your design guarantee a deterministic, correct classification?
- How would you support **suit-based tie-breaking** in a ruleset that uses it, without disturbing rulesets that don't?
- How would you handle **wild cards / jokers** that can stand in for any rank?
Quick Answer: This question evaluates object-oriented design, rule-based comparison logic, and algorithmic reasoning for ranking and tie-breaking of card hands, including skills in extensibility and comparison-key representation.