miaoda-game-bridge-rules
v0.1.2
Published
Engine-neutral four-player single-board Duplicate Contract Bridge rules with auction, dummy control, legal play, scoring, and safe views.
Maintainers
Readme
miaoda-game-bridge-rules
Immutable, engine-neutral rules for four-player single-board Duplicate Contract
Bridge profile wbf-duplicate-4p-single-board-v1. The package owns a normal
auction, declarer and dummy derivation, legal trick play, dummy control,
duplicate scoring, player-safe views, public events, and JSON restoration.
pnpm add miaoda-game-bridge-rulesPlay one board
import {
applyBridgeAction,
createBridgeGame,
createBridgePlayerView,
} from 'miaoda-game-bridge-rules';
let state = createBridgeGame({
playerIds: ['north', 'east', 'south', 'west'],
dealerId: 'north',
vulnerability: 'none',
seed: 2026,
});
const view = createBridgePlayerView(state, state.activeSeatId);
const call = view.legalActions.calls[0];
if (call) {
const result = applyBridgeAction(state, {
type: 'call',
playerId: view.viewerId,
call,
});
if (result.ok) state = result.state;
}Calls are pass, bids from one club through seven no-trump, double, and
redouble. Submit only calls listed by legalActions.calls.
Declarer and dummy
activeSeatId always identifies the seat whose hand must supply the next card.
Normally that seat submits its own play. When activeSeatId is the dummy, the
declarer submits the action with playerId set to the declarer and seatId set
to the dummy:
const declarerView = createBridgePlayerView(state, state.contract!.declarerId);
const cardId = declarerView.legalActions.playCardIds[0];
const result = applyBridgeAction(state, {
type: 'play-card',
playerId: declarerView.viewerId,
seatId: declarerView.legalActions.playSeatId!,
cardId,
});The dummy hand is absent from other player views until the opening lead is accepted. It then becomes public. Opponent hands and the authoritative deck snapshot never appear in a player view.
Claims and concessions
The active hand controller may submit a claim using one value from
legalActions.claimTrickCounts. The value is the number of all unresolved
tricks that the claimant's partnership proposes to win; 0 concedes every
unresolved trick.
Play pauses in claim-pending. When declarer claims, both defenders must submit
accept-claim; when a defender claims, declarer responds for the declaring
side. Full acceptance scores the agreed allocation immediately. Any authorized
reject-claim restores normal play at the exact suspended position. Use
canAcceptClaim and canRejectClaim to render response controls.
This protocol does not adjudicate a disputed claim. It records no proposed line of play and does not infer tricks from double-dummy information.
Scoring and boundaries
The completed result.scoreNorthSouth is the raw duplicate score from the
North-South perspective. It covers vulnerability, doubles/redoubles,
overtricks, undertricks, part-score/game bonuses, and slams.
Compare two team-table results with scoreInternationalMatchpoints(first,
second). Score a pairs field with scoreMatchpoints(scoresNorthSouth), which
returns standard 2/1/0 matchpoints, top, and normalized percentage per table.
The fixed single-board profile does not itself implement tournament movement,
Rubber Bridge, disputed-claim adjudication, irregular calls/plays, director
rulings, or bidding conventions. Use miaoda-game-bridge-tournament-core for complete odd-table
Mitchell pairs and head-to-head team aggregation. A separate safe-view baseline
bot is available from miaoda-game-bridge-strategy-core; it is not a
competitive bidding system or double-dummy solver. Pagat's Contract Bridge
summary and the WBF Laws of Duplicate Bridge are the source references.
Keep BridgeState on a trusted host. Use createBridgePlayerView for clients,
projectBridgeEvents for broadcast animation facts, and restoreBridgeState
for parsed snapshots.
