FAILURE MAP
← Case archive

FA-83491 / Card game rule engines / Open access

Knocking with exactly ten deadwood is refused · case 01

A legal ten-point knock is reported illegal.

Verified by executionVariant 1 · 8 checks per implementationDownload source bundle ↓JSON ↗

ROOT CAUSE

The knock limit uses >= 10.

VERIFIED REPAIR

Knock is legal with up to 10 deadwood.

Unsuccessful approach: Limiting the combined deadwood of both players rejects legal knocks.

Case contract

Input [knocker_deadwood_cards, defender_deadwood_after_layoffs, gin, big_gin]. Deadwood: A=1, T/J/Q/K=10, others pip. Knocking above 10 is ["illegal", 0]. Big gin scores 31 + defender deadwood, gin 25 + defender deadwood (gin cannot be undercut). Otherwise if defender deadwood <= knocker deadwood the defender undercuts for 25 + difference; else knocker scores the difference.

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):
    kc, dc, gin, big = x
    def dw(cards):
        t = 0
        for c in cards:
            r = c[0]
            t += 1 if r == 'A' else 10 if r in 'TJQK' else int(r)
        return t
    k = dw(kc)
    d = dw(dc)
    if k >= 10:
        return ['illegal', 0]
    if big:
        return ['knocker', 31 + d]
    if gin:
        return ['knocker', 25 + d]
    if d <= k:
        return ['defender', 25 + k - d]
    return ['knocker', d - k]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[[[[], ['5H', 'JH', 'AC', '4H'], True, False], ['knocker', 45]], [[[], ['KS', '5S', '4H'], False, False], ['knocker', 19]], [[['KD', 'JH', '9S'], ['JS', '7D'], False, False], ['illegal', 0]], [[['4S', '4D', 'AS'], ['3D', 'JS', 'KD', '7D'], False, False], ['knocker', 21]], [[['TS', 'JC', '5S'], ['4H', '7C'], False, False], ['illegal', 0]], [[[], ['KS', 'TD', '5D', '7D'], False, False], ['knocker', 32]], [[[], ['4D', 'JS'], True, False], ['knocker', 39]], [[['KC'], ['5D'], False, False], ['defender', 30]]], [[[['6S', '8S', '6C'], ['4C', 'KH', '9S'], False, False], ['illegal', 0]], [[[], [], True, False], ['knocker', 25]], [[['6C'], [], False, False], ['defender', 31]], [[[], ['4D', 'JS'], True, False], ['knocker', 39]], [[['4S'], ['JH', '9H', '8C'], False, False], ['knocker', 23]], [[[], ['KH', '3D'], False, False], ['knocker', 13]], [[['2C'], ['2D', 'AH', 'JD', '5S'], False, False], ['knocker', 16]], [[['QH'], ['9D'], False, False], ['defender', 26]]], [[[['KH', '4H', 'JD'], [], False, False], ['illegal', 0]], [[[], ['8S', 'QD'], True, False], ['knocker', 43]], [[['7D', '7S'], ['2S', 'QC'], False, False], ['illegal', 0]], [[['2C'], ['2D', 'AH', 'JD', '5S'], False, False], ['knocker', 16]], [[[], ['3C', '3H', '5S'], True, False], ['knocker', 36]], [[['AC', '9D', 'AD'], ['8S'], False, False], ['illegal', 0]], [[[], ['8D', 'KC', '7C', 'AC'], False, False], ['knocker', 26]], [[['2H', '5S', '3C'], ['7C', '4C', 'TC'], False, False], ['knocker', 11]]], [[[[], ['JS', '8H', '7C'], False, False], ['knocker', 25]], [[[], ['2C'], False, False], ['knocker', 2]], [[[], ['KD'], False, False], ['knocker', 10]], [[[], [], True, False], ['knocker', 25]], [[['TH', '3H', 'KC'], ['TD', 'AS', 'JD', '5H'], False, False], ['illegal', 0]], [[['TH', '8S'], ['QC', '5S', '2S', '6D'], False, False], ['illegal', 0]], [[[], ['4C', '7H'], True, True], ['knocker', 42]], [[['JS'], ['7D'], False, False], ['defender', 28]]], [[[[], ['8D', 'KC', '7C', 'AC'], False, False], ['knocker', 26]], [[['TH', '6C'], ['6D', 'QC'], False, False], ['illegal', 0]], [[['9C'], ['8S', 'JD'], False, False], ['knocker', 9]], [[['5H', '6D'], ['QH', '8D', 'KH'], False, False], ['illegal', 0]], [[['QC', 'JD'], ['7C', 'KH', '8C', '5S'], False, False], ['illegal', 0]], [[[], ['4D', '3S'], False, False], ['knocker', 7]], [[[], ['4S', 'AH', '8C', 'JS'], True, True], ['knocker', 54]], [[['KC'], ['8H', '2C', '9S', '6D'], False, False], ['knocker', 15]]]]
for i, (args, expected) in enumerate(fixtures[N-1]):
    check("knock 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 fixtureActualExpectedOutcome
knock score case 0['knocker', 45]['knocker', 45]Passed
knock score case 1['knocker', 19]['knocker', 19]Passed
knock score case 2['illegal', 0]['illegal', 0]Passed
knock score case 3['knocker', 21]['knocker', 21]Passed
knock score case 4['illegal', 0]['illegal', 0]Passed
knock score case 5['knocker', 32]['knocker', 32]Passed
knock score case 6['knocker', 39]['knocker', 39]Passed
knock score case 7['illegal', 0]['defender', 30]Failed

SHA-256 / 038d1a025e3cff012717eae514163b35d3832416e1721c2ee171a2806222480c

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(x):
    kc, dc, gin, big = x
    def dw(cards):
        t = 0
        for c in cards:
            r = c[0]
            t += 1 if r == 'A' else 10 if r in 'TJQK' else int(r)
        return t
    k = dw(kc)
    d = dw(dc)
    if k + d > 10:
        return ['illegal', 0]
    if big:
        return ['knocker', 31 + d]
    if gin:
        return ['knocker', 25 + d]
    if d <= k:
        return ['defender', 25 + k - d]
    return ['knocker', d - k]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[[[[], ['5H', 'JH', 'AC', '4H'], True, False], ['knocker', 45]], [[[], ['KS', '5S', '4H'], False, False], ['knocker', 19]], [[['KD', 'JH', '9S'], ['JS', '7D'], False, False], ['illegal', 0]], [[['4S', '4D', 'AS'], ['3D', 'JS', 'KD', '7D'], False, False], ['knocker', 21]], [[['TS', 'JC', '5S'], ['4H', '7C'], False, False], ['illegal', 0]], [[[], ['KS', 'TD', '5D', '7D'], False, False], ['knocker', 32]], [[[], ['4D', 'JS'], True, False], ['knocker', 39]], [[['KC'], ['5D'], False, False], ['defender', 30]]], [[[['6S', '8S', '6C'], ['4C', 'KH', '9S'], False, False], ['illegal', 0]], [[[], [], True, False], ['knocker', 25]], [[['6C'], [], False, False], ['defender', 31]], [[[], ['4D', 'JS'], True, False], ['knocker', 39]], [[['4S'], ['JH', '9H', '8C'], False, False], ['knocker', 23]], [[[], ['KH', '3D'], False, False], ['knocker', 13]], [[['2C'], ['2D', 'AH', 'JD', '5S'], False, False], ['knocker', 16]], [[['QH'], ['9D'], False, False], ['defender', 26]]], [[[['KH', '4H', 'JD'], [], False, False], ['illegal', 0]], [[[], ['8S', 'QD'], True, False], ['knocker', 43]], [[['7D', '7S'], ['2S', 'QC'], False, False], ['illegal', 0]], [[['2C'], ['2D', 'AH', 'JD', '5S'], False, False], ['knocker', 16]], [[[], ['3C', '3H', '5S'], True, False], ['knocker', 36]], [[['AC', '9D', 'AD'], ['8S'], False, False], ['illegal', 0]], [[[], ['8D', 'KC', '7C', 'AC'], False, False], ['knocker', 26]], [[['2H', '5S', '3C'], ['7C', '4C', 'TC'], False, False], ['knocker', 11]]], [[[[], ['JS', '8H', '7C'], False, False], ['knocker', 25]], [[[], ['2C'], False, False], ['knocker', 2]], [[[], ['KD'], False, False], ['knocker', 10]], [[[], [], True, False], ['knocker', 25]], [[['TH', '3H', 'KC'], ['TD', 'AS', 'JD', '5H'], False, False], ['illegal', 0]], [[['TH', '8S'], ['QC', '5S', '2S', '6D'], False, False], ['illegal', 0]], [[[], ['4C', '7H'], True, True], ['knocker', 42]], [[['JS'], ['7D'], False, False], ['defender', 28]]], [[[[], ['8D', 'KC', '7C', 'AC'], False, False], ['knocker', 26]], [[['TH', '6C'], ['6D', 'QC'], False, False], ['illegal', 0]], [[['9C'], ['8S', 'JD'], False, False], ['knocker', 9]], [[['5H', '6D'], ['QH', '8D', 'KH'], False, False], ['illegal', 0]], [[['QC', 'JD'], ['7C', 'KH', '8C', '5S'], False, False], ['illegal', 0]], [[[], ['4D', '3S'], False, False], ['knocker', 7]], [[[], ['4S', 'AH', '8C', 'JS'], True, True], ['knocker', 54]], [[['KC'], ['8H', '2C', '9S', '6D'], False, False], ['knocker', 15]]]]
for i, (args, expected) in enumerate(fixtures[N-1]):
    check("knock 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 fixtureActualExpectedOutcome
knock score case 0['illegal', 0]['knocker', 45]Failed
knock score case 1['illegal', 0]['knocker', 19]Failed
knock score case 2['illegal', 0]['illegal', 0]Passed
knock score case 3['illegal', 0]['knocker', 21]Failed
knock score case 4['illegal', 0]['illegal', 0]Passed
knock score case 5['illegal', 0]['knocker', 32]Failed
knock score case 6['illegal', 0]['knocker', 39]Failed
knock score case 7['illegal', 0]['defender', 30]Failed

SHA-256 / fbd9aa720be792b73e606a203f11a37d61783d355437df2cdc9b15fca01d587c

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(x):
    kc, dc, gin, big = x
    def dw(cards):
        t = 0
        for c in cards:
            r = c[0]
            t += 1 if r == 'A' else 10 if r in 'TJQK' else int(r)
        return t
    k = dw(kc)
    d = dw(dc)
    if k > 10:
        return ['illegal', 0]
    if big:
        return ['knocker', 31 + d]
    if gin:
        return ['knocker', 25 + d]
    if d <= k:
        return ['defender', 25 + k - d]
    return ['knocker', d - k]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[[[[], ['5H', 'JH', 'AC', '4H'], True, False], ['knocker', 45]], [[[], ['KS', '5S', '4H'], False, False], ['knocker', 19]], [[['KD', 'JH', '9S'], ['JS', '7D'], False, False], ['illegal', 0]], [[['4S', '4D', 'AS'], ['3D', 'JS', 'KD', '7D'], False, False], ['knocker', 21]], [[['TS', 'JC', '5S'], ['4H', '7C'], False, False], ['illegal', 0]], [[[], ['KS', 'TD', '5D', '7D'], False, False], ['knocker', 32]], [[[], ['4D', 'JS'], True, False], ['knocker', 39]], [[['KC'], ['5D'], False, False], ['defender', 30]]], [[[['6S', '8S', '6C'], ['4C', 'KH', '9S'], False, False], ['illegal', 0]], [[[], [], True, False], ['knocker', 25]], [[['6C'], [], False, False], ['defender', 31]], [[[], ['4D', 'JS'], True, False], ['knocker', 39]], [[['4S'], ['JH', '9H', '8C'], False, False], ['knocker', 23]], [[[], ['KH', '3D'], False, False], ['knocker', 13]], [[['2C'], ['2D', 'AH', 'JD', '5S'], False, False], ['knocker', 16]], [[['QH'], ['9D'], False, False], ['defender', 26]]], [[[['KH', '4H', 'JD'], [], False, False], ['illegal', 0]], [[[], ['8S', 'QD'], True, False], ['knocker', 43]], [[['7D', '7S'], ['2S', 'QC'], False, False], ['illegal', 0]], [[['2C'], ['2D', 'AH', 'JD', '5S'], False, False], ['knocker', 16]], [[[], ['3C', '3H', '5S'], True, False], ['knocker', 36]], [[['AC', '9D', 'AD'], ['8S'], False, False], ['illegal', 0]], [[[], ['8D', 'KC', '7C', 'AC'], False, False], ['knocker', 26]], [[['2H', '5S', '3C'], ['7C', '4C', 'TC'], False, False], ['knocker', 11]]], [[[[], ['JS', '8H', '7C'], False, False], ['knocker', 25]], [[[], ['2C'], False, False], ['knocker', 2]], [[[], ['KD'], False, False], ['knocker', 10]], [[[], [], True, False], ['knocker', 25]], [[['TH', '3H', 'KC'], ['TD', 'AS', 'JD', '5H'], False, False], ['illegal', 0]], [[['TH', '8S'], ['QC', '5S', '2S', '6D'], False, False], ['illegal', 0]], [[[], ['4C', '7H'], True, True], ['knocker', 42]], [[['JS'], ['7D'], False, False], ['defender', 28]]], [[[[], ['8D', 'KC', '7C', 'AC'], False, False], ['knocker', 26]], [[['TH', '6C'], ['6D', 'QC'], False, False], ['illegal', 0]], [[['9C'], ['8S', 'JD'], False, False], ['knocker', 9]], [[['5H', '6D'], ['QH', '8D', 'KH'], False, False], ['illegal', 0]], [[['QC', 'JD'], ['7C', 'KH', '8C', '5S'], False, False], ['illegal', 0]], [[[], ['4D', '3S'], False, False], ['knocker', 7]], [[[], ['4S', 'AH', '8C', 'JS'], True, True], ['knocker', 54]], [[['KC'], ['8H', '2C', '9S', '6D'], False, False], ['knocker', 15]]]]
for i, (args, expected) in enumerate(fixtures[N-1]):
    check("knock 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 fixtureActualExpectedOutcome
knock score case 0['knocker', 45]['knocker', 45]Passed
knock score case 1['knocker', 19]['knocker', 19]Passed
knock score case 2['illegal', 0]['illegal', 0]Passed
knock score case 3['knocker', 21]['knocker', 21]Passed
knock score case 4['illegal', 0]['illegal', 0]Passed
knock score case 5['knocker', 32]['knocker', 32]Passed
knock score case 6['knocker', 39]['knocker', 39]Passed
knock score case 7['defender', 30]['defender', 30]Passed

SHA-256 / 23ac0d2c7be1a01d45eb90e4620af5e68f56c16548934b168a60dd2d2e4eff59

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.999348+00:00.

Case digest / d1a06c7d45ba07b0abc1a35e151d7b90720510247e48d65804cdceed15a39800