FAILURE MAP
← Case archive

FA-82191 / Poker hand evaluation / Open access

Full house named by highest rank instead of trips · case 01

Threes full of kings is announced as "Kings full of Threes".

Verified by executionVariant 1 · 8 checks per implementationDownload source bundle ↓JSON ↗

ROOT CAUSE

The two ranks are ordered by rank value rather than by which rank forms the trips.

VERIFIED REPAIR

Name the trips rank first, then the pair rank.

Unsuccessful approach: Swapping the group order names the pair first for every full house.

Case contract

Input: five distinct cards. Return the showdown announcement: "Royal Flush", "Straight Flush, <top> high", "Four <plural>", "Full House, <trips plural> full of <pair plural>", "Flush, <top> high", "Straight, <top> high" (wheel is Five high), "Three <plural>", "Two Pair, <higher plural> and <lower plural>", "Pair of <plural>", "High Card, <top>". Rank names Two..Ace; plurals add "s" except Deuces and Sixes.

Why this case matters

Dealer announcements and hand histories must name the same hand the evaluator paid.

1 / The failure

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
import collections
N = 1
observations = []
def solve(x):
    R='23456789TJQKA'
    NAME={2:'Two',3:'Three',4:'Four',5:'Five',6:'Six',7:'Seven',8:'Eight',9:'Nine',10:'Ten',11:'Jack',12:'Queen',13:'King',14:'Ace'}
    def pl(v): return {2:'Deuces',6:'Sixes'}.get(v, NAME[v]+'s')
    vals=sorted((R.index(c[0])+2 for c in x), reverse=True)
    cnt=collections.Counter(vals)
    groups=sorted(cnt, key=lambda v:(cnt[v],v), reverse=True)
    shape=sorted(cnt.values(), reverse=True)
    flush=len({c[1] for c in x})==1
    wheel=vals==[14,5,4,3,2]
    straight=len(cnt)==5 and (vals[0]-vals[4]==4 or wheel)
    top=5 if wheel else vals[0]
    if straight and flush: return 'Royal Flush' if top==14 else 'Straight Flush, %s high' % NAME[top]
    if shape==[4,1]: return 'Four %s' % pl(groups[0])
    if shape==[3,2]: return 'Full House, %s full of %s' % (pl(max(cnt)), pl(min(cnt)))
    if flush: return 'Flush, %s high' % NAME[vals[0]]
    if straight: return 'Straight, %s high' % NAME[top]
    if shape==[3,1,1]: return 'Three %s' % pl(groups[0])
    if shape==[2,2,1]: return 'Two Pair, %s and %s' % (pl(groups[0]), pl(groups[1]))
    if shape==[2,1,1,1]: return 'Pair of %s' % pl(groups[0])
    return 'High Card, %s' % NAME[vals[0]]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('pair of sixes', ['6h', '6d', 'Kc', '9s', '2h'], 'Pair of Sixes'),
  ('pair of deuces', ['2h', '2d', 'Kc', '9s', '5h'], 'Pair of Deuces'),
  ('wheel', ['Ah', '2d', '3c', '4s', '5h'], 'Straight, Five high'),
  ('steel wheel', ['As', '2s', '3s', '4s', '5s'], 'Straight Flush, Five high'),
  ('royal', ['Th', 'Jh', 'Qh', 'Kh', 'Ah'], 'Royal Flush'),
  ('king-high straight flush', ['9d', 'Td', 'Jd', 'Qd', 'Kd'], 'Straight Flush, King high'),
  ('threes full of kings', ['3h', '3d', '3c', 'Ks', 'Kh'], 'Full House, Threes full of Kings'),
  ('kings full of threes', ['Kh', 'Kd', 'Kc', '3s', '3h'], 'Full House, Kings full of Threes')],
 [('steel wheel', ['As', '2s', '3s', '4s', '5s'], 'Straight Flush, Five high'),
  ('royal', ['Th', 'Jh', 'Qh', 'Kh', 'Ah'], 'Royal Flush'),
  ('king-high straight flush', ['9d', 'Td', 'Jd', 'Qd', 'Kd'], 'Straight Flush, King high'),
  ('threes full of kings', ['3h', '3d', '3c', 'Ks', 'Kh'], 'Full House, Threes full of Kings'),
  ('kings full of threes', ['Kh', 'Kd', 'Kc', '3s', '3h'], 'Full House, Kings full of Threes'),
  ('two pair ace kicker', ['Ah', 'Kd', 'Kc', '7s', '7h'], 'Two Pair, Kings and Sevens'),
  ('two pair low kicker', ['Kh', 'Kd', '7c', '7s', '2h'], 'Two Pair, Kings and Sevens'),
  ('pair below high cards', ['Ah', 'Kd', '5c', '5s', '9h'], 'Pair of Fives')],
 [('threes full of kings', ['3h', '3d', '3c', 'Ks', 'Kh'], 'Full House, Threes full of Kings'),
  ('kings full of threes', ['Kh', 'Kd', 'Kc', '3s', '3h'], 'Full House, Kings full of Threes'),
  ('two pair ace kicker', ['Ah', 'Kd', 'Kc', '7s', '7h'], 'Two Pair, Kings and Sevens'),
  ('two pair low kicker', ['Kh', 'Kd', '7c', '7s', '2h'], 'Two Pair, Kings and Sevens'),
  ('pair below high cards', ['Ah', 'Kd', '5c', '5s', '9h'], 'Pair of Fives'),
  ('high card', ['Ah', 'Jd', '8c', '5s', '3h'], 'High Card, Ace'),
  ('four nines', ['9h', '9d', '9c', '9s', '2h'], 'Four Nines'),
  ('flush', ['2c', '7c', '9c', 'Jc', 'Qc'], 'Flush, Queen high')],
 [('pair of sixes', ['6h', '6d', 'Kc', '9s', '2h'], 'Pair of Sixes'),
  ('threes full of kings', ['3h', '3d', '3c', 'Ks', 'Kh'], 'Full House, Threes full of Kings'),
  ('kings full of threes', ['Kh', 'Kd', 'Kc', '3s', '3h'], 'Full House, Kings full of Threes'),
  ('high card', ['Ah', 'Jd', '8c', '5s', '3h'], 'High Card, Ace'),
  ('four nines', ['9h', '9d', '9c', '9s', '2h'], 'Four Nines'),
  ('flush', ['2c', '7c', '9c', 'Jc', 'Qc'], 'Flush, Queen high'),
  ('six-high straight', ['2h', '3d', '4c', '5s', '6h'], 'Straight, Six high'),
  ('three sixes', ['6h', '6d', '6c', 'Ks', '2h'], 'Three Sixes')],
 [('pair of sixes', ['6h', '6d', 'Kc', '9s', '2h'], 'Pair of Sixes'),
  ('pair of deuces', ['2h', '2d', 'Kc', '9s', '5h'], 'Pair of Deuces'),
  ('wheel', ['Ah', '2d', '3c', '4s', '5h'], 'Straight, Five high'),
  ('steel wheel', ['As', '2s', '3s', '4s', '5s'], 'Straight Flush, Five high'),
  ('threes full of kings', ['3h', '3d', '3c', 'Ks', 'Kh'], 'Full House, Threes full of Kings'),
  ('kings full of threes', ['Kh', 'Kd', 'Kc', '3s', '3h'], 'Full House, Kings full of Threes'),
  ('six-high straight', ['2h', '3d', '4c', '5s', '6h'], 'Straight, Six high'),
  ('three sixes', ['6h', '6d', '6c', 'Ks', '2h'], 'Three Sixes')]]
for label, args, expected in fixtures[N-1]:
    try:
        actual = solve(args)
    except Exception as exc:
        actual = 'error:' + type(exc).__name__
    check(label, actual, expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
pair of sixesPair of SixesPair of SixesPassed
pair of deucesPair of DeucesPair of DeucesPassed
wheelStraight, Five highStraight, Five highPassed
steel wheelStraight Flush, Five highStraight Flush, Five highPassed
royalRoyal FlushRoyal FlushPassed
king-high straight flushStraight Flush, King highStraight Flush, King highPassed
threes full of kingsFull House, Kings full of ThreesFull House, Threes full of KingsFailed
kings full of threesFull House, Kings full of ThreesFull House, Kings full of ThreesPassed

SHA-256 / d430cb6d1d56ac4aadd0d86de988f38bf93689930b1b219f1cb613fa2f91a90d

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
import collections
N = 1
observations = []
def solve(x):
    R='23456789TJQKA'
    NAME={2:'Two',3:'Three',4:'Four',5:'Five',6:'Six',7:'Seven',8:'Eight',9:'Nine',10:'Ten',11:'Jack',12:'Queen',13:'King',14:'Ace'}
    def pl(v): return {2:'Deuces',6:'Sixes'}.get(v, NAME[v]+'s')
    vals=sorted((R.index(c[0])+2 for c in x), reverse=True)
    cnt=collections.Counter(vals)
    groups=sorted(cnt, key=lambda v:(cnt[v],v), reverse=True)
    shape=sorted(cnt.values(), reverse=True)
    flush=len({c[1] for c in x})==1
    wheel=vals==[14,5,4,3,2]
    straight=len(cnt)==5 and (vals[0]-vals[4]==4 or wheel)
    top=5 if wheel else vals[0]
    if straight and flush: return 'Royal Flush' if top==14 else 'Straight Flush, %s high' % NAME[top]
    if shape==[4,1]: return 'Four %s' % pl(groups[0])
    if shape==[3,2]: return 'Full House, %s full of %s' % (pl(groups[1]), pl(groups[0]))
    if flush: return 'Flush, %s high' % NAME[vals[0]]
    if straight: return 'Straight, %s high' % NAME[top]
    if shape==[3,1,1]: return 'Three %s' % pl(groups[0])
    if shape==[2,2,1]: return 'Two Pair, %s and %s' % (pl(groups[0]), pl(groups[1]))
    if shape==[2,1,1,1]: return 'Pair of %s' % pl(groups[0])
    return 'High Card, %s' % NAME[vals[0]]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('pair of sixes', ['6h', '6d', 'Kc', '9s', '2h'], 'Pair of Sixes'),
  ('pair of deuces', ['2h', '2d', 'Kc', '9s', '5h'], 'Pair of Deuces'),
  ('wheel', ['Ah', '2d', '3c', '4s', '5h'], 'Straight, Five high'),
  ('steel wheel', ['As', '2s', '3s', '4s', '5s'], 'Straight Flush, Five high'),
  ('royal', ['Th', 'Jh', 'Qh', 'Kh', 'Ah'], 'Royal Flush'),
  ('king-high straight flush', ['9d', 'Td', 'Jd', 'Qd', 'Kd'], 'Straight Flush, King high'),
  ('threes full of kings', ['3h', '3d', '3c', 'Ks', 'Kh'], 'Full House, Threes full of Kings'),
  ('kings full of threes', ['Kh', 'Kd', 'Kc', '3s', '3h'], 'Full House, Kings full of Threes')],
 [('steel wheel', ['As', '2s', '3s', '4s', '5s'], 'Straight Flush, Five high'),
  ('royal', ['Th', 'Jh', 'Qh', 'Kh', 'Ah'], 'Royal Flush'),
  ('king-high straight flush', ['9d', 'Td', 'Jd', 'Qd', 'Kd'], 'Straight Flush, King high'),
  ('threes full of kings', ['3h', '3d', '3c', 'Ks', 'Kh'], 'Full House, Threes full of Kings'),
  ('kings full of threes', ['Kh', 'Kd', 'Kc', '3s', '3h'], 'Full House, Kings full of Threes'),
  ('two pair ace kicker', ['Ah', 'Kd', 'Kc', '7s', '7h'], 'Two Pair, Kings and Sevens'),
  ('two pair low kicker', ['Kh', 'Kd', '7c', '7s', '2h'], 'Two Pair, Kings and Sevens'),
  ('pair below high cards', ['Ah', 'Kd', '5c', '5s', '9h'], 'Pair of Fives')],
 [('threes full of kings', ['3h', '3d', '3c', 'Ks', 'Kh'], 'Full House, Threes full of Kings'),
  ('kings full of threes', ['Kh', 'Kd', 'Kc', '3s', '3h'], 'Full House, Kings full of Threes'),
  ('two pair ace kicker', ['Ah', 'Kd', 'Kc', '7s', '7h'], 'Two Pair, Kings and Sevens'),
  ('two pair low kicker', ['Kh', 'Kd', '7c', '7s', '2h'], 'Two Pair, Kings and Sevens'),
  ('pair below high cards', ['Ah', 'Kd', '5c', '5s', '9h'], 'Pair of Fives'),
  ('high card', ['Ah', 'Jd', '8c', '5s', '3h'], 'High Card, Ace'),
  ('four nines', ['9h', '9d', '9c', '9s', '2h'], 'Four Nines'),
  ('flush', ['2c', '7c', '9c', 'Jc', 'Qc'], 'Flush, Queen high')],
 [('pair of sixes', ['6h', '6d', 'Kc', '9s', '2h'], 'Pair of Sixes'),
  ('threes full of kings', ['3h', '3d', '3c', 'Ks', 'Kh'], 'Full House, Threes full of Kings'),
  ('kings full of threes', ['Kh', 'Kd', 'Kc', '3s', '3h'], 'Full House, Kings full of Threes'),
  ('high card', ['Ah', 'Jd', '8c', '5s', '3h'], 'High Card, Ace'),
  ('four nines', ['9h', '9d', '9c', '9s', '2h'], 'Four Nines'),
  ('flush', ['2c', '7c', '9c', 'Jc', 'Qc'], 'Flush, Queen high'),
  ('six-high straight', ['2h', '3d', '4c', '5s', '6h'], 'Straight, Six high'),
  ('three sixes', ['6h', '6d', '6c', 'Ks', '2h'], 'Three Sixes')],
 [('pair of sixes', ['6h', '6d', 'Kc', '9s', '2h'], 'Pair of Sixes'),
  ('pair of deuces', ['2h', '2d', 'Kc', '9s', '5h'], 'Pair of Deuces'),
  ('wheel', ['Ah', '2d', '3c', '4s', '5h'], 'Straight, Five high'),
  ('steel wheel', ['As', '2s', '3s', '4s', '5s'], 'Straight Flush, Five high'),
  ('threes full of kings', ['3h', '3d', '3c', 'Ks', 'Kh'], 'Full House, Threes full of Kings'),
  ('kings full of threes', ['Kh', 'Kd', 'Kc', '3s', '3h'], 'Full House, Kings full of Threes'),
  ('six-high straight', ['2h', '3d', '4c', '5s', '6h'], 'Straight, Six high'),
  ('three sixes', ['6h', '6d', '6c', 'Ks', '2h'], 'Three Sixes')]]
for label, args, expected in fixtures[N-1]:
    try:
        actual = solve(args)
    except Exception as exc:
        actual = 'error:' + type(exc).__name__
    check(label, actual, expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
pair of sixesPair of SixesPair of SixesPassed
pair of deucesPair of DeucesPair of DeucesPassed
wheelStraight, Five highStraight, Five highPassed
steel wheelStraight Flush, Five highStraight Flush, Five highPassed
royalRoyal FlushRoyal FlushPassed
king-high straight flushStraight Flush, King highStraight Flush, King highPassed
threes full of kingsFull House, Kings full of ThreesFull House, Threes full of KingsFailed
kings full of threesFull House, Threes full of KingsFull House, Kings full of ThreesFailed

SHA-256 / 1be43cbd0ac960a9d91bc48139fa34b15eea8f26d142ea070f0101975c2b3af2

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json
import collections
N = 1
observations = []
def solve(x):
    R='23456789TJQKA'
    NAME={2:'Two',3:'Three',4:'Four',5:'Five',6:'Six',7:'Seven',8:'Eight',9:'Nine',10:'Ten',11:'Jack',12:'Queen',13:'King',14:'Ace'}
    def pl(v): return {2:'Deuces',6:'Sixes'}.get(v, NAME[v]+'s')
    vals=sorted((R.index(c[0])+2 for c in x), reverse=True)
    cnt=collections.Counter(vals)
    groups=sorted(cnt, key=lambda v:(cnt[v],v), reverse=True)
    shape=sorted(cnt.values(), reverse=True)
    flush=len({c[1] for c in x})==1
    wheel=vals==[14,5,4,3,2]
    straight=len(cnt)==5 and (vals[0]-vals[4]==4 or wheel)
    top=5 if wheel else vals[0]
    if straight and flush: return 'Royal Flush' if top==14 else 'Straight Flush, %s high' % NAME[top]
    if shape==[4,1]: return 'Four %s' % pl(groups[0])
    if shape==[3,2]: return 'Full House, %s full of %s' % (pl(groups[0]), pl(groups[1]))
    if flush: return 'Flush, %s high' % NAME[vals[0]]
    if straight: return 'Straight, %s high' % NAME[top]
    if shape==[3,1,1]: return 'Three %s' % pl(groups[0])
    if shape==[2,2,1]: return 'Two Pair, %s and %s' % (pl(groups[0]), pl(groups[1]))
    if shape==[2,1,1,1]: return 'Pair of %s' % pl(groups[0])
    return 'High Card, %s' % NAME[vals[0]]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('pair of sixes', ['6h', '6d', 'Kc', '9s', '2h'], 'Pair of Sixes'),
  ('pair of deuces', ['2h', '2d', 'Kc', '9s', '5h'], 'Pair of Deuces'),
  ('wheel', ['Ah', '2d', '3c', '4s', '5h'], 'Straight, Five high'),
  ('steel wheel', ['As', '2s', '3s', '4s', '5s'], 'Straight Flush, Five high'),
  ('royal', ['Th', 'Jh', 'Qh', 'Kh', 'Ah'], 'Royal Flush'),
  ('king-high straight flush', ['9d', 'Td', 'Jd', 'Qd', 'Kd'], 'Straight Flush, King high'),
  ('threes full of kings', ['3h', '3d', '3c', 'Ks', 'Kh'], 'Full House, Threes full of Kings'),
  ('kings full of threes', ['Kh', 'Kd', 'Kc', '3s', '3h'], 'Full House, Kings full of Threes')],
 [('steel wheel', ['As', '2s', '3s', '4s', '5s'], 'Straight Flush, Five high'),
  ('royal', ['Th', 'Jh', 'Qh', 'Kh', 'Ah'], 'Royal Flush'),
  ('king-high straight flush', ['9d', 'Td', 'Jd', 'Qd', 'Kd'], 'Straight Flush, King high'),
  ('threes full of kings', ['3h', '3d', '3c', 'Ks', 'Kh'], 'Full House, Threes full of Kings'),
  ('kings full of threes', ['Kh', 'Kd', 'Kc', '3s', '3h'], 'Full House, Kings full of Threes'),
  ('two pair ace kicker', ['Ah', 'Kd', 'Kc', '7s', '7h'], 'Two Pair, Kings and Sevens'),
  ('two pair low kicker', ['Kh', 'Kd', '7c', '7s', '2h'], 'Two Pair, Kings and Sevens'),
  ('pair below high cards', ['Ah', 'Kd', '5c', '5s', '9h'], 'Pair of Fives')],
 [('threes full of kings', ['3h', '3d', '3c', 'Ks', 'Kh'], 'Full House, Threes full of Kings'),
  ('kings full of threes', ['Kh', 'Kd', 'Kc', '3s', '3h'], 'Full House, Kings full of Threes'),
  ('two pair ace kicker', ['Ah', 'Kd', 'Kc', '7s', '7h'], 'Two Pair, Kings and Sevens'),
  ('two pair low kicker', ['Kh', 'Kd', '7c', '7s', '2h'], 'Two Pair, Kings and Sevens'),
  ('pair below high cards', ['Ah', 'Kd', '5c', '5s', '9h'], 'Pair of Fives'),
  ('high card', ['Ah', 'Jd', '8c', '5s', '3h'], 'High Card, Ace'),
  ('four nines', ['9h', '9d', '9c', '9s', '2h'], 'Four Nines'),
  ('flush', ['2c', '7c', '9c', 'Jc', 'Qc'], 'Flush, Queen high')],
 [('pair of sixes', ['6h', '6d', 'Kc', '9s', '2h'], 'Pair of Sixes'),
  ('threes full of kings', ['3h', '3d', '3c', 'Ks', 'Kh'], 'Full House, Threes full of Kings'),
  ('kings full of threes', ['Kh', 'Kd', 'Kc', '3s', '3h'], 'Full House, Kings full of Threes'),
  ('high card', ['Ah', 'Jd', '8c', '5s', '3h'], 'High Card, Ace'),
  ('four nines', ['9h', '9d', '9c', '9s', '2h'], 'Four Nines'),
  ('flush', ['2c', '7c', '9c', 'Jc', 'Qc'], 'Flush, Queen high'),
  ('six-high straight', ['2h', '3d', '4c', '5s', '6h'], 'Straight, Six high'),
  ('three sixes', ['6h', '6d', '6c', 'Ks', '2h'], 'Three Sixes')],
 [('pair of sixes', ['6h', '6d', 'Kc', '9s', '2h'], 'Pair of Sixes'),
  ('pair of deuces', ['2h', '2d', 'Kc', '9s', '5h'], 'Pair of Deuces'),
  ('wheel', ['Ah', '2d', '3c', '4s', '5h'], 'Straight, Five high'),
  ('steel wheel', ['As', '2s', '3s', '4s', '5s'], 'Straight Flush, Five high'),
  ('threes full of kings', ['3h', '3d', '3c', 'Ks', 'Kh'], 'Full House, Threes full of Kings'),
  ('kings full of threes', ['Kh', 'Kd', 'Kc', '3s', '3h'], 'Full House, Kings full of Threes'),
  ('six-high straight', ['2h', '3d', '4c', '5s', '6h'], 'Straight, Six high'),
  ('three sixes', ['6h', '6d', '6c', 'Ks', '2h'], 'Three Sixes')]]
for label, args, expected in fixtures[N-1]:
    try:
        actual = solve(args)
    except Exception as exc:
        actual = 'error:' + type(exc).__name__
    check(label, actual, expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
pair of sixesPair of SixesPair of SixesPassed
pair of deucesPair of DeucesPair of DeucesPassed
wheelStraight, Five highStraight, Five highPassed
steel wheelStraight Flush, Five highStraight Flush, Five highPassed
royalRoyal FlushRoyal FlushPassed
king-high straight flushStraight Flush, King highStraight Flush, King highPassed
threes full of kingsFull House, Threes full of KingsFull House, Threes full of KingsPassed
kings full of threesFull House, Kings full of ThreesFull House, Kings full of ThreesPassed

SHA-256 / 3e60fb45225231f6a933d915ad0e5a0e6e4735896b5d4c0a8ef180bc55f0f336

Verification & scope

A deterministic toy model with an explicitly stated contract; card strings are rank+suit (for example "As", "Td"); no claim of conformance to any casino or tournament rulebook. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.

Observations recorded using Python 3.12.14 at 2026-09-29T14:50:09.850074+00:00.

Case digest / f65de52802e4b1b0fcc64652afcf19f9eb7d2c797c933ec34b79f3908105a6f4