Utils
Duplicate scoring, vulnerability predicates, and engine.stats.
@workspace/bridge/utils/* maps one import path per file in src/utils/. Each file holds its own code and belongs to no rules module; the rules modules have their own sub-path (@workspace/bridge/auction, @workspace/bridge/hands, …, see Overview). utils/contract is documented in Format. Honour-point counting and PBN hand strings live in Hands.
scoring
Import from @workspace/bridge/utils/scoring. Implements the Laws of Duplicate Bridge scoring table from the declaring side's point of view. Honour bonuses (rubber bridge) are out of scope.
| Export | Signature | Description |
|---|---|---|
isScoringModeSet | (mode: ScoringMode) => mode is ScoringModeSet | true unless the mode is "UNSET". |
isScoringMode | (value: unknown) => value is ScoringMode | Exact membership in "TPP" | "IMP" | "UNSET"; no alias handling (see Parsers for that). |
contractScore | (level: number, denomination: Denomination, vulnerable: boolean) => number | Score of an undoubled contract that makes exactly. A tiebreaker for par computations, not a full scorer. |
duplicateScore | (contract: DisplayContract, tricksWon: number, vulnerable: boolean) => number | Full score of a played contract: positive when made, negative when down. |
computeDuplicateScore | (input: ComputeScoreInput) => number | Same computation with a flat input object. |
ComputeScoreInput | type | Input of computeDuplicateScore; doubling is a CounterStatus. |
Prop
Type
Values applied:
| Component | Non-vulnerable | Vulnerable |
|---|---|---|
| Trick points | minors 20, majors 30, NT 40 then 30 per trick; ×2 doubled, ×4 redoubled | same |
| Part-score bonus (under 100 trick points) | 50 | 50 |
| Game bonus (100 trick points or more) | 300 | 500 |
| Small slam / grand slam bonus | 500 / 1000 | 750 / 1500 |
| Insult bonus (doubled / redoubled contract made) | 50 / 100 | 50 / 100 |
| Overtrick, undoubled | trick value | trick value |
| Overtrick, doubled (×2 redoubled) | 100 | 200 |
| Undertricks, undoubled | 50 each | 100 each |
| Undertricks, doubled (×2 redoubled) | 100, then 200, 200, then 300 each | 200, then 300 each |
contractScore(2, "S", false); // => 110 (60 trick points + 50 part-score bonus)
contractScore(3, "NT", false); // => 400 (100 + 300 game bonus)
contractScore(6, "NT", true); // => 1440 (190 + 500 game + 750 small slam)
duplicateScore({ level: 4, suit: "S", doubled: "none" }, 10, false); // => 420
duplicateScore({ level: 4, suit: "S", doubled: "none" }, 8, true); // => -200
duplicateScore({ level: 2, suit: "S", doubled: "X" }, 8, false); // => 470 (120 + 300 + 50 insult)
duplicateScore({ level: 4, suit: "H", doubled: "XX" }, 11, true); // => 1480
computeDuplicateScore({
level: 4,
denomination: "S",
doubling: "X",
vulnerable: false,
tricks: 6,
}); // => -800 (down four, doubled, non-vulnerable)
isScoringMode("IMP"); // => true
isScoringMode("imp"); // => false (exact canonical values only)vulnerability
Import from @workspace/bridge/utils/vulnerability.
| Function | Signature | Description |
|---|---|---|
getPlayerVulnerabilityStatus | (player: Player, vulnerability: Vulnerability) => VulnerabilityStatus | "vulnerable", "non-vulnerable" or "unset" for one seat. |
isVulnerabilitySet | (vulnerability: Vulnerability) => vulnerability is VulnerabilitySet | true unless "UNSET". |
isVulnerability | (value: unknown) => value is Vulnerability | Exact membership in "UNSET" | "NONE" | "NS" | "EW" | "ALL". |
isSideVulnerable | (side: Partnership, vulnerability: Vulnerability) => boolean | Whether a partnership is at risk; false for "NONE" and "UNSET". |
getPlayerVulnerabilityStatus("N", "NS"); // => "vulnerable"
getPlayerVulnerabilityStatus("E", "NS"); // => "non-vulnerable"
getPlayerVulnerabilityStatus("E", "UNSET"); // => "unset"
isSideVulnerable("EW", "ALL"); // => true
isSideVulnerable("EW", "NS"); // => false
isVulnerability("ALL"); // => trueengine.stats
engine.stats groups the hand-evaluation and contract-classification helpers.
| Member | Signature | Description |
|---|---|---|
HCP_VALUES, THT_VALUES | constants | See Hands. |
computeHandHCP | (hand: HandData) => number | See Hands. |
honorComputers | Record<HonorMetric, (hand: HandData) => number> | HONOR_COMPUTERS. |
computeCardCounts | (hands: HandsData) => CardCounts | See Hands. |
determineContractLevel | (contract: string | null) => ContractLevel | "partial", "game", "small_slam" or "grand_slam" from a contract string; "partial" for null or invalid. Also in @workspace/bridge/auction. |
getPlayerVulnerabilityStatus | (player: Player, vulnerability: Vulnerability) => VulnerabilityStatus | See vulnerability. |
const north: HandData = { S: ["A", "K"], H: ["Q"], D: ["T", "9"], C: ["J"] };
const engine = createBridgeEngine(
createBridgeContext({ teachingMode: "auction", locale: "en" }),
);
engine.stats.computeHandHCP(north); // => 10
engine.stats.honorComputers.hcp(north); // => 10
engine.stats.determineContractLevel("4S"); // => "game"
engine.stats.determineContractLevel("6NT"); // => "small_slam"
engine.stats.getPlayerVulnerabilityStatus("S", "EW"); // => "non-vulnerable"