FA-83581 / Card game rule engines / Open access
Notrump contracts miss the first-trick premium · case 01
3NT made scores as a partscore.
ROOT CAUSE
Notrump tricks are all valued at 30.
VERIFIED REPAIR
The first notrump trick is worth 40.
Unsuccessful approach: Valuing every notrump trick at 40 overstates longer contracts.
Case contract
Input [level, strain, doubled(0/1/2), vulnerable, tricks]. Trick score C/D 20, H/S 30, NT 40+30; x2/x4 doubled/redoubled. Game bonus 300/500 when trick score >= 100 else 50 partscore; slam 500/750 (6) or 1000/1500 (7); overtricks trick value undoubled, 100/200 per double level; insult 50 per double level. Undertricks: 50/100 undoubled; doubled NV 100, 200, 200, then 300; doubled V 200 then 300; redoubled x2.
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):
level, strain, dbl, vul, tricks = x
need = 6 + level
if tricks < need:
down = need - tricks
if dbl == 0:
return -(100 if vul else 50) * down
pen = 0
for k in range(1, down + 1):
if vul:
pen += 200 if k == 1 else 300
else:
pen += 100 if k == 1 else 200 if k <= 3 else 300
return -pen * dbl
per = 20 if strain in 'CD' else 30
base = per * level
mult = [1, 2, 4][dbl]
trick_score = base * mult
score = trick_score
score += (500 if vul else 300) if trick_score >= 100 else 50
if level == 6:
score += 750 if vul else 500
elif level == 7:
score += 1500 if vul else 1000
over = tricks - need
if dbl == 0:
score += over * per
else:
score += over * (200 if vul else 100) * dbl
score += 50 * dbl
return score
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[[[7, 'N', 2, False, 13], 2280], [[7, 'S', 0, True, 12], -100], [[6, 'N', 0, True, 12], 1440], [[2, 'H', 1, True, 9], 870], [[3, 'H', 2, False, 11], 1160], [[5, 'N', 0, False, 11], 460], [[2, 'D', 0, True, 8], 90], [[4, 'N', 0, True, 12], 690]], [[[6, 'S', 0, False, 12], 980], [[7, 'D', 0, True, 8], -500], [[6, 'H', 0, False, 12], 980], [[6, 'H', 0, False, 11], -50], [[6, 'N', 0, False, 12], 990], [[6, 'D', 2, True, 13], 2230], [[5, 'N', 0, True, 6], -500], [[7, 'N', 1, True, 13], 2490]], [[[6, 'N', 0, True, 12], 1440], [[2, 'C', 0, True, 9], 110], [[4, 'N', 0, False, 12], 490], [[7, 'C', 0, True, 13], 2140], [[3, 'S', 1, False, 5], -800], [[5, 'C', 2, False, 13], 1200], [[7, 'C', 1, True, 8], -1400], [[1, 'N', 0, True, 7], 90]], [[[4, 'N', 2, True, 11], 1520], [[2, 'C', 2, False, 7], -200], [[2, 'H', 2, False, 5], -1000], [[7, 'H', 2, False, 11], -600], [[3, 'N', 1, True, 7], -500], [[6, 'S', 0, True, 12], 1430], [[4, 'N', 0, True, 12], 690], [[3, 'N', 2, False, 11], 1200]], [[[1, 'D', 2, True, 6], -400], [[5, 'H', 0, True, 11], 650], [[4, 'D', 0, True, 7], -300], [[7, 'D', 1, True, 10], -800], [[4, 'C', 1, True, 9], -200], [[3, 'S', 2, True, 9], 960], [[6, 'N', 0, False, 12], 990], [[7, 'N', 1, True, 13], 2490]]]
for i, (args, expected) in enumerate(fixtures[N-1]):
check("duplicate score 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 |
|---|---|---|---|
| duplicate score case 0 | 2240 | 2280 | Failed |
| duplicate score case 1 | -100 | -100 | Passed |
| duplicate score case 2 | 1430 | 1440 | Failed |
| duplicate score case 3 | 870 | 870 | Passed |
| duplicate score case 4 | 1160 | 1160 | Passed |
| duplicate score case 5 | 450 | 460 | Failed |
| duplicate score case 6 | 90 | 90 | Passed |
| duplicate score case 7 | 680 | 690 | Failed |
SHA-256 / 6706149e36e3c3e531c9e14aa3dd853885ac7248de79b024fbea3bc3ab2b0f95
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
level, strain, dbl, vul, tricks = x
need = 6 + level
if tricks < need:
down = need - tricks
if dbl == 0:
return -(100 if vul else 50) * down
pen = 0
for k in range(1, down + 1):
if vul:
pen += 200 if k == 1 else 300
else:
pen += 100 if k == 1 else 200 if k <= 3 else 300
return -pen * dbl
per = 20 if strain in 'CD' else 30
base = per * level + (10 * level if strain == 'N' else 0)
mult = [1, 2, 4][dbl]
trick_score = base * mult
score = trick_score
score += (500 if vul else 300) if trick_score >= 100 else 50
if level == 6:
score += 750 if vul else 500
elif level == 7:
score += 1500 if vul else 1000
over = tricks - need
if dbl == 0:
score += over * per
else:
score += over * (200 if vul else 100) * dbl
score += 50 * dbl
return score
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[[[7, 'N', 2, False, 13], 2280], [[7, 'S', 0, True, 12], -100], [[6, 'N', 0, True, 12], 1440], [[2, 'H', 1, True, 9], 870], [[3, 'H', 2, False, 11], 1160], [[5, 'N', 0, False, 11], 460], [[2, 'D', 0, True, 8], 90], [[4, 'N', 0, True, 12], 690]], [[[6, 'S', 0, False, 12], 980], [[7, 'D', 0, True, 8], -500], [[6, 'H', 0, False, 12], 980], [[6, 'H', 0, False, 11], -50], [[6, 'N', 0, False, 12], 990], [[6, 'D', 2, True, 13], 2230], [[5, 'N', 0, True, 6], -500], [[7, 'N', 1, True, 13], 2490]], [[[6, 'N', 0, True, 12], 1440], [[2, 'C', 0, True, 9], 110], [[4, 'N', 0, False, 12], 490], [[7, 'C', 0, True, 13], 2140], [[3, 'S', 1, False, 5], -800], [[5, 'C', 2, False, 13], 1200], [[7, 'C', 1, True, 8], -1400], [[1, 'N', 0, True, 7], 90]], [[[4, 'N', 2, True, 11], 1520], [[2, 'C', 2, False, 7], -200], [[2, 'H', 2, False, 5], -1000], [[7, 'H', 2, False, 11], -600], [[3, 'N', 1, True, 7], -500], [[6, 'S', 0, True, 12], 1430], [[4, 'N', 0, True, 12], 690], [[3, 'N', 2, False, 11], 1200]], [[[1, 'D', 2, True, 6], -400], [[5, 'H', 0, True, 11], 650], [[4, 'D', 0, True, 7], -300], [[7, 'D', 1, True, 10], -800], [[4, 'C', 1, True, 9], -200], [[3, 'S', 2, True, 9], 960], [[6, 'N', 0, False, 12], 990], [[7, 'N', 1, True, 13], 2490]]]
for i, (args, expected) in enumerate(fixtures[N-1]):
check("duplicate score 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 |
|---|---|---|---|
| duplicate score case 0 | 2520 | 2280 | Failed |
| duplicate score case 1 | -100 | -100 | Passed |
| duplicate score case 2 | 1490 | 1440 | Failed |
| duplicate score case 3 | 870 | 870 | Passed |
| duplicate score case 4 | 1160 | 1160 | Passed |
| duplicate score case 5 | 500 | 460 | Failed |
| duplicate score case 6 | 90 | 90 | Passed |
| duplicate score case 7 | 720 | 690 | Failed |
SHA-256 / 993bf7eba025e49cde11ef09a1ada415a2db42fc558897f956f748755a8476be
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
level, strain, dbl, vul, tricks = x
need = 6 + level
if tricks < need:
down = need - tricks
if dbl == 0:
return -(100 if vul else 50) * down
pen = 0
for k in range(1, down + 1):
if vul:
pen += 200 if k == 1 else 300
else:
pen += 100 if k == 1 else 200 if k <= 3 else 300
return -pen * dbl
per = 20 if strain in 'CD' else 30
base = per * level + (10 if strain == 'N' else 0)
mult = [1, 2, 4][dbl]
trick_score = base * mult
score = trick_score
score += (500 if vul else 300) if trick_score >= 100 else 50
if level == 6:
score += 750 if vul else 500
elif level == 7:
score += 1500 if vul else 1000
over = tricks - need
if dbl == 0:
score += over * per
else:
score += over * (200 if vul else 100) * dbl
score += 50 * dbl
return score
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[[[7, 'N', 2, False, 13], 2280], [[7, 'S', 0, True, 12], -100], [[6, 'N', 0, True, 12], 1440], [[2, 'H', 1, True, 9], 870], [[3, 'H', 2, False, 11], 1160], [[5, 'N', 0, False, 11], 460], [[2, 'D', 0, True, 8], 90], [[4, 'N', 0, True, 12], 690]], [[[6, 'S', 0, False, 12], 980], [[7, 'D', 0, True, 8], -500], [[6, 'H', 0, False, 12], 980], [[6, 'H', 0, False, 11], -50], [[6, 'N', 0, False, 12], 990], [[6, 'D', 2, True, 13], 2230], [[5, 'N', 0, True, 6], -500], [[7, 'N', 1, True, 13], 2490]], [[[6, 'N', 0, True, 12], 1440], [[2, 'C', 0, True, 9], 110], [[4, 'N', 0, False, 12], 490], [[7, 'C', 0, True, 13], 2140], [[3, 'S', 1, False, 5], -800], [[5, 'C', 2, False, 13], 1200], [[7, 'C', 1, True, 8], -1400], [[1, 'N', 0, True, 7], 90]], [[[4, 'N', 2, True, 11], 1520], [[2, 'C', 2, False, 7], -200], [[2, 'H', 2, False, 5], -1000], [[7, 'H', 2, False, 11], -600], [[3, 'N', 1, True, 7], -500], [[6, 'S', 0, True, 12], 1430], [[4, 'N', 0, True, 12], 690], [[3, 'N', 2, False, 11], 1200]], [[[1, 'D', 2, True, 6], -400], [[5, 'H', 0, True, 11], 650], [[4, 'D', 0, True, 7], -300], [[7, 'D', 1, True, 10], -800], [[4, 'C', 1, True, 9], -200], [[3, 'S', 2, True, 9], 960], [[6, 'N', 0, False, 12], 990], [[7, 'N', 1, True, 13], 2490]]]
for i, (args, expected) in enumerate(fixtures[N-1]):
check("duplicate score 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 |
|---|---|---|---|
| duplicate score case 0 | 2280 | 2280 | Passed |
| duplicate score case 1 | -100 | -100 | Passed |
| duplicate score case 2 | 1440 | 1440 | Passed |
| duplicate score case 3 | 870 | 870 | Passed |
| duplicate score case 4 | 1160 | 1160 | Passed |
| duplicate score case 5 | 460 | 460 | Passed |
| duplicate score case 6 | 90 | 90 | Passed |
| duplicate score case 7 | 690 | 690 | Passed |
SHA-256 / a6e35f8fdc79ec2ad033d76454eeb89081fc4684fd0534defc908235d0229be1
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:22.851691+00:00.
Case digest / d153070dad33f90bd64071530a1adf2739615ea81b58759f9f2cc1935881a87b