FA-83406 / Card game rule engines / Open access
Ace-ten after a split is paid as a natural · case 01
Split hands reaching 21 in two cards collect 3:2.
ROOT CAUSE
The natural test ignores the split flag.
VERIFIED REPAIR
A natural requires two cards and a hand that did not come from a split.
Unsuccessful approach: Dropping the two-card requirement pays 3:2 on multi-card 21s.
Case contract
Input [player_cards, dealer_cards, bet, doubled, from_split]. A natural is a two-card 21 not from a split. Player bust loses the stake (2*bet if doubled) first; both naturals push; player natural pays 3:2 on the bet; dealer natural takes only the original bet; dealer bust pays the stake; otherwise compare totals, equal totals push. Return net units.
Why this case matters
Card-game engines, scoring apps and online tables apply this rule automatically on every hand.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
p, d, bet, doubled, split = x
def total(h):
t = 0
a = 0
for c in h:
if c[0] == 'A':
a += 1
t += 1
elif c[0] in 'TJQK':
t += 10
else:
t += int(c[0])
if a and t + 10 <= 21:
t += 10
return t
stake = bet * 2 if doubled else bet
pt = total(p)
dt = total(d)
pn = pt == 21 and len(p) == 2
dn = dt == 21 and len(d) == 2
if pt > 21:
return -stake
if pn and dn:
return 0
if pn:
return bet * 3 // 2
if dn:
return -bet
if dt > 21:
return stake
if pt > dt:
return stake
if pt < dt:
return -stake
return 0
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[[[['7D', '8C'], ['JC', '7S', 'QD'], 4, False, True], 4], [[['KH', '5D'], ['AD', 'JD'], 4, True, False], -4], [[['9H', '2H', '2D'], ['KC', '3C'], 10, False, False], 0], [[['7S', '6D'], ['4H', 'AC'], 20, False, False], -20], [[['6D', 'JC'], ['9H', '6C'], 4, True, False], 8], [[['7H', 'JS'], ['8S', '2D'], 20, True, False], 40], [[['TD', 'AD'], ['JC', '7H'], 4, False, True], 4], [[['7S', '4H', 'TC'], ['TC', 'AD'], 10, True, False], -10]], [[[['JD', 'QC'], ['JC', '2D'], 20, False, False], 20], [[['3C', 'TH'], ['AC', '8C'], 4, False, True], -4], [[['9C', '2D', '5D', '8D'], ['6H', 'KH'], 20, False, False], -20], [[['2D', '6S'], ['6H', 'TC'], 4, False, False], -4], [[['4H', '7S', '2C'], ['JC', '4S'], 20, False, False], -20], [[['9C', '8S'], ['9S', '5H'], 4, False, False], 4], [[['KS', '3D', '4D', '4H'], ['5H', 'QC'], 20, False, False], 20], [[['AS', 'KH'], ['9C', '8D'], 10, False, True], 10]], [[[['QS', 'QD', '5S'], ['8D', 'QH'], 4, False, False], -4], [[['4D', 'KS'], ['9D', 'JH', '9C'], 20, False, True], 20], [[['5H', '4D'], ['QH', '6D'], 2, False, False], -2], [[['5D', 'TC', '2C', '5S'], ['KD', 'QD'], 10, False, False], -10], [[['TH', '9S', '2H', '3H'], ['AS', 'QD'], 2, False, False], -2], [[['2D', '7C'], ['AC', 'TC'], 2, True, False], -2], [[['TD', '9C', '2D'], ['AH', '8D'], 4, True, False], 8], [[['TD', 'AD'], ['JC', '7H'], 4, False, True], 4]], [[[['3D', '3S', '3C', 'JC'], ['TC', '9D'], 20, False, False], 0], [[['QS', '5S'], ['7S', '9C', '9S'], 2, False, True], 2], [[['4C', 'KH'], ['7H', 'TS'], 10, True, False], -20], [[['KH', '4D'], ['QD', '7S'], 2, False, False], -2], [[['TH', '3S', '5C', '8D'], ['8S', '6S'], 4, False, False], -4], [[['JC', '7S'], ['9D', 'JH', '6C'], 20, False, False], 20], [[['8D', 'AC', '7C', '5C'], ['9C', '8H'], 20, False, False], 20], [[['AS', 'KH'], ['9C', '8D'], 10, False, True], 10]], [[[['6D', '7C'], ['JD', '8S'], 2, True, False], -4], [[['7H', 'QH'], ['7S', '4C'], 10, False, False], 10], [[['AC', '7D'], ['4D', '4C'], 20, False, False], 20], [[['AS', '3C'], ['QD', '9C'], 4, False, False], -4], [[['KS', '8H', 'JD'], ['TS', 'AS'], 4, False, True], -4], [[['TD', 'QS', 'TH'], ['8H', 'TS'], 20, False, False], -20], [[['8S', 'TS', '3S'], ['TD', '9H', 'AS'], 10, False, False], 10], [[['TD', 'AD'], ['JC', '7H'], 4, False, True], 4]]]
for i, (args, expected) in enumerate(fixtures[N-1]):
check("settlement case %d" % i, solve(args), 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 |
|---|---|---|---|
| settlement case 0 | 4 | 4 | Passed |
| settlement case 1 | -4 | -4 | Passed |
| settlement case 2 | 0 | 0 | Passed |
| settlement case 3 | -20 | -20 | Passed |
| settlement case 4 | 8 | 8 | Passed |
| settlement case 5 | 40 | 40 | Passed |
| settlement case 6 | 6 | 4 | Failed |
| settlement case 7 | -10 | -10 | Passed |
SHA-256 / 3e3b0501daf18142029be480d9f066896cabca38817386be1599dfbe90ee529a
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
p, d, bet, doubled, split = x
def total(h):
t = 0
a = 0
for c in h:
if c[0] == 'A':
a += 1
t += 1
elif c[0] in 'TJQK':
t += 10
else:
t += int(c[0])
if a and t + 10 <= 21:
t += 10
return t
stake = bet * 2 if doubled else bet
pt = total(p)
dt = total(d)
pn = pt == 21 and not split
dn = dt == 21 and len(d) == 2
if pt > 21:
return -stake
if pn and dn:
return 0
if pn:
return bet * 3 // 2
if dn:
return -bet
if dt > 21:
return stake
if pt > dt:
return stake
if pt < dt:
return -stake
return 0
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[[[['7D', '8C'], ['JC', '7S', 'QD'], 4, False, True], 4], [[['KH', '5D'], ['AD', 'JD'], 4, True, False], -4], [[['9H', '2H', '2D'], ['KC', '3C'], 10, False, False], 0], [[['7S', '6D'], ['4H', 'AC'], 20, False, False], -20], [[['6D', 'JC'], ['9H', '6C'], 4, True, False], 8], [[['7H', 'JS'], ['8S', '2D'], 20, True, False], 40], [[['TD', 'AD'], ['JC', '7H'], 4, False, True], 4], [[['7S', '4H', 'TC'], ['TC', 'AD'], 10, True, False], -10]], [[[['JD', 'QC'], ['JC', '2D'], 20, False, False], 20], [[['3C', 'TH'], ['AC', '8C'], 4, False, True], -4], [[['9C', '2D', '5D', '8D'], ['6H', 'KH'], 20, False, False], -20], [[['2D', '6S'], ['6H', 'TC'], 4, False, False], -4], [[['4H', '7S', '2C'], ['JC', '4S'], 20, False, False], -20], [[['9C', '8S'], ['9S', '5H'], 4, False, False], 4], [[['KS', '3D', '4D', '4H'], ['5H', 'QC'], 20, False, False], 20], [[['AS', 'KH'], ['9C', '8D'], 10, False, True], 10]], [[[['QS', 'QD', '5S'], ['8D', 'QH'], 4, False, False], -4], [[['4D', 'KS'], ['9D', 'JH', '9C'], 20, False, True], 20], [[['5H', '4D'], ['QH', '6D'], 2, False, False], -2], [[['5D', 'TC', '2C', '5S'], ['KD', 'QD'], 10, False, False], -10], [[['TH', '9S', '2H', '3H'], ['AS', 'QD'], 2, False, False], -2], [[['2D', '7C'], ['AC', 'TC'], 2, True, False], -2], [[['TD', '9C', '2D'], ['AH', '8D'], 4, True, False], 8], [[['TD', 'AD'], ['JC', '7H'], 4, False, True], 4]], [[[['3D', '3S', '3C', 'JC'], ['TC', '9D'], 20, False, False], 0], [[['QS', '5S'], ['7S', '9C', '9S'], 2, False, True], 2], [[['4C', 'KH'], ['7H', 'TS'], 10, True, False], -20], [[['KH', '4D'], ['QD', '7S'], 2, False, False], -2], [[['TH', '3S', '5C', '8D'], ['8S', '6S'], 4, False, False], -4], [[['JC', '7S'], ['9D', 'JH', '6C'], 20, False, False], 20], [[['8D', 'AC', '7C', '5C'], ['9C', '8H'], 20, False, False], 20], [[['AS', 'KH'], ['9C', '8D'], 10, False, True], 10]], [[[['6D', '7C'], ['JD', '8S'], 2, True, False], -4], [[['7H', 'QH'], ['7S', '4C'], 10, False, False], 10], [[['AC', '7D'], ['4D', '4C'], 20, False, False], 20], [[['AS', '3C'], ['QD', '9C'], 4, False, False], -4], [[['KS', '8H', 'JD'], ['TS', 'AS'], 4, False, True], -4], [[['TD', 'QS', 'TH'], ['8H', 'TS'], 20, False, False], -20], [[['8S', 'TS', '3S'], ['TD', '9H', 'AS'], 10, False, False], 10], [[['TD', 'AD'], ['JC', '7H'], 4, False, True], 4]]]
for i, (args, expected) in enumerate(fixtures[N-1]):
check("settlement case %d" % i, solve(args), 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 |
|---|---|---|---|
| settlement case 0 | 4 | 4 | Passed |
| settlement case 1 | -4 | -4 | Passed |
| settlement case 2 | 0 | 0 | Passed |
| settlement case 3 | -20 | -20 | Passed |
| settlement case 4 | 8 | 8 | Passed |
| settlement case 5 | 40 | 40 | Passed |
| settlement case 6 | 4 | 4 | Passed |
| settlement case 7 | 0 | -10 | Failed |
SHA-256 / 8cf99244fd841e193b7b371774501bf0f15bdd490eef73e02e63724e8b515141
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
p, d, bet, doubled, split = x
def total(h):
t = 0
a = 0
for c in h:
if c[0] == 'A':
a += 1
t += 1
elif c[0] in 'TJQK':
t += 10
else:
t += int(c[0])
if a and t + 10 <= 21:
t += 10
return t
stake = bet * 2 if doubled else bet
pt = total(p)
dt = total(d)
pn = pt == 21 and len(p) == 2 and not split
dn = dt == 21 and len(d) == 2
if pt > 21:
return -stake
if pn and dn:
return 0
if pn:
return bet * 3 // 2
if dn:
return -bet
if dt > 21:
return stake
if pt > dt:
return stake
if pt < dt:
return -stake
return 0
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[[[['7D', '8C'], ['JC', '7S', 'QD'], 4, False, True], 4], [[['KH', '5D'], ['AD', 'JD'], 4, True, False], -4], [[['9H', '2H', '2D'], ['KC', '3C'], 10, False, False], 0], [[['7S', '6D'], ['4H', 'AC'], 20, False, False], -20], [[['6D', 'JC'], ['9H', '6C'], 4, True, False], 8], [[['7H', 'JS'], ['8S', '2D'], 20, True, False], 40], [[['TD', 'AD'], ['JC', '7H'], 4, False, True], 4], [[['7S', '4H', 'TC'], ['TC', 'AD'], 10, True, False], -10]], [[[['JD', 'QC'], ['JC', '2D'], 20, False, False], 20], [[['3C', 'TH'], ['AC', '8C'], 4, False, True], -4], [[['9C', '2D', '5D', '8D'], ['6H', 'KH'], 20, False, False], -20], [[['2D', '6S'], ['6H', 'TC'], 4, False, False], -4], [[['4H', '7S', '2C'], ['JC', '4S'], 20, False, False], -20], [[['9C', '8S'], ['9S', '5H'], 4, False, False], 4], [[['KS', '3D', '4D', '4H'], ['5H', 'QC'], 20, False, False], 20], [[['AS', 'KH'], ['9C', '8D'], 10, False, True], 10]], [[[['QS', 'QD', '5S'], ['8D', 'QH'], 4, False, False], -4], [[['4D', 'KS'], ['9D', 'JH', '9C'], 20, False, True], 20], [[['5H', '4D'], ['QH', '6D'], 2, False, False], -2], [[['5D', 'TC', '2C', '5S'], ['KD', 'QD'], 10, False, False], -10], [[['TH', '9S', '2H', '3H'], ['AS', 'QD'], 2, False, False], -2], [[['2D', '7C'], ['AC', 'TC'], 2, True, False], -2], [[['TD', '9C', '2D'], ['AH', '8D'], 4, True, False], 8], [[['TD', 'AD'], ['JC', '7H'], 4, False, True], 4]], [[[['3D', '3S', '3C', 'JC'], ['TC', '9D'], 20, False, False], 0], [[['QS', '5S'], ['7S', '9C', '9S'], 2, False, True], 2], [[['4C', 'KH'], ['7H', 'TS'], 10, True, False], -20], [[['KH', '4D'], ['QD', '7S'], 2, False, False], -2], [[['TH', '3S', '5C', '8D'], ['8S', '6S'], 4, False, False], -4], [[['JC', '7S'], ['9D', 'JH', '6C'], 20, False, False], 20], [[['8D', 'AC', '7C', '5C'], ['9C', '8H'], 20, False, False], 20], [[['AS', 'KH'], ['9C', '8D'], 10, False, True], 10]], [[[['6D', '7C'], ['JD', '8S'], 2, True, False], -4], [[['7H', 'QH'], ['7S', '4C'], 10, False, False], 10], [[['AC', '7D'], ['4D', '4C'], 20, False, False], 20], [[['AS', '3C'], ['QD', '9C'], 4, False, False], -4], [[['KS', '8H', 'JD'], ['TS', 'AS'], 4, False, True], -4], [[['TD', 'QS', 'TH'], ['8H', 'TS'], 20, False, False], -20], [[['8S', 'TS', '3S'], ['TD', '9H', 'AS'], 10, False, False], 10], [[['TD', 'AD'], ['JC', '7H'], 4, False, True], 4]]]
for i, (args, expected) in enumerate(fixtures[N-1]):
check("settlement case %d" % i, solve(args), 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 |
|---|---|---|---|
| settlement case 0 | 4 | 4 | Passed |
| settlement case 1 | -4 | -4 | Passed |
| settlement case 2 | 0 | 0 | Passed |
| settlement case 3 | -20 | -20 | Passed |
| settlement case 4 | 8 | 8 | Passed |
| settlement case 5 | 40 | 40 | Passed |
| settlement case 6 | 4 | 4 | Passed |
| settlement case 7 | -10 | -10 | Passed |
SHA-256 / 8f66cccecbfc0ba8a05c8c002a155db8b94e541fb2c564a5f70a322e6eb72b0f
Verification & scope
A bounded toy rule contract stated explicitly in the contract field; cards are two-character codes (rank, suit). Not a complete implementation of any published rulebook or casino table rules. 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:21.305485+00:00.
Case digest / 3f9479db92bd9032a9b463959567c9de45fd8c24fb57c6fe9c3b463e3d98cdd4