FA-82126 / Poker hand evaluation / Open access
Kicker-plus range runs into the pair of the top card · case 01
"A9s+" includes an "AAs" class counted as a pocket pair and "KTo+" includes AK-type classes.
ROOT CAUSE
The kicker loop stops at the top card itself instead of one rank below it.
VERIFIED REPAIR
Kicker-plus ranges end at one rank below the top card.
Unsuccessful approach: Stopping at the king index for every top card is correct only for aces.
Case contract
Input: a comma-separated hold'em range string. Tokens: pairs "77", "77+" (up to AA), "TT-77"; non-pairs "AKs"/"AKo"/"AK" (no suffix means both suited and offsuit), "A9s+" (kicker rises to one below the top card), "A9s-A6s" (inclusive). Whitespace around tokens is ignored. Duplicated classes count once. Return total combos: pair 6, suited 4, offsuit 12.
Why this case matters
Range combo counts drive equity, frequency and blocker arithmetic in solvers and trackers.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
R='23456789TJQKA'
classes=set()
def add(h, suf):
if h[0]==h[1]: classes.add(h)
elif suf: classes.add(h+suf)
else:
classes.add(h+'s'); classes.add(h+'o')
for tok in x.split(','):
tok=tok.strip()
if not tok: continue
plus=tok.endswith('+')
t=tok.rstrip('+')
if '-' in t:
a,b=t.split('-')
if a[0]==a[1]:
lo,hi=sorted([R.index(a[0]),R.index(b[0])])
for i in range(lo,hi+1): add(R[i]*2,'')
else:
lo,hi=sorted([R.index(a[1]),R.index(b[1])])
for i in range(lo,hi+1): add(a[0]+R[i],a[2:])
continue
if t[0]==t[1]:
start=R.index(t[0])
stop=len(R)-1 if plus else start
for i in range(start,stop+1): add(R[i]*2,'')
else:
top=R.index(t[0]); k=R.index(t[1])
stop=top if plus else k
for i in range(k,stop+1): add(t[0]+R[i],t[2:])
total=0
for c in classes:
if c[0]==c[1]: total+=6
elif c.endswith('s'): total+=4
else: total+=12
return total
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('pairs plus', '77+', 48), ('queens plus', 'QQ+', 18), ('suited ace plus', 'A9s+', 20),
('offsuit king plus', 'KTo+', 36), ('unsuffixed class', 'AK', 16),
('dash kicker range', 'A9s-A6s', 16), ('dash pair range', 'TT-77', 24),
('suited connector plus', 'T9s+', 4)],
[('offsuit king plus', 'KTo+', 36), ('dash kicker range', 'A9s-A6s', 16),
('dash pair range', 'TT-77', 24), ('reversed dash pairs', '77-TT', 24),
('spaced tokens', 'QQ+, AKs', 22), ('overlap dedupe', 'AKs,AK', 16), ('single pair', '22', 6),
('suited connector plus', 'T9s+', 4)],
[('suited ace plus', 'A9s+', 20), ('offsuit king plus', 'KTo+', 36),
('spaced tokens', 'QQ+, AKs', 22), ('overlap dedupe', 'AKs,AK', 16), ('single pair', '22', 6),
('mixed', 'JJ+,AQs+,KQo', 44), ('aces only', 'AA', 6), ('suited connector plus', 'T9s+', 4)],
[('pairs plus', '77+', 48), ('suited ace plus', 'A9s+', 20), ('offsuit king plus', 'KTo+', 36),
('mixed', 'JJ+,AQs+,KQo', 44), ('aces only', 'AA', 6), ('suited connector plus', 'T9s+', 4),
('trailing comma', 'KK+,', 12), ('reversed kicker dash', 'K6o-K9o', 48)],
[('pairs plus', '77+', 48), ('queens plus', 'QQ+', 18), ('suited ace plus', 'A9s+', 20),
('offsuit king plus', 'KTo+', 36), ('unsuffixed class', 'AK', 16),
('dash kicker range', 'A9s-A6s', 16), ('suited connector plus', 'T9s+', 4),
('reversed kicker dash', 'K6o-K9o', 48)]]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| pairs plus | 48 | 48 | Passed |
| queens plus | 18 | 18 | Passed |
| suited ace plus | 26 | 20 | Failed |
| offsuit king plus | 42 | 36 | Failed |
| unsuffixed class | 16 | 16 | Passed |
| dash kicker range | 16 | 16 | Passed |
| dash pair range | 24 | 24 | Passed |
| suited connector plus | 10 | 4 | Failed |
SHA-256 / 05deb13c1581608ff7ef748455f5bc4d36ce2c53fb9aae5cbe319c44beccd88e
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
R='23456789TJQKA'
classes=set()
def add(h, suf):
if h[0]==h[1]: classes.add(h)
elif suf: classes.add(h+suf)
else:
classes.add(h+'s'); classes.add(h+'o')
for tok in x.split(','):
tok=tok.strip()
if not tok: continue
plus=tok.endswith('+')
t=tok.rstrip('+')
if '-' in t:
a,b=t.split('-')
if a[0]==a[1]:
lo,hi=sorted([R.index(a[0]),R.index(b[0])])
for i in range(lo,hi+1): add(R[i]*2,'')
else:
lo,hi=sorted([R.index(a[1]),R.index(b[1])])
for i in range(lo,hi+1): add(a[0]+R[i],a[2:])
continue
if t[0]==t[1]:
start=R.index(t[0])
stop=len(R)-1 if plus else start
for i in range(start,stop+1): add(R[i]*2,'')
else:
top=R.index(t[0]); k=R.index(t[1])
stop=len(R)-2 if plus else k
for i in range(k,stop+1): add(t[0]+R[i],t[2:])
total=0
for c in classes:
if c[0]==c[1]: total+=6
elif c.endswith('s'): total+=4
else: total+=12
return total
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('pairs plus', '77+', 48), ('queens plus', 'QQ+', 18), ('suited ace plus', 'A9s+', 20),
('offsuit king plus', 'KTo+', 36), ('unsuffixed class', 'AK', 16),
('dash kicker range', 'A9s-A6s', 16), ('dash pair range', 'TT-77', 24),
('suited connector plus', 'T9s+', 4)],
[('offsuit king plus', 'KTo+', 36), ('dash kicker range', 'A9s-A6s', 16),
('dash pair range', 'TT-77', 24), ('reversed dash pairs', '77-TT', 24),
('spaced tokens', 'QQ+, AKs', 22), ('overlap dedupe', 'AKs,AK', 16), ('single pair', '22', 6),
('suited connector plus', 'T9s+', 4)],
[('suited ace plus', 'A9s+', 20), ('offsuit king plus', 'KTo+', 36),
('spaced tokens', 'QQ+, AKs', 22), ('overlap dedupe', 'AKs,AK', 16), ('single pair', '22', 6),
('mixed', 'JJ+,AQs+,KQo', 44), ('aces only', 'AA', 6), ('suited connector plus', 'T9s+', 4)],
[('pairs plus', '77+', 48), ('suited ace plus', 'A9s+', 20), ('offsuit king plus', 'KTo+', 36),
('mixed', 'JJ+,AQs+,KQo', 44), ('aces only', 'AA', 6), ('suited connector plus', 'T9s+', 4),
('trailing comma', 'KK+,', 12), ('reversed kicker dash', 'K6o-K9o', 48)],
[('pairs plus', '77+', 48), ('queens plus', 'QQ+', 18), ('suited ace plus', 'A9s+', 20),
('offsuit king plus', 'KTo+', 36), ('unsuffixed class', 'AK', 16),
('dash kicker range', 'A9s-A6s', 16), ('suited connector plus', 'T9s+', 4),
('reversed kicker dash', 'K6o-K9o', 48)]]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| pairs plus | 48 | 48 | Passed |
| queens plus | 18 | 18 | Passed |
| suited ace plus | 20 | 20 | Passed |
| offsuit king plus | 42 | 36 | Failed |
| unsuffixed class | 16 | 16 | Passed |
| dash kicker range | 16 | 16 | Passed |
| dash pair range | 24 | 24 | Passed |
| suited connector plus | 22 | 4 | Failed |
SHA-256 / 9bf3ecbdc309db48aff9ca713c86a076ed5242d5ed15ae6d5d7c2f6b43301b7f
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
R='23456789TJQKA'
classes=set()
def add(h, suf):
if h[0]==h[1]: classes.add(h)
elif suf: classes.add(h+suf)
else:
classes.add(h+'s'); classes.add(h+'o')
for tok in x.split(','):
tok=tok.strip()
if not tok: continue
plus=tok.endswith('+')
t=tok.rstrip('+')
if '-' in t:
a,b=t.split('-')
if a[0]==a[1]:
lo,hi=sorted([R.index(a[0]),R.index(b[0])])
for i in range(lo,hi+1): add(R[i]*2,'')
else:
lo,hi=sorted([R.index(a[1]),R.index(b[1])])
for i in range(lo,hi+1): add(a[0]+R[i],a[2:])
continue
if t[0]==t[1]:
start=R.index(t[0])
stop=len(R)-1 if plus else start
for i in range(start,stop+1): add(R[i]*2,'')
else:
top=R.index(t[0]); k=R.index(t[1])
stop=top-1 if plus else k
for i in range(k,stop+1): add(t[0]+R[i],t[2:])
total=0
for c in classes:
if c[0]==c[1]: total+=6
elif c.endswith('s'): total+=4
else: total+=12
return total
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('pairs plus', '77+', 48), ('queens plus', 'QQ+', 18), ('suited ace plus', 'A9s+', 20),
('offsuit king plus', 'KTo+', 36), ('unsuffixed class', 'AK', 16),
('dash kicker range', 'A9s-A6s', 16), ('dash pair range', 'TT-77', 24),
('suited connector plus', 'T9s+', 4)],
[('offsuit king plus', 'KTo+', 36), ('dash kicker range', 'A9s-A6s', 16),
('dash pair range', 'TT-77', 24), ('reversed dash pairs', '77-TT', 24),
('spaced tokens', 'QQ+, AKs', 22), ('overlap dedupe', 'AKs,AK', 16), ('single pair', '22', 6),
('suited connector plus', 'T9s+', 4)],
[('suited ace plus', 'A9s+', 20), ('offsuit king plus', 'KTo+', 36),
('spaced tokens', 'QQ+, AKs', 22), ('overlap dedupe', 'AKs,AK', 16), ('single pair', '22', 6),
('mixed', 'JJ+,AQs+,KQo', 44), ('aces only', 'AA', 6), ('suited connector plus', 'T9s+', 4)],
[('pairs plus', '77+', 48), ('suited ace plus', 'A9s+', 20), ('offsuit king plus', 'KTo+', 36),
('mixed', 'JJ+,AQs+,KQo', 44), ('aces only', 'AA', 6), ('suited connector plus', 'T9s+', 4),
('trailing comma', 'KK+,', 12), ('reversed kicker dash', 'K6o-K9o', 48)],
[('pairs plus', '77+', 48), ('queens plus', 'QQ+', 18), ('suited ace plus', 'A9s+', 20),
('offsuit king plus', 'KTo+', 36), ('unsuffixed class', 'AK', 16),
('dash kicker range', 'A9s-A6s', 16), ('suited connector plus', 'T9s+', 4),
('reversed kicker dash', 'K6o-K9o', 48)]]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| pairs plus | 48 | 48 | Passed |
| queens plus | 18 | 18 | Passed |
| suited ace plus | 20 | 20 | Passed |
| offsuit king plus | 36 | 36 | Passed |
| unsuffixed class | 16 | 16 | Passed |
| dash kicker range | 16 | 16 | Passed |
| dash pair range | 24 | 24 | Passed |
| suited connector plus | 4 | 4 | Passed |
SHA-256 / 4b37215bae4e284c332f171027d39c66d2d4ceb511dd6634a704500bd99e0fd8
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.348238+00:00.
Case digest / 485ff6fc9132e5367ef393bc9b2d776ee1b5b6f2aafa451f0c160e7660075850