FA-83626 / Card game rule engines / Open access
A new bid keeps the previous double · case 01
The final contract reports X although a later bid overcalled the doubled contract.
ROOT CAUSE
The double state is not reset on a new bid.
VERIFIED REPAIR
Reset the double state on every bid.
Unsuccessful approach: Decrementing the state turns a redouble into a double instead of clearing it.
Case contract
Input list of calls (bids like "2H"/"3N", "P", "X", "XX") starting with dealer seat 0. Each bid must outrank the previous bid (level, then C<D<H<S<N). X requires the last non-pass call to be an opponent bid; XX an opponent X. Four initial passes -> ["passed out", None, None]; three passes after a bid -> ["contract", bid+X/XX, declarer] where declarer is the first player of the declaring side to name the strain. Illegal call -> ["illegal", index, None]; unfinished -> ["open", None, None].
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):
calls = x
strains = 'CDHSN'
last_bid = None
last_bidder = None
state = 0
last_call = None
last_caller = None
passes = 0
first = {}
for i, call in enumerate(calls):
seat = i % 4
if call == 'P':
passes += 1
if last_bid is None and passes == 4:
return ['passed out', None, None]
if last_bid is not None and passes == 3:
side = last_bidder % 2
return ['contract', last_bid + ['', 'X', 'XX'][state], first[(side, last_bid[1])]]
continue
if call == 'X':
if last_call is None or last_call not in ('bid',) or (last_caller - seat) % 2 == 0:
return ['illegal', i, None]
state = 1
elif call == 'XX':
if last_call != 'X' or (last_caller - seat) % 2 == 0:
return ['illegal', i, None]
state = 2
else:
rank = (int(call[0]), strains.index(call[1]))
if last_bid is not None and rank <= (int(last_bid[0]), strains.index(last_bid[1])):
return ['illegal', i, None]
last_bid = call
last_bidder = seat
first.setdefault((seat % 2, call[1]), seat)
passes = 0
last_call = 'bid' if call not in ('X', 'XX') else call
last_caller = seat
return ['open', None, None]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[[['X', '1D', 'P'], ['illegal', 0, None]], [['P', 'X', '1C', 'P', 'P', 'P', 'P', '1N', '2S', '3H'], ['illegal', 1, None]], [['1S', 'P', 'P', '2C', '2N', 'P', '2S'], ['illegal', 6, None]], [['P', '1S', 'P', 'P', 'P', '2D', 'P'], ['contract', '1S', 1]], [['P', 'P'], ['open', None, None]], [['1H'], ['open', None, None]], [['P', '1D', 'X', '2C', '2S', 'P', '3D', 'P', 'P', 'P'], ['contract', '3D', 2]], [['1H', 'X', 'XX', '2C', 'P', 'P', 'P'], ['contract', '2C', 3]]], [[['P', 'P', '1C', 'P', 'X', 'P', '1H', '2C', '2N'], ['illegal', 4, None]], [['1C', '1C', 'P', '1S', 'P', 'X', 'P', 'P', 'XX'], ['illegal', 1, None]], [['P', 'P', '1C', 'P'], ['open', None, None]], [['1S', 'P', 'P', '2C', 'XX', 'X'], ['illegal', 4, None]], [['XX', '1C', '1N', 'P', 'P', 'P'], ['illegal', 0, None]], [['1C', 'XX', 'P', 'X', 'P', 'P', '1D', 'X', 'P', 'P', 'P'], ['illegal', 1, None]], [['1H', 'X', '2C', 'P', 'P', 'P'], ['contract', '2C', 2]], [['1C', 'P', 'P', 'X', 'XX', '1N', 'P', 'P', 'P'], ['contract', '1N', 1]]], [[['P', 'P'], ['open', None, None]], [['1H', 'P', 'XX', 'X', '1D', '2D'], ['illegal', 2, None]], [['XX'], ['illegal', 0, None]], [['XX', 'P'], ['illegal', 0, None]], [['X', 'P', '1C', 'P', 'P', 'P', 'X'], ['illegal', 0, None]], [['P', 'X', 'XX', 'X', 'X', 'P', 'X', 'P'], ['illegal', 1, None]], [['P', '1D', 'X', '2C', '2S', 'P', '3D', 'P', 'P', 'P'], ['contract', '3D', 2]], [['1S', 'X', 'XX', 'P', '2D', 'P', 'P', 'P'], ['contract', '2D', 0]]], [[['1H', '1S', 'P', '2H', 'P', 'P', '2N', 'X', 'P', '3D'], ['open', None, None]], [['P', 'X', 'X', '1H', 'P', 'P', 'X', '2D', 'P', 'P'], ['illegal', 1, None]], [['P', 'P', 'X', 'P', 'X'], ['illegal', 2, None]], [['XX', 'P', 'P', 'P'], ['illegal', 0, None]], [['P', 'X'], ['illegal', 1, None]], [['X', '1D', 'P', '2C', 'P', 'P', 'P', 'XX', '2D', 'X', '2H', 'P', '2S'], ['illegal', 0, None]], [['1H', 'X', 'XX', '2C', 'P', 'P', 'P'], ['contract', '2C', 3]], [['1H', 'X', '2C', 'P', 'P', 'P'], ['contract', '2C', 2]]], [[['1D', 'XX', 'P', 'P', 'XX', 'P'], ['illegal', 1, None]], [['1S', 'P', 'XX', '2D', '2S', 'P'], ['illegal', 2, None]], [['P', 'P', '1D', 'P', 'P', 'P', 'P', '1H', 'X', '1N', 'P'], ['contract', '1D', 2]], [['P', 'P', '1H', 'XX', '1S', '2C', '2H', '2D', 'P', 'P', '3C', 'XX', 'P'], ['illegal', 3, None]], [['P', '1C', 'XX', 'P', 'P', 'P', 'P', '1N', 'P'], ['illegal', 2, None]], [['P', '1D', 'X', '2C', '2S', 'P', '3D', 'P', 'P', 'P'], ['contract', '3D', 2]], [['XX', 'P', 'X', 'XX', '1C', 'P', 'XX', 'P', 'P', 'XX', 'X', '1H'], ['illegal', 0, None]], [['1C', 'P', 'P', 'X', 'XX', '1N', 'P', 'P', 'P'], ['contract', '1N', 1]]]]
for i, (args, expected) in enumerate(fixtures[N-1]):
check("auction 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 |
|---|---|---|---|
| auction case 0 | ['illegal', 0, None] | ['illegal', 0, None] | Passed |
| auction case 1 | ['illegal', 1, None] | ['illegal', 1, None] | Passed |
| auction case 2 | ['illegal', 6, None] | ['illegal', 6, None] | Passed |
| auction case 3 | ['contract', '1S', 1] | ['contract', '1S', 1] | Passed |
| auction case 4 | ['open', None, None] | ['open', None, None] | Passed |
| auction case 5 | ['open', None, None] | ['open', None, None] | Passed |
| auction case 6 | ['contract', '3DX', 2] | ['contract', '3D', 2] | Failed |
| auction case 7 | ['contract', '2CXX', 3] | ['contract', '2C', 3] | Failed |
SHA-256 / 75559fcc4ca361a6e9121f9e8b020f7719ff1e53866e26893e0c066a02048b87
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
calls = x
strains = 'CDHSN'
last_bid = None
last_bidder = None
state = 0
last_call = None
last_caller = None
passes = 0
first = {}
for i, call in enumerate(calls):
seat = i % 4
if call == 'P':
passes += 1
if last_bid is None and passes == 4:
return ['passed out', None, None]
if last_bid is not None and passes == 3:
side = last_bidder % 2
return ['contract', last_bid + ['', 'X', 'XX'][state], first[(side, last_bid[1])]]
continue
if call == 'X':
if last_call is None or last_call not in ('bid',) or (last_caller - seat) % 2 == 0:
return ['illegal', i, None]
state = 1
elif call == 'XX':
if last_call != 'X' or (last_caller - seat) % 2 == 0:
return ['illegal', i, None]
state = 2
else:
rank = (int(call[0]), strains.index(call[1]))
if last_bid is not None and rank <= (int(last_bid[0]), strains.index(last_bid[1])):
return ['illegal', i, None]
last_bid = call
last_bidder = seat
state = max(state - 1, 0)
first.setdefault((seat % 2, call[1]), seat)
passes = 0
last_call = 'bid' if call not in ('X', 'XX') else call
last_caller = seat
return ['open', None, None]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[[['X', '1D', 'P'], ['illegal', 0, None]], [['P', 'X', '1C', 'P', 'P', 'P', 'P', '1N', '2S', '3H'], ['illegal', 1, None]], [['1S', 'P', 'P', '2C', '2N', 'P', '2S'], ['illegal', 6, None]], [['P', '1S', 'P', 'P', 'P', '2D', 'P'], ['contract', '1S', 1]], [['P', 'P'], ['open', None, None]], [['1H'], ['open', None, None]], [['P', '1D', 'X', '2C', '2S', 'P', '3D', 'P', 'P', 'P'], ['contract', '3D', 2]], [['1H', 'X', 'XX', '2C', 'P', 'P', 'P'], ['contract', '2C', 3]]], [[['P', 'P', '1C', 'P', 'X', 'P', '1H', '2C', '2N'], ['illegal', 4, None]], [['1C', '1C', 'P', '1S', 'P', 'X', 'P', 'P', 'XX'], ['illegal', 1, None]], [['P', 'P', '1C', 'P'], ['open', None, None]], [['1S', 'P', 'P', '2C', 'XX', 'X'], ['illegal', 4, None]], [['XX', '1C', '1N', 'P', 'P', 'P'], ['illegal', 0, None]], [['1C', 'XX', 'P', 'X', 'P', 'P', '1D', 'X', 'P', 'P', 'P'], ['illegal', 1, None]], [['1H', 'X', '2C', 'P', 'P', 'P'], ['contract', '2C', 2]], [['1C', 'P', 'P', 'X', 'XX', '1N', 'P', 'P', 'P'], ['contract', '1N', 1]]], [[['P', 'P'], ['open', None, None]], [['1H', 'P', 'XX', 'X', '1D', '2D'], ['illegal', 2, None]], [['XX'], ['illegal', 0, None]], [['XX', 'P'], ['illegal', 0, None]], [['X', 'P', '1C', 'P', 'P', 'P', 'X'], ['illegal', 0, None]], [['P', 'X', 'XX', 'X', 'X', 'P', 'X', 'P'], ['illegal', 1, None]], [['P', '1D', 'X', '2C', '2S', 'P', '3D', 'P', 'P', 'P'], ['contract', '3D', 2]], [['1S', 'X', 'XX', 'P', '2D', 'P', 'P', 'P'], ['contract', '2D', 0]]], [[['1H', '1S', 'P', '2H', 'P', 'P', '2N', 'X', 'P', '3D'], ['open', None, None]], [['P', 'X', 'X', '1H', 'P', 'P', 'X', '2D', 'P', 'P'], ['illegal', 1, None]], [['P', 'P', 'X', 'P', 'X'], ['illegal', 2, None]], [['XX', 'P', 'P', 'P'], ['illegal', 0, None]], [['P', 'X'], ['illegal', 1, None]], [['X', '1D', 'P', '2C', 'P', 'P', 'P', 'XX', '2D', 'X', '2H', 'P', '2S'], ['illegal', 0, None]], [['1H', 'X', 'XX', '2C', 'P', 'P', 'P'], ['contract', '2C', 3]], [['1H', 'X', '2C', 'P', 'P', 'P'], ['contract', '2C', 2]]], [[['1D', 'XX', 'P', 'P', 'XX', 'P'], ['illegal', 1, None]], [['1S', 'P', 'XX', '2D', '2S', 'P'], ['illegal', 2, None]], [['P', 'P', '1D', 'P', 'P', 'P', 'P', '1H', 'X', '1N', 'P'], ['contract', '1D', 2]], [['P', 'P', '1H', 'XX', '1S', '2C', '2H', '2D', 'P', 'P', '3C', 'XX', 'P'], ['illegal', 3, None]], [['P', '1C', 'XX', 'P', 'P', 'P', 'P', '1N', 'P'], ['illegal', 2, None]], [['P', '1D', 'X', '2C', '2S', 'P', '3D', 'P', 'P', 'P'], ['contract', '3D', 2]], [['XX', 'P', 'X', 'XX', '1C', 'P', 'XX', 'P', 'P', 'XX', 'X', '1H'], ['illegal', 0, None]], [['1C', 'P', 'P', 'X', 'XX', '1N', 'P', 'P', 'P'], ['contract', '1N', 1]]]]
for i, (args, expected) in enumerate(fixtures[N-1]):
check("auction 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 |
|---|---|---|---|
| auction case 0 | ['illegal', 0, None] | ['illegal', 0, None] | Passed |
| auction case 1 | ['illegal', 1, None] | ['illegal', 1, None] | Passed |
| auction case 2 | ['illegal', 6, None] | ['illegal', 6, None] | Passed |
| auction case 3 | ['contract', '1S', 1] | ['contract', '1S', 1] | Passed |
| auction case 4 | ['open', None, None] | ['open', None, None] | Passed |
| auction case 5 | ['open', None, None] | ['open', None, None] | Passed |
| auction case 6 | ['contract', '3D', 2] | ['contract', '3D', 2] | Passed |
| auction case 7 | ['contract', '2CX', 3] | ['contract', '2C', 3] | Failed |
SHA-256 / e50fde49005abfe85185211531d10e6445c6c8d0a2713a08b4fce0db5b79d54b
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
calls = x
strains = 'CDHSN'
last_bid = None
last_bidder = None
state = 0
last_call = None
last_caller = None
passes = 0
first = {}
for i, call in enumerate(calls):
seat = i % 4
if call == 'P':
passes += 1
if last_bid is None and passes == 4:
return ['passed out', None, None]
if last_bid is not None and passes == 3:
side = last_bidder % 2
return ['contract', last_bid + ['', 'X', 'XX'][state], first[(side, last_bid[1])]]
continue
if call == 'X':
if last_call is None or last_call not in ('bid',) or (last_caller - seat) % 2 == 0:
return ['illegal', i, None]
state = 1
elif call == 'XX':
if last_call != 'X' or (last_caller - seat) % 2 == 0:
return ['illegal', i, None]
state = 2
else:
rank = (int(call[0]), strains.index(call[1]))
if last_bid is not None and rank <= (int(last_bid[0]), strains.index(last_bid[1])):
return ['illegal', i, None]
last_bid = call
last_bidder = seat
state = 0
first.setdefault((seat % 2, call[1]), seat)
passes = 0
last_call = 'bid' if call not in ('X', 'XX') else call
last_caller = seat
return ['open', None, None]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[[['X', '1D', 'P'], ['illegal', 0, None]], [['P', 'X', '1C', 'P', 'P', 'P', 'P', '1N', '2S', '3H'], ['illegal', 1, None]], [['1S', 'P', 'P', '2C', '2N', 'P', '2S'], ['illegal', 6, None]], [['P', '1S', 'P', 'P', 'P', '2D', 'P'], ['contract', '1S', 1]], [['P', 'P'], ['open', None, None]], [['1H'], ['open', None, None]], [['P', '1D', 'X', '2C', '2S', 'P', '3D', 'P', 'P', 'P'], ['contract', '3D', 2]], [['1H', 'X', 'XX', '2C', 'P', 'P', 'P'], ['contract', '2C', 3]]], [[['P', 'P', '1C', 'P', 'X', 'P', '1H', '2C', '2N'], ['illegal', 4, None]], [['1C', '1C', 'P', '1S', 'P', 'X', 'P', 'P', 'XX'], ['illegal', 1, None]], [['P', 'P', '1C', 'P'], ['open', None, None]], [['1S', 'P', 'P', '2C', 'XX', 'X'], ['illegal', 4, None]], [['XX', '1C', '1N', 'P', 'P', 'P'], ['illegal', 0, None]], [['1C', 'XX', 'P', 'X', 'P', 'P', '1D', 'X', 'P', 'P', 'P'], ['illegal', 1, None]], [['1H', 'X', '2C', 'P', 'P', 'P'], ['contract', '2C', 2]], [['1C', 'P', 'P', 'X', 'XX', '1N', 'P', 'P', 'P'], ['contract', '1N', 1]]], [[['P', 'P'], ['open', None, None]], [['1H', 'P', 'XX', 'X', '1D', '2D'], ['illegal', 2, None]], [['XX'], ['illegal', 0, None]], [['XX', 'P'], ['illegal', 0, None]], [['X', 'P', '1C', 'P', 'P', 'P', 'X'], ['illegal', 0, None]], [['P', 'X', 'XX', 'X', 'X', 'P', 'X', 'P'], ['illegal', 1, None]], [['P', '1D', 'X', '2C', '2S', 'P', '3D', 'P', 'P', 'P'], ['contract', '3D', 2]], [['1S', 'X', 'XX', 'P', '2D', 'P', 'P', 'P'], ['contract', '2D', 0]]], [[['1H', '1S', 'P', '2H', 'P', 'P', '2N', 'X', 'P', '3D'], ['open', None, None]], [['P', 'X', 'X', '1H', 'P', 'P', 'X', '2D', 'P', 'P'], ['illegal', 1, None]], [['P', 'P', 'X', 'P', 'X'], ['illegal', 2, None]], [['XX', 'P', 'P', 'P'], ['illegal', 0, None]], [['P', 'X'], ['illegal', 1, None]], [['X', '1D', 'P', '2C', 'P', 'P', 'P', 'XX', '2D', 'X', '2H', 'P', '2S'], ['illegal', 0, None]], [['1H', 'X', 'XX', '2C', 'P', 'P', 'P'], ['contract', '2C', 3]], [['1H', 'X', '2C', 'P', 'P', 'P'], ['contract', '2C', 2]]], [[['1D', 'XX', 'P', 'P', 'XX', 'P'], ['illegal', 1, None]], [['1S', 'P', 'XX', '2D', '2S', 'P'], ['illegal', 2, None]], [['P', 'P', '1D', 'P', 'P', 'P', 'P', '1H', 'X', '1N', 'P'], ['contract', '1D', 2]], [['P', 'P', '1H', 'XX', '1S', '2C', '2H', '2D', 'P', 'P', '3C', 'XX', 'P'], ['illegal', 3, None]], [['P', '1C', 'XX', 'P', 'P', 'P', 'P', '1N', 'P'], ['illegal', 2, None]], [['P', '1D', 'X', '2C', '2S', 'P', '3D', 'P', 'P', 'P'], ['contract', '3D', 2]], [['XX', 'P', 'X', 'XX', '1C', 'P', 'XX', 'P', 'P', 'XX', 'X', '1H'], ['illegal', 0, None]], [['1C', 'P', 'P', 'X', 'XX', '1N', 'P', 'P', 'P'], ['contract', '1N', 1]]]]
for i, (args, expected) in enumerate(fixtures[N-1]):
check("auction 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 |
|---|---|---|---|
| auction case 0 | ['illegal', 0, None] | ['illegal', 0, None] | Passed |
| auction case 1 | ['illegal', 1, None] | ['illegal', 1, None] | Passed |
| auction case 2 | ['illegal', 6, None] | ['illegal', 6, None] | Passed |
| auction case 3 | ['contract', '1S', 1] | ['contract', '1S', 1] | Passed |
| auction case 4 | ['open', None, None] | ['open', None, None] | Passed |
| auction case 5 | ['open', None, None] | ['open', None, None] | Passed |
| auction case 6 | ['contract', '3D', 2] | ['contract', '3D', 2] | Passed |
| auction case 7 | ['contract', '2C', 3] | ['contract', '2C', 3] | Passed |
SHA-256 / 9d07ad210de980bdcee53d3e23548642a0eb697a032ef62e89ea31e2d486f2e6
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:23.331251+00:00.
Case digest / e06bbc26cb97e1dbde3bc0704c27a43bcb029c9411c3a825968ff091b748f861