Hand Rank Descriptor — Reversing the 2+2 Lookup

# Hand Rank Descriptor — Reversing the 2+2 Lookup

The 2+2 hand ranking algorithm assigns a unique value (1–7462) to every 5-card poker hand using prime number multiplication + hash table lookup. To reverse this — enumerate all hands for a given rank — use a **hand descriptor**.

---

## Hand Descriptor Schema

Each of the 7,462 distinct hand values maps to a compact descriptor:

| Hand Type | Descriptor | Example | Example Hand |
|-----------|-----------|---------|-------------|
| Straight Flush | `(Type, HighRank)` | `(SF, 10)` | T♥ J♥ Q♥ K♥ A♥ |
| Four of a Kind | `(Type, QuadRank, Kicker)` | `(FOAK, 7, 5)` | 7♣ 7♦ 7♥ 7♠ 5♣ |
| Full House | `(Type, TripRank, PairRank)` | `(FH, 10, 8)` | T♣ T♦ T♥ 8♣ 8♦ |
| Flush | `(Type, Ranks)` | `(FL, (A,9,7,5,2))` | A♠ 9♠ 7♠ 5♠ 2♠ |
| Straight | `(Type, HighRank)` | `(ST, 8)` | 4♦ 5♣ 6♥ 7♦ 8♠ |
| Three of a Kind | `(Type, TripRank, Kickers)` | `(TOAK, Q, (9,4))` | Q♣ Q♦ Q♥ 9♠ 4♦ |
| Two Pair | `(Type, HiPair, LoPair, Kicker)` | `(TP, J, 6, K)` | J♣ J♦ 6♥ 6♣ K♠ |
| One Pair | `(Type, PairRank, Kickers)` | `(OP, 2, (A,K,9))` | 2♣ 2♦ A♠ K♥ 9♦ |
| High Card | `(Type, Ranks)` | `(HC, (A,J,9,6,3))` | A♣ J♦ 9♥ 6♠ 3♣ |

---

## Generation: From Descriptor to All Matching Hands

### Straight Flush / Straight
4 suits × 1 combination each → **4 hands** (SF) or 4×10 = **40 hands** (straight, incl. royal)

### Four of a Kind
All 4 suits for quad + 4 choices for kicker → **4 hands per rank**

### Full House
C(4,3) = 4 suit combos for trips × C(4,2) = 6 for pair → **24 hands per rank**

### Flush / High Card
All 5 cards same suit, 4 suit choices → **4 hands per rank**

### Three of a Kind
C(4,3) = 4 for trips × 4 × 4 for kickers → **64 hands per rank**

### Two Pair
C(4,2) = 6 for each pair × 4 for kicker → **144 hands per rank**

### One Pair
C(4,2) = 6 for pair × 4 × 4 × 4 for kickers → **384 hands per rank**

---

## Implementation Strategy

```python
# Build reverse lookup table (one-time cost)
reverse_lookup = [None] * 7463

for combo in all_5card_combinations(deck):
    rank = evaluate(combo)           # 2+2 lookup → 1..7462
    descriptor = make_descriptor(combo)
    if reverse_lookup[rank] is None:
        reverse_lookup[rank] = descriptor

# Enumerate hands for any rank R:
descriptor = reverse_lookup[R]
hands = generate_from_descriptor(descriptor)
```

This avoids brute-forcing all 2.6M five-card combinations per query and uses combinatorial generation instead.

id: 1353ef49602b40328db3528b39f888b5
parent_id: 1246bbc3bb4948fc8329079b84b4ae3d
created_time: 2026-05-31T10:59:27.907Z
updated_time: 2026-05-31T10:59:27.907Z
is_conflict: 0
latitude: 0.00000000
longitude: 0.00000000
altitude: 0.0000
author: 
source_url: 
is_todo: 0
todo_due: 0
todo_completed: 0
source: joplin-desktop
source_application: net.cozic.joplin-desktop
application_data: 
order: 1780225167907
user_created_time: 2026-05-31T10:59:27.907Z
user_updated_time: 2026-05-31T10:59:27.907Z
encryption_cipher_text: 
encryption_applied: 0
markup_language: 1
is_shared: 0
share_id: 
conflict_original_id: 
master_key_id: 
user_data: 
deleted_time: 0
type_: 1