FA-83421 / Card game rule engines / Open access
Twenty-one against twenty-one is paid as a win · case 01
Non-natural 21 ties are paid instead of pushed.
ROOT CAUSE
The final tie branch pays when both totals are 21.
VERIFIED REPAIR
Any remaining equal totals push.
Unsuccessful approach: Treating all ties as losses charges pushes.
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 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 stake if pt == 21 else 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], [[['KS', '6D', 'AD'], ['3C', '4D', 'TH'], 4, False, False], 0], [[['5S', '6H', 'TD'], ['9C', '5D', '7H'], 10, False, False], 0]], [[[['2S', '6D', 'JS'], ['7H', 'AC', 'TD'], 4, False, False], 0], [[['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], [[['8D', 'TH', '5H'], ['KD', '6D', '7D'], 20, False, True], -20], [[['5S', '6H', 'TD'], ['9C', '5D', '7H'], 10, False, False], 0]], [[[['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], [[['AS', '8D', '7S'], ['KS', '6C'], 20, False, False], 0], [[['5S', '6H', 'TD'], ['9C', '5D', '7H'], 10, False, False], 0]], [[[['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], [[['5S', '6H', 'TD'], ['9C', '5D', '7H'], 10, False, False], 0], [[['5S', '6H', 'TD'], ['9C', '5D', '7H'], 10, False, False], 0]], [[[['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], [[['JH', '7D', 'AS'], ['TD', '8S'], 20, True, False], 0], [[['5S', '6H', 'TD'], ['9C', '5D', '7H'], 10, False, False], 0]]]
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 | 0 | 0 | Passed |
| settlement case 7 | 10 | 0 | Failed |
SHA-256 / 0e7f5ad63b0b43737d3302363d719ff9ac1ec23d9aea57ecec3a7e7858cd7e73
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 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
return -stake
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], [[['KS', '6D', 'AD'], ['3C', '4D', 'TH'], 4, False, False], 0], [[['5S', '6H', 'TD'], ['9C', '5D', '7H'], 10, False, False], 0]], [[[['2S', '6D', 'JS'], ['7H', 'AC', 'TD'], 4, False, False], 0], [[['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], [[['8D', 'TH', '5H'], ['KD', '6D', '7D'], 20, False, True], -20], [[['5S', '6H', 'TD'], ['9C', '5D', '7H'], 10, False, False], 0]], [[[['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], [[['AS', '8D', '7S'], ['KS', '6C'], 20, False, False], 0], [[['5S', '6H', 'TD'], ['9C', '5D', '7H'], 10, False, False], 0]], [[[['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], [[['5S', '6H', 'TD'], ['9C', '5D', '7H'], 10, False, False], 0], [[['5S', '6H', 'TD'], ['9C', '5D', '7H'], 10, False, False], 0]], [[[['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], [[['JH', '7D', 'AS'], ['TD', '8S'], 20, True, False], 0], [[['5S', '6H', 'TD'], ['9C', '5D', '7H'], 10, False, False], 0]]]
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 | -10 | 0 | Failed |
| settlement case 3 | -20 | -20 | Passed |
| settlement case 4 | 8 | 8 | Passed |
| settlement case 5 | 40 | 40 | Passed |
| settlement case 6 | -4 | 0 | Failed |
| settlement case 7 | -10 | 0 | Failed |
SHA-256 / c6e46a2e02acf312bdda1ce7b93eecf50388fb973f484ca445e559435026630a
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], [[['KS', '6D', 'AD'], ['3C', '4D', 'TH'], 4, False, False], 0], [[['5S', '6H', 'TD'], ['9C', '5D', '7H'], 10, False, False], 0]], [[[['2S', '6D', 'JS'], ['7H', 'AC', 'TD'], 4, False, False], 0], [[['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], [[['8D', 'TH', '5H'], ['KD', '6D', '7D'], 20, False, True], -20], [[['5S', '6H', 'TD'], ['9C', '5D', '7H'], 10, False, False], 0]], [[[['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], [[['AS', '8D', '7S'], ['KS', '6C'], 20, False, False], 0], [[['5S', '6H', 'TD'], ['9C', '5D', '7H'], 10, False, False], 0]], [[[['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], [[['5S', '6H', 'TD'], ['9C', '5D', '7H'], 10, False, False], 0], [[['5S', '6H', 'TD'], ['9C', '5D', '7H'], 10, False, False], 0]], [[[['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], [[['JH', '7D', 'AS'], ['TD', '8S'], 20, True, False], 0], [[['5S', '6H', 'TD'], ['9C', '5D', '7H'], 10, False, False], 0]]]
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 | 0 | 0 | Passed |
| settlement case 7 | 0 | 0 | Passed |
SHA-256 / 7bf504cc045d2988410e20c0eb3cd254b7090dbf422409958fa4c921d8a4fda5
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.344623+00:00.
Case digest / e6a98cab22ed09f16c802a42d79bc6a7503e4ad00b816354b2416560e66b465f