export type TractorSuit = 'clubs' | 'diamonds' | 'hearts' | 'spades'; export type TractorEffectiveSuit = TractorSuit | 'trump'; export type TractorRank = 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14; export type TractorLevel = TractorRank; /** One physical card from the fixed two-deck, four-Joker pack. */ export type TractorCard = { readonly id: string; readonly copy: 0 | 1; readonly suit: TractorSuit; readonly rank: TractorRank; } | { readonly id: string; readonly copy: 0 | 1; readonly joker: 'small' | 'big'; }; /** Trump facts fixed for one hand. `null` means a no-trump hand. */ export interface TractorTrump { readonly levelRank: TractorRank; readonly trumpSuit: TractorSuit | null; } /** Successful interpretation of a sequence of consecutive identical pairs. */ export interface TractorRun { readonly effectiveSuit: TractorEffectiveSuit; readonly pairCount: number; readonly cardCount: number; /** Pair face keys ordered from weakest to strongest. */ readonly orderedPairKeys: readonly string[]; readonly lowestStrength: number; readonly highestStrength: number; } export type TractorRunValidation = { readonly ok: true; readonly run: TractorRun; } | { readonly ok: false; readonly code: 'invalid-card' | 'duplicate-card' | 'too-short' | 'not-pairs' | 'mixed-effective-suit' | 'not-consecutive'; readonly message: string; }; /** Public structure followers must answer after a lead has been accepted. */ export interface TractorLeadPattern { readonly kind: 'single' | 'pair' | 'tractor' | 'top-set'; readonly effectiveSuit: TractorEffectiveSuit; readonly cardCount: number; /** Number of identical pairs followers must preserve as far as possible. */ readonly pairCount: number; } export type TractorLeadAnalysis = { readonly ok: true; readonly pattern: TractorLeadPattern; } | { readonly ok: false; readonly code: 'invalid-card' | 'duplicate-card' | 'mixed-effective-suit'; readonly message: string; }; /** Exact structural duty derived from a lead and the follower's complete hand. */ export interface TractorFollowObligation { readonly cardCount: number; readonly effectiveSuit: TractorEffectiveSuit; readonly availableSuitCount: number; readonly requiredSuitCount: number; readonly availablePairCount: number; readonly requiredPairCount: number; } export type TractorFollowValidation = { readonly ok: true; readonly obligation: TractorFollowObligation; } | { readonly ok: false; readonly code: 'invalid-card' | 'duplicate-card' | 'wrong-card-count' | 'card-not-in-hand' | 'must-follow-suit' | 'must-follow-pairs'; readonly message: string; readonly obligation?: TractorFollowObligation; }; /** One player's already-validated contribution to a multi-card trick. */ export interface TractorTrickPlay { readonly playerId: string; readonly cards: readonly TractorCard[]; } /** Explainable winner selection for an accepted multi-card trick. */ export interface TractorTrickWinner { readonly playerId: string; readonly playIndex: number; readonly winningEffectiveSuit: TractorEffectiveSuit; readonly winningStrength: number; } /** One single or identical-pair component extracted from a top-set attempt. */ export interface TractorTopSetComponent { readonly kind: 'single' | 'pair'; readonly cardIds: readonly string[]; readonly faceKey: string; readonly strength: number; } export type TractorTopSetResolution = { readonly ok: true; readonly status: 'accepted'; readonly pattern: TractorLeadPattern & { readonly kind: 'top-set'; }; } | { readonly ok: true; readonly status: 'challenged'; readonly attemptedPattern: TractorLeadPattern & { readonly kind: 'top-set'; }; readonly forcedLead: TractorTopSetComponent; readonly withdrawnCardIds: readonly string[]; readonly penaltyPoints: number; readonly challengerId: string; /** Higher same-suit singles or identical pairs the challenger may use. */ readonly beatingOptions: readonly (readonly string[])[]; } | { readonly ok: false; readonly code: 'invalid-card' | 'duplicate-card' | 'mixed-effective-suit' | 'not-top-set' | 'invalid-followers'; readonly message: string; }; export interface TractorHandSettlementInput { /** Point cards already won in ordinary tricks by the opponents. */ readonly opponentTrickPoints: number; /** Point value of the starter's eight hidden discards. */ readonly kittyPoints: number; readonly opponentsWonLastTrick: boolean; /** Number of cards in the final lead; ignored when declarers win that trick. */ readonly lastLeadCardCount: number; /** Signed transfer: declarer penalties are positive, opponent penalties negative. */ readonly opponentPenaltyDelta?: number; } export interface TractorHandSettlement { readonly opponentTrickPoints: number; readonly kittyPoints: number; readonly kittyMultiplier: number; readonly kittyAward: number; readonly opponentPenaltyDelta: number; readonly opponentTotalPoints: number; readonly nextDeclarers: 'declarers' | 'opponents'; readonly advancingTeam: 'declarers' | 'opponents'; readonly levelDelta: number; } export type TractorLevelAdvance = { readonly wonMatch: false; readonly level: TractorLevel; } | { readonly wonMatch: true; readonly level: TractorLevel; readonly excessSteps: number; }; export interface TractorTrumpDeclaration { readonly playerId: string; readonly kind: 'single-level' | 'pair-level' | 'joker-pair'; readonly cardIds: readonly string[]; readonly trumpSuit: TractorSuit | null; /** Pagat declaration tier: single=1, level pair=2, Joker pair=3. */ readonly strength: 1 | 2 | 3; } export type TractorTrumpDeclarationAnalysis = { readonly ok: true; readonly declaration: TractorTrumpDeclaration; } | { readonly ok: false; readonly code: 'invalid-player' | 'invalid-card' | 'duplicate-card' | 'not-declaration'; readonly message: string; }; export type TractorTrumpOverrideValidation = { readonly ok: true; } | { readonly ok: false; readonly code: 'self-override' | 'insufficient-strength' | 'already-no-trump'; readonly message: string; }; //# sourceMappingURL=types.d.ts.map