dsh-poker 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/LICENSE +21 -0
  3. package/README.md +56 -0
  4. package/cordis.patch.yml +3 -0
  5. package/lib/client.js +2419 -0
  6. package/lib/client.js.map +1 -0
  7. package/lib/index.js +3051 -0
  8. package/lib/index.js.map +1 -0
  9. package/lib/invariant-C3ZiFskw.js +13 -0
  10. package/lib/invariant-C3ZiFskw.js.map +1 -0
  11. package/lib/invariant.js +3 -0
  12. package/lib/join/card-back-landlord.png +0 -0
  13. package/lib/join/card-back.png +0 -0
  14. package/lib/join/face-a.png +0 -0
  15. package/lib/join/face-j.png +0 -0
  16. package/lib/join/face-k.png +0 -0
  17. package/lib/join/face-q.png +0 -0
  18. package/lib/join/index.html +13 -0
  19. package/lib/join/index.js +8910 -0
  20. package/lib/join/index.js.map +1 -0
  21. package/lib/join/join.css +14 -0
  22. package/lib/join/joker-black.png +0 -0
  23. package/lib/join/joker-red.png +0 -0
  24. package/lib/join/role-farmer-b.png +0 -0
  25. package/lib/join/role-farmer-c.png +0 -0
  26. package/lib/join/role-farmer.png +0 -0
  27. package/lib/join/role-landlord-b.png +0 -0
  28. package/lib/join/role-landlord.png +0 -0
  29. package/lib/join/role-spectator.png +0 -0
  30. package/lib/types/client/CardBack.d.ts +7 -0
  31. package/lib/types/client/CardBack.d.ts.map +1 -0
  32. package/lib/types/client/CardFace.d.ts +8 -0
  33. package/lib/types/client/CardFace.d.ts.map +1 -0
  34. package/lib/types/client/HostApp.d.ts +4 -0
  35. package/lib/types/client/HostApp.d.ts.map +1 -0
  36. package/lib/types/client/InviteDialog.d.ts +15 -0
  37. package/lib/types/client/InviteDialog.d.ts.map +1 -0
  38. package/lib/types/client/JoinForm.d.ts +13 -0
  39. package/lib/types/client/JoinForm.d.ts.map +1 -0
  40. package/lib/types/client/LobbyView.d.ts +34 -0
  41. package/lib/types/client/LobbyView.d.ts.map +1 -0
  42. package/lib/types/client/SeatAvatar.d.ts +18 -0
  43. package/lib/types/client/SeatAvatar.d.ts.map +1 -0
  44. package/lib/types/client/SeatRow.d.ts +8 -0
  45. package/lib/types/client/SeatRow.d.ts.map +1 -0
  46. package/lib/types/client/SettingsCard.d.ts +6 -0
  47. package/lib/types/client/SettingsCard.d.ts.map +1 -0
  48. package/lib/types/client/SettlementView.d.ts +6 -0
  49. package/lib/types/client/SettlementView.d.ts.map +1 -0
  50. package/lib/types/client/TableView.d.ts +16 -0
  51. package/lib/types/client/TableView.d.ts.map +1 -0
  52. package/lib/types/client/card-stack.d.ts +3 -0
  53. package/lib/types/client/card-stack.d.ts.map +1 -0
  54. package/lib/types/client/host-api.d.ts +50 -0
  55. package/lib/types/client/host-api.d.ts.map +1 -0
  56. package/lib/types/client/index.d.ts +5 -0
  57. package/lib/types/client/index.d.ts.map +1 -0
  58. package/lib/types/client/slots.d.ts +3 -0
  59. package/lib/types/client/slots.d.ts.map +1 -0
  60. package/lib/types/client/store.d.ts +15 -0
  61. package/lib/types/client/store.d.ts.map +1 -0
  62. package/lib/types/config.d.ts +76 -0
  63. package/lib/types/config.d.ts.map +1 -0
  64. package/lib/types/crypto.d.ts +9 -0
  65. package/lib/types/crypto.d.ts.map +1 -0
  66. package/lib/types/engine/cards.d.ts +22 -0
  67. package/lib/types/engine/cards.d.ts.map +1 -0
  68. package/lib/types/engine/deal.d.ts +11 -0
  69. package/lib/types/engine/deal.d.ts.map +1 -0
  70. package/lib/types/engine/hands.d.ts +16 -0
  71. package/lib/types/engine/hands.d.ts.map +1 -0
  72. package/lib/types/engine/index.d.ts +7 -0
  73. package/lib/types/engine/index.d.ts.map +1 -0
  74. package/lib/types/engine/play.d.ts +73 -0
  75. package/lib/types/engine/play.d.ts.map +1 -0
  76. package/lib/types/engine/random.d.ts +2 -0
  77. package/lib/types/engine/random.d.ts.map +1 -0
  78. package/lib/types/engine/score.d.ts +11 -0
  79. package/lib/types/engine/score.d.ts.map +1 -0
  80. package/lib/types/identity/player.d.ts +6 -0
  81. package/lib/types/identity/player.d.ts.map +1 -0
  82. package/lib/types/index.d.ts +8 -0
  83. package/lib/types/index.d.ts.map +1 -0
  84. package/lib/types/invariant.d.ts +9 -0
  85. package/lib/types/invariant.d.ts.map +1 -0
  86. package/lib/types/join/JoinApp.d.ts +2 -0
  87. package/lib/types/join/JoinApp.d.ts.map +1 -0
  88. package/lib/types/join/main.d.ts +2 -0
  89. package/lib/types/join/main.d.ts.map +1 -0
  90. package/lib/types/net/auth.d.ts +16 -0
  91. package/lib/types/net/auth.d.ts.map +1 -0
  92. package/lib/types/net/join-page.d.ts +6 -0
  93. package/lib/types/net/join-page.d.ts.map +1 -0
  94. package/lib/types/net/json.d.ts +3 -0
  95. package/lib/types/net/json.d.ts.map +1 -0
  96. package/lib/types/net/protocol.d.ts +2 -0
  97. package/lib/types/net/protocol.d.ts.map +1 -0
  98. package/lib/types/net/routes.d.ts +4 -0
  99. package/lib/types/net/routes.d.ts.map +1 -0
  100. package/lib/types/net/views.d.ts +3 -0
  101. package/lib/types/net/views.d.ts.map +1 -0
  102. package/lib/types/net/ws.d.ts +8 -0
  103. package/lib/types/net/ws.d.ts.map +1 -0
  104. package/lib/types/persist/domain.d.ts +104 -0
  105. package/lib/types/persist/domain.d.ts.map +1 -0
  106. package/lib/types/persist/schemas.d.ts +79 -0
  107. package/lib/types/persist/schemas.d.ts.map +1 -0
  108. package/lib/types/room/RoomManager.d.ts +142 -0
  109. package/lib/types/room/RoomManager.d.ts.map +1 -0
  110. package/lib/types/settle/audit.d.ts +15 -0
  111. package/lib/types/settle/audit.d.ts.map +1 -0
  112. package/lib/types/settle/escrow.d.ts +4 -0
  113. package/lib/types/settle/escrow.d.ts.map +1 -0
  114. package/lib/types/settle/grant.d.ts +7 -0
  115. package/lib/types/settle/grant.d.ts.map +1 -0
  116. package/lib/types/settle/ledger.d.ts +39 -0
  117. package/lib/types/settle/ledger.d.ts.map +1 -0
  118. package/lib/types/settle/math.d.ts +12 -0
  119. package/lib/types/settle/math.d.ts.map +1 -0
  120. package/lib/types/types.d.ts +253 -0
  121. package/lib/types/types.d.ts.map +1 -0
  122. package/package.json +116 -0
  123. package/prepare.mjs +16 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["parsed: URL","RANK_VALUE: Record<string, number>","cards: CardId[]","picked: Rank[]","wilds: CardId[]","naturals: CardId[]","combos: LegalCombo[]","found: ClassifiedHand[]","best: ClassifiedHand | null","hand: ClassifiedHand","runs: Rank[][]","leftover: CardId[]","bombs: CardId[][]","out: CardId[][]","taken: CardId[]","play: Play","z","record: GrantRecord","domain: DomainLike","welcomeAtoms: bigint","created: PlayerRecord","entry: LedgerRecord","ctx: { logger?: { warn(message: unknown): void; info(message: unknown): void }; webServer?: { port?: number } } | null","seatCount: SeatCount","room: Room","live: LiveRoom","seat: Seat | null","events: ServerEvent[]","name","publicSettlement: PublicSettlement","out: Partial<Record<Seat, CardId[]>>","totals: Record<string, number>","remaining: Record<string, number>","out: Record<string, string>","name","name","session","chunks: Buffer[]","headers: Record<string, string | string[]>","send","state: SocketState","command: ClientCommand"],"sources":["../src/settle/math.ts","../src/config.ts","../src/crypto.ts","../src/types.ts","../src/engine/cards.ts","../src/engine/random.ts","../src/engine/deal.ts","../src/engine/hands.ts","../src/engine/play.ts","../src/engine/score.ts","../src/identity/player.ts","../src/persist/schemas.ts","../src/persist/domain.ts","../src/settle/audit.ts","../src/settle/grant.ts","../src/settle/ledger.ts","../src/room/RoomManager.ts","../src/net/auth.ts","../src/net/json.ts","../src/net/join-page.ts","../src/net/routes.ts","../src/net/ws.ts","../src/index.ts"],"sourcesContent":["export const STAKE_LADDER_M = [1, 2, 5, 10, 20, 50, 100] as const\nexport const DEFAULT_STAKE_M = 1\nexport const DEFAULT_MAX_MULTIPLIER = 8\nexport const DEFAULT_WELCOME_ATOMS = 200_000_000n\nexport const ATOMS_PER_M = 1_000_000n\nexport const ATOMS_PER_B = 1_000_000_000n\n\nexport function stakeAtomsFromM(stakeM: number): bigint {\n return BigInt(stakeM) * ATOMS_PER_M\n}\n\nexport function perFarmerCapAtoms(stakeAtoms: bigint, maxMultiplier: number): bigint {\n return stakeAtoms * 3n * BigInt(maxMultiplier) * 2n\n}\n\nexport function seatCapAtoms(stakeAtoms: bigint, maxMultiplier: number, seatCount: 3 | 4 = 3): bigint {\n const farmers = BigInt(seatCount - 1)\n return farmers * perFarmerCapAtoms(stakeAtoms, maxMultiplier)\n}\n\n/** Display token atoms as AI-token units: M (million) or B (billion). Never dump raw zeros. */\nexport function formatM(atoms: bigint): string {\n const sign = atoms < 0n ? '-' : ''\n const abs = atoms < 0n ? -atoms : atoms\n if (abs >= ATOMS_PER_B) return `${sign}${formatScaled(abs, ATOMS_PER_B)}B`\n return `${sign}${formatScaled(abs, ATOMS_PER_M)}M`\n}\n\nfunction formatScaled(abs: bigint, unit: bigint): string {\n const whole = abs / unit\n const rem = abs % unit\n if (rem === 0n) return whole.toString()\n const decimals = unit.toString().length - 1\n const frac = rem.toString().padStart(decimals, '0').replace(/0+$/, '')\n return `${whole.toString()}.${frac}`\n}\n","import z from '@deepseek-ai/schemastery'\nimport { seatCapAtoms, STAKE_LADDER_M } from './settle/math.ts'\n\nexport const STAKE_LADDER = STAKE_LADDER_M\nexport const DEFAULT_STAKE_M = 1\nexport const DEFAULT_MAX_MULTIPLIER = 8\nexport const DEFAULT_WELCOME_ATOMS = '200000000'\nexport const DEFAULT_SEAT_COUNT = 3\nexport const DEFAULT_TURN_TIMEOUT_MS = 120_000\nexport const MAX_MULTIPLIER_OPTIONS = [8, 16, 32, 64] as const\nexport const SEAT_COUNT_OPTIONS = [3, 4] as const\n\nexport interface PluginConfig {\n enabled?: boolean\n welcomeAtoms?: string\n defaultStakeM?: number\n defaultMaxMultiplier?: number\n defaultSeatCount?: 3 | 4\n defaultLaiZi?: boolean\n publicBaseUrl?: string\n maxRooms?: number\n allowSpectators?: boolean\n spectatorCardCounter?: boolean\n houseEnabled?: boolean\n reconnectWindowMs?: number\n turnTimeoutMs?: number\n doubleWindowMs?: number\n heartbeatMs?: number\n disconnectAfterMs?: number\n retainClosedRoomsHours?: number\n inviteTtlHours?: number\n routePrefix?: string\n}\n\nexport const Config = z.object({\n enabled: z.boolean().default(true),\n welcomeAtoms: z.string().default(DEFAULT_WELCOME_ATOMS),\n defaultStakeM: z.number().default(DEFAULT_STAKE_M),\n defaultMaxMultiplier: z.number().default(DEFAULT_MAX_MULTIPLIER),\n defaultSeatCount: z.union([z.const(3), z.const(4)]).default(DEFAULT_SEAT_COUNT),\n defaultLaiZi: z.boolean().default(false),\n publicBaseUrl: z.string().default(''),\n maxRooms: z.number().default(32),\n allowSpectators: z.boolean().default(true),\n spectatorCardCounter: z.boolean().default(false),\n houseEnabled: z.boolean().default(false),\n reconnectWindowMs: z.number().default(120_000),\n turnTimeoutMs: z.number().default(DEFAULT_TURN_TIMEOUT_MS),\n doubleWindowMs: z.number().default(8_000),\n heartbeatMs: z.number().default(15_000),\n disconnectAfterMs: z.number().default(60_000),\n retainClosedRoomsHours: z.number().default(24),\n inviteTtlHours: z.number().default(24),\n routePrefix: z.string().default('/doudizhu'),\n})\n\nexport interface ResolvedConfig {\n enabled: boolean\n welcomeAtoms: bigint\n defaultStakeM: number\n defaultMaxMultiplier: number\n defaultSeatCount: 3 | 4\n defaultLaiZi: boolean\n publicBaseUrl: string\n maxRooms: number\n allowSpectators: boolean\n spectatorCardCounter: boolean\n houseEnabled: boolean\n reconnectWindowMs: number\n turnTimeoutMs: number\n doubleWindowMs: number\n heartbeatMs: number\n disconnectAfterMs: number\n retainClosedRoomsHours: number\n inviteTtlHours: number\n routePrefix: string\n}\n\nexport function resolveConfig(config: PluginConfig): ResolvedConfig {\n const welcomeAtoms = parseWelcome(config.welcomeAtoms ?? DEFAULT_WELCOME_ATOMS)\n const defaultStakeM = config.defaultStakeM ?? DEFAULT_STAKE_M\n const defaultMaxMultiplier = config.defaultMaxMultiplier ?? DEFAULT_MAX_MULTIPLIER\n return {\n enabled: config.enabled !== false,\n welcomeAtoms,\n defaultStakeM,\n defaultMaxMultiplier,\n defaultSeatCount: config.defaultSeatCount === 4 ? 4 : 3,\n defaultLaiZi: config.defaultLaiZi === true,\n publicBaseUrl: (config.publicBaseUrl ?? '').replace(/\\/+$/, ''),\n maxRooms: config.maxRooms ?? 32,\n allowSpectators: config.allowSpectators !== false,\n spectatorCardCounter: config.spectatorCardCounter === true,\n houseEnabled: false,\n reconnectWindowMs: config.reconnectWindowMs ?? 120_000,\n turnTimeoutMs: config.turnTimeoutMs ?? DEFAULT_TURN_TIMEOUT_MS,\n doubleWindowMs: config.doubleWindowMs ?? 8_000,\n heartbeatMs: config.heartbeatMs ?? 15_000,\n disconnectAfterMs: config.disconnectAfterMs ?? 60_000,\n retainClosedRoomsHours: config.retainClosedRoomsHours ?? 24,\n inviteTtlHours: config.inviteTtlHours ?? 24,\n routePrefix: normalizePrefix(config.routePrefix ?? '/doudizhu'),\n }\n}\n\nexport function parseWelcome(value: string): bigint {\n if (!/^\\d+$/.test(value)) throw new Error('welcomeAtoms must be a non-negative decimal string')\n return BigInt(value)\n}\n\nexport function validatePublicBaseUrl(value: string): string {\n if (value === '') return ''\n let parsed: URL\n try {\n parsed = new URL(value)\n } catch {\n throw new Error('publicBaseUrl must be an absolute http(s) origin')\n }\n if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') {\n throw new Error('publicBaseUrl must be http or https')\n }\n if (parsed.origin !== value.replace(/\\/+$/, '')) {\n throw new Error('publicBaseUrl must be an origin with no path, query, or trailing slash')\n }\n return parsed.origin\n}\n\nexport function assertCreateEconomy(\n welcomeAtoms: bigint,\n stakeM: number,\n maxMultiplier: number,\n seatCount: 3 | 4 = 3,\n): void {\n if (!STAKE_LADDER_M.includes(stakeM as typeof STAKE_LADDER_M[number])) {\n throw new Error(`stakeM must be one of ${STAKE_LADDER_M.join(',')}`)\n }\n if (!(MAX_MULTIPLIER_OPTIONS as readonly number[]).includes(maxMultiplier)) {\n throw new Error(`maxMultiplier must be one of ${MAX_MULTIPLIER_OPTIONS.join(',')}`)\n }\n if (seatCount !== 3 && seatCount !== 4) {\n throw new Error('seatCount must be 3 or 4')\n }\n const cap = seatCapAtoms(BigInt(stakeM) * 1_000_000n, maxMultiplier, seatCount)\n if (welcomeAtoms < cap) {\n throw failWelcome()\n }\n}\n\nfunction failWelcome(): Error {\n return Object.assign(new Error('welcome-below-seatcap'), { name: 'welcome-below-seatcap', code: 'insufficient' })\n}\n\nfunction normalizePrefix(prefix: string): string {\n if (!prefix.startsWith('/')) return `/${prefix.replace(/\\/+$/, '')}`\n return prefix.replace(/\\/+$/, '') || '/doudizhu'\n}\n","import { createHash, randomBytes } from 'node:crypto'\n\nexport function randomId(prefix: string): string {\n return `${prefix}_${randomBytes(10).toString('hex')}`\n}\n\nexport function randomRoomCode(): string {\n const n = randomBytes(4).readUInt32BE(0) % 1_000_000\n return n.toString().padStart(6, '0')\n}\n\nexport function randomToken(): string {\n return randomBytes(32).toString('base64url').replace(/=+$/, '')\n}\n\nexport function sha256Hex(value: string): string {\n return createHash('sha256').update(value).digest('hex')\n}\n\nexport function inviteHash(roomSecret: string, domain: 'sit' | 'watch', token: string): string {\n return sha256Hex(`${roomSecret}|${domain}|${token}`)\n}\n\nexport function cookieValue(): string {\n return randomBytes(24).toString('base64url')\n}\n\nexport function nowIso(date = new Date()): string {\n return date.toISOString()\n}\n\nexport function addHours(hours: number, date = new Date()): string {\n return new Date(date.getTime() + hours * 3600_000).toISOString()\n}\n","export type RoomId = string & { readonly __brand: 'RoomId' }\nexport type PlayerId = string & { readonly __brand: 'PlayerId' }\nexport type HandId = string & { readonly __brand: 'HandId' }\nexport type GrantId = string & { readonly __brand: 'GrantId' }\nexport type SettlementId = string & { readonly __brand: 'SettlementId' }\nexport type EntryId = string & { readonly __brand: 'EntryId' }\nexport type CardId = string & { readonly __brand: 'CardId' }\nexport type SeatCount = 3 | 4\nexport type Seat = 0 | 1 | 2 | 3\nexport type GameMode = 'classic' | 'laizi'\nexport type TokenAtomString = string & { readonly __atom: unique symbol }\n\nexport type RoomPhase =\n | 'waiting' | 'dealing' | 'bidding' | 'doubling'\n | 'playing' | 'settling' | 'void' | 'closed'\n\nexport type HandType =\n | 'solo' | 'pair' | 'trio' | 'trioSolo' | 'trioPair'\n | 'seq' | 'seqPair' | 'plane' | 'planeSolo' | 'planePair'\n | 'fourDualSolo' | 'fourDualPair' | 'bomb' | 'rocket' | 'pass'\n\nexport type BidScore = number\nexport type BidAction = 'pass' | 'call' | 'rob'\nexport type DoubleAction = 'pass' | 'double' | 'reDouble'\nexport type AuctionKind = 'call' | 'rob'\n\nexport type RejectCode =\n | 'illegal' | 'not-your-turn' | 'phase' | 'auth' | 'duplicate-nonce'\n | 'insufficient' | 'room-full' | 'expired'\n\nexport interface Player {\n readonly playerId: PlayerId\n readonly displayName: string\n readonly avatarUrl: string | null\n readonly createdAt: string\n}\n\nexport interface SeatState {\n readonly seat: Seat\n readonly playerId: PlayerId | null\n readonly displayName: string | null\n readonly avatarUrl: string | null\n readonly ready: boolean\n readonly connected: boolean\n readonly role: 'empty' | 'farmer' | 'landlord'\n readonly grantId: GrantId | null\n readonly cardsLeft: number\n}\n\nexport interface Room {\n readonly roomId: RoomId\n readonly roomCode: string\n readonly title: string\n readonly hostPlayerId: PlayerId\n readonly phase: RoomPhase\n readonly stakeAtoms: bigint\n readonly maxMultiplier: number\n readonly seatCount: SeatCount\n readonly laiZi: boolean\n readonly seats: readonly SeatState[]\n readonly spectatorIds: readonly PlayerId[]\n readonly currentHandId: HandId | null\n readonly createdAt: string\n readonly inviteExpiresAt: string\n readonly shareable: boolean\n}\n\n/** Wire copy of Room: atoms are decimal strings so JSON/WS stay serializable. */\nexport interface RoomView extends Omit<Room, 'stakeAtoms'> {\n readonly stakeAtoms: TokenAtomString\n}\n\nexport interface Play {\n readonly seat: Seat\n readonly type: HandType\n readonly cards: readonly CardId[]\n readonly seq: number\n readonly ts: string\n}\n\nexport interface Hand {\n readonly handId: HandId\n readonly roomId: RoomId\n readonly dealerSeat: Seat\n readonly bottom: readonly CardId[]\n readonly hands: readonly CardId[][]\n readonly bid: BidScore\n readonly landlordSeat: Seat | null\n readonly farmerDoubledBySeat: Partial<Record<Seat, number>>\n readonly landlordReDouble: number\n readonly mingPaiBySeat: Partial<Record<Seat, boolean>>\n readonly mingPaiMult: number\n readonly bombCount: number\n readonly rocketCount: number\n readonly spring: 'none' | 'spring' | 'anti'\n readonly table: readonly Play[]\n readonly turnSeat: Seat\n readonly leadSeat: Seat\n readonly laiZiRanks: readonly string[]\n}\n\nexport interface LegalCombo {\n readonly type: Exclude<HandType, 'pass'>\n readonly cards: readonly CardId[]\n}\n\nexport interface PlayerView {\n readonly room: RoomView\n readonly you: {\n readonly playerId: PlayerId\n readonly seat: Seat | null\n readonly spectator: boolean\n readonly cards: CardId[]\n }\n readonly publicHands: readonly number[]\n readonly lastPlays: readonly Play[]\n readonly bottom: readonly CardId[] | null\n readonly laiZiRanks: readonly string[]\n readonly bid: BidScore\n readonly auction: { readonly kind: AuctionKind; readonly multiplier: number } | null\n readonly revealedBySeat: Partial<Record<Seat, readonly CardId[]>>\n readonly mingPaiBySeat: Partial<Record<Seat, boolean>>\n readonly turnSeat: Seat | null\n readonly leadSeat: Seat | null\n readonly deadlineAt: string | null\n readonly yourAvailableAtoms: TokenAtomString\n readonly yourEscrowAtoms: TokenAtomString\n readonly legal: { readonly canPass: boolean; readonly combos: readonly LegalCombo[] }\n readonly remainingRanks: Readonly<Record<string, number>> | null\n readonly chat: readonly ChatLine[]\n}\n\nexport interface ChatLine {\n readonly playerId: PlayerId\n readonly displayName: string\n readonly text: string\n readonly ts: string\n}\n\nexport interface Settlement {\n readonly settlementId: SettlementId\n readonly handId: HandId\n readonly winner: 'landlord' | 'farmers'\n readonly unitAtoms: TokenAtomString\n readonly deltas: readonly { seat: Seat; playerId: PlayerId; atoms: TokenAtomString }[]\n readonly rakeAtoms: TokenAtomString\n readonly formula: string\n}\n\nexport interface PublicSettlement {\n readonly settlementId: SettlementId\n readonly handId: HandId\n readonly winner: 'landlord' | 'farmers'\n readonly unitAtoms: TokenAtomString\n readonly deltas: Settlement['deltas']\n readonly rakeAtoms: TokenAtomString\n readonly formula: string\n readonly trustNote: string\n}\n\nexport interface TokenGrant {\n readonly grantId: GrantId\n readonly playerId: PlayerId\n readonly roomId: RoomId\n readonly maxExposureAtoms: TokenAtomString\n readonly issuedAt: string\n readonly expiresAt: string\n readonly source: 'host-welcome'\n readonly issuedBy: 'room-host'\n}\n\nexport type ClientCommand =\n | { type: 'sit'; seat: Seat; displayName: string }\n | { type: 'stand' }\n | { type: 'ready'; ready: boolean }\n | { type: 'bid'; action: BidAction; score?: BidScore }\n | { type: 'double'; action: DoubleAction }\n | { type: 'mingPai' }\n | { type: 'rename'; title: string }\n | { type: 'play'; cards: CardId[]; nonce: string }\n | { type: 'pass'; nonce: string }\n | { type: 'chat'; text: string }\n | { type: 'rematch' }\n | { type: 'hostKick'; playerId: PlayerId }\n | { type: 'hostClose' }\n | { type: 'ping' }\n\nexport type ServerEvent =\n | { type: 'snapshot'; seq: number; view: PlayerView }\n | { type: 'reject'; nonce?: string; code: RejectCode; reason: string }\n | { type: 'settled'; seq: number; settlement: PublicSettlement }\n | { type: 'pong'; ts: number }\n\nexport class CommandError extends Error {\n readonly code: RejectCode\n constructor(code: RejectCode, reason: string) {\n super(reason)\n this.code = code\n this.name = 'CommandError'\n }\n}\n\nexport function asTokenAtomString(value: bigint | number | string): TokenAtomString {\n return String(value) as TokenAtomString\n}\n\nexport function parseAtoms(value: string): bigint {\n if (!/^-?\\d+$/.test(value)) throw new Error(`invalid atom string: ${value}`)\n return BigInt(value)\n}\n\nexport function asCardId(value: string): CardId {\n return value as CardId\n}\n\nexport function asPlayerId(value: string): PlayerId {\n return value as PlayerId\n}\n\nexport function asRoomId(value: string): RoomId {\n return value as RoomId\n}\n\nexport function asHandId(value: string): HandId {\n return value as HandId\n}\n\nexport function asGrantId(value: string): GrantId {\n return value as GrantId\n}\n\nexport function asSettlementId(value: string): SettlementId {\n return value as SettlementId\n}\n\nexport function asEntryId(value: string): EntryId {\n return value as EntryId\n}\n\nexport function nextSeat(seat: Seat, count: number = 3): Seat {\n return ((seat + 1) % count) as Seat\n}\n\nexport function prevSeat(seat: Seat, count: number = 3): Seat {\n return ((seat + count - 1) % count) as Seat\n}\n\nexport function isSeat(value: unknown, count: number = 4): value is Seat {\n return typeof value === 'number' && Number.isInteger(value) && value >= 0 && value < count\n}\n\nexport function allSeats(count: SeatCount): Seat[] {\n return Array.from({ length: count }, (_, index) => index as Seat)\n}\n\nexport function decksFor(count: SeatCount): 1 | 2 {\n return count === 3 ? 1 : 2\n}\n\nexport function holeCount(count: SeatCount): number {\n return count === 3 ? 3 : 8\n}\n\nexport function dealtHandSize(count: SeatCount): number {\n return count === 3 ? 17 : 25\n}\n\nexport function landlordHandSize(count: SeatCount): number {\n return dealtHandSize(count) + holeCount(count)\n}\n","import { asCardId, type CardId } from '../types.ts'\n\nexport const SUITS = ['S', 'H', 'C', 'D'] as const\nexport const RANK_ORDER = [\n '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A', '2', 'BJ', 'RJ',\n] as const\n\nexport type Suit = typeof SUITS[number]\nexport type Rank = typeof RANK_ORDER[number]\n\nconst RANK_VALUE: Record<string, number> = Object.fromEntries(\n RANK_ORDER.map((rank, index) => [rank, index]),\n)\n\nexport function cardBase(card: CardId | string): string {\n return String(card).split('~')[0] ?? String(card)\n}\n\nexport function rankOf(card: CardId): Rank {\n const raw = cardBase(card)\n if (raw === 'BJ' || raw === 'RJ') return raw\n if (raw.startsWith('S') || raw.startsWith('H') || raw.startsWith('C') || raw.startsWith('D')) {\n return raw.slice(1) as Rank\n }\n throw new Error(`invalid card: ${raw}`)\n}\n\nexport function suitOf(card: CardId): Suit | null {\n const raw = cardBase(card)\n if (raw === 'BJ' || raw === 'RJ') return null\n return raw[0] as Suit\n}\n\nexport function rankValue(rank: string): number {\n const value = RANK_VALUE[rank]\n if (value === undefined) throw new Error(`invalid rank: ${rank}`)\n return value\n}\n\nexport function cardValue(card: CardId): number {\n return rankValue(rankOf(card))\n}\n\nexport function compareCards(a: CardId, b: CardId): number {\n const byRank = cardValue(a) - cardValue(b)\n if (byRank !== 0) return byRank\n return a.localeCompare(b)\n}\n\nexport function sortCards(cards: readonly CardId[]): CardId[] {\n return [...cards].sort(compareCards)\n}\n\nexport function fullDeck(copy = 0): CardId[] {\n const suffix = copy === 0 ? '' : `~${copy}`\n const cards: CardId[] = []\n for (const suit of SUITS) {\n for (const rank of RANK_ORDER) {\n if (rank === 'BJ' || rank === 'RJ') continue\n cards.push(asCardId(`${suit}${rank}${suffix}`))\n }\n }\n cards.push(asCardId(`BJ${suffix}`), asCardId(`RJ${suffix}`))\n return cards\n}\n\nexport function stackedDeck(decks: 1 | 2): CardId[] {\n const cards: CardId[] = []\n for (let copy = 0; copy < decks; copy += 1) cards.push(...fullDeck(copy))\n return cards\n}\n\nexport const WILDABLE_RANKS = RANK_ORDER.filter((rank) => rank !== 'BJ' && rank !== 'RJ')\n\nexport function isJokerRank(rank: Rank): boolean {\n return rank === 'BJ' || rank === 'RJ'\n}\n\nexport function isWildCard(card: CardId, wildRanks: readonly string[]): boolean {\n if (wildRanks.length === 0) return false\n const rank = rankOf(card)\n return !isJokerRank(rank) && wildRanks.includes(rank)\n}\n\nexport function shuffle<T>(items: readonly T[], random: () => number): T[] {\n const next = [...items]\n for (let i = next.length - 1; i > 0; i -= 1) {\n const j = Math.floor(random() * (i + 1))\n const a = next[i]\n const b = next[j]\n if (a === undefined || b === undefined) continue\n next[i] = b\n next[j] = a\n }\n return next\n}\n\nexport function parseCardId(raw: string): CardId | null {\n if (/^(BJ|RJ)(?:~\\d+)?$/.test(raw)) return asCardId(raw)\n const match = /^(S|H|C|D)(10|[3-9JQKA2])(?:~\\d+)?$/.exec(raw)\n return match ? asCardId(raw) : null\n}\n\nexport function countRanks(cards: readonly CardId[]): Map<Rank, CardId[]> {\n const groups = new Map<Rank, CardId[]>()\n for (const card of cards) {\n const rank = rankOf(card)\n const bucket = groups.get(rank)\n if (bucket) bucket.push(card)\n else groups.set(rank, [card])\n }\n return groups\n}\n\nexport function isNaturalSeqRank(rank: Rank): boolean {\n return rank !== '2' && rank !== 'BJ' && rank !== 'RJ'\n}\n","import { randomBytes } from 'node:crypto'\n\nexport function cryptoUnit(): number {\n const buf = randomBytes(4)\n return buf.readUInt32BE(0) / 0x1_0000_0000\n}\n","import { asCardId, holeCount, type CardId, type Seat, type SeatCount } from '../types.ts'\nimport { stackedDeck, shuffle, WILDABLE_RANKS, type Rank } from './cards.ts'\nimport { cryptoUnit } from './random.ts'\n\nexport interface Deal {\n readonly hands: readonly CardId[][]\n readonly bottom: readonly CardId[]\n}\n\nexport function dealCards(seatCount: SeatCount = 3, random: () => number = cryptoUnit): Deal {\n const decks = seatCount === 3 ? 1 : 2\n const deck = shuffle(stackedDeck(decks), random)\n const hole = holeCount(seatCount)\n const per = (deck.length - hole) / seatCount\n const hands = Array.from({ length: seatCount }, (_, index) => (\n deck.slice(index * per, (index + 1) * per).map(asCardId)\n ))\n return {\n hands,\n bottom: deck.slice(seatCount * per, seatCount * per + hole).map(asCardId),\n }\n}\n\nexport function giveBottom(hands: readonly CardId[][], bottom: readonly CardId[], landlord: Seat): CardId[][] {\n return hands.map((hand, index) => (index === landlord ? [...hand, ...bottom] : [...hand]))\n}\n\nexport function randomSeat(count: SeatCount = 3, random: () => number = cryptoUnit): Seat {\n return Math.floor(random() * count) as Seat\n}\n\nexport function pickLaiZiRanks(count: 1 | 2, random: () => number = cryptoUnit, exclude: readonly string[] = []): Rank[] {\n const pool = WILDABLE_RANKS.filter((rank) => !exclude.includes(rank))\n const picked: Rank[] = []\n const available = [...pool]\n for (let i = 0; i < count && available.length > 0; i += 1) {\n const index = Math.floor(random() * available.length)\n const rank = available.splice(index, 1)[0]\n if (rank) picked.push(rank)\n }\n return picked\n}\n","import type { CardId, HandType, LegalCombo } from '../types.ts'\nimport { asCardId } from '../types.ts'\nimport {\n cardValue, countRanks, isJokerRank, isNaturalSeqRank, isWildCard, rankOf, rankValue, sortCards,\n WILDABLE_RANKS, type Rank,\n} from './cards.ts'\n\nexport interface ClassifiedHand {\n readonly type: Exclude<HandType, 'pass'>\n readonly cards: readonly CardId[]\n readonly key: number\n readonly length: number\n readonly wingLength: number\n readonly wild?: boolean\n}\n\nexport function classify(cards: readonly CardId[], laiZiRanks: readonly string[] = []): ClassifiedHand | null {\n if (laiZiRanks.length === 0) return classifyNatural(cards)\n const wilds: CardId[] = []\n const naturals: CardId[] = []\n for (const card of cards) {\n if (isWildCard(card, laiZiRanks)) wilds.push(card)\n else naturals.push(card)\n }\n if (wilds.length === 0) return classifyNatural(cards)\n return classifyWithWilds(cards, naturals, wilds.length)\n}\n\nfunction classifyNatural(cards: readonly CardId[]): ClassifiedHand | null {\n const sorted = sortCards(cards)\n if (sorted.length === 0) return null\n const ranks = countRanks(sorted)\n const entries = [...ranks.entries()].sort((a, b) => rankValue(a[0]) - rankValue(b[0]))\n\n if (jokerCount(ranks) === sorted.length && sorted.length >= 2) {\n return { type: 'rocket', cards: sorted, key: rankValue('RJ'), length: sorted.length, wingLength: 0 }\n }\n if (sorted.length === 1) {\n return { type: 'solo', cards: sorted, key: cardValue(sorted[0]!), length: 1, wingLength: 0 }\n }\n if (sorted.length === 2 && entries.length === 1) {\n return { type: 'pair', cards: sorted, key: rankValue(entries[0]![0]), length: 2, wingLength: 0 }\n }\n if (sorted.length === 3 && entries.length === 1) {\n return { type: 'trio', cards: sorted, key: rankValue(entries[0]![0]), length: 3, wingLength: 0 }\n }\n if (sorted.length >= 4 && entries.length === 1) {\n return { type: 'bomb', cards: sorted, key: rankValue(entries[0]![0]), length: sorted.length, wingLength: 0 }\n }\n\n const trioSolo = classifyTrioSolo(sorted, entries)\n if (trioSolo) return trioSolo\n const trioPair = classifyTrioPair(sorted, entries)\n if (trioPair) return trioPair\n const seq = classifySeq(sorted, entries)\n if (seq) return seq\n const seqPair = classifySeqPair(sorted, entries)\n if (seqPair) return seqPair\n const plane = classifyBarePlane(sorted, entries)\n if (plane) return plane\n const planePair = classifyPlanePair(sorted, entries)\n if (planePair) return planePair\n const planeSolo = classifyPlaneSolo(sorted, entries)\n if (planeSolo) return planeSolo\n const fourDualPair = classifyFourDualPair(sorted, entries)\n if (fourDualPair) return fourDualPair\n const fourDualSolo = classifyFourDualSolo(sorted, entries)\n if (fourDualSolo) return fourDualSolo\n return null\n}\n\nexport function beats(prev: ClassifiedHand, next: ClassifiedHand, decks: 1 | 2 = 1): boolean {\n const nextBomb = next.type === 'bomb' || next.type === 'rocket'\n const prevBomb = prev.type === 'bomb' || prev.type === 'rocket'\n if (next.type === 'rocket' && decks === 1) return prev.type !== 'rocket'\n if (prev.type === 'rocket' && decks === 1) return false\n if (nextBomb && prevBomb) {\n if (next.length !== prev.length) return next.length > prev.length\n if (!!next.wild !== !!prev.wild) return !next.wild && !!prev.wild\n if (next.type !== prev.type) return next.type === 'rocket'\n return next.key > prev.key\n }\n if (nextBomb) return true\n if (prevBomb) return false\n if (next.type !== prev.type || next.length !== prev.length || next.wingLength !== prev.wingLength) {\n return false\n }\n return next.key > prev.key\n}\n\nexport function enumerateLegal(\n hand: readonly CardId[],\n lead: ClassifiedHand | null,\n laiZiRanks: readonly string[] = [],\n decks: 1 | 2 = 1,\n): LegalCombo[] {\n const sorted = sortCards(hand)\n const combos: LegalCombo[] = []\n const seen = new Set<string>()\n const push = (type: Exclude<HandType, 'pass'>, cards: readonly CardId[]): void => {\n const key = `${type}:${sortCards(cards).join(',')}`\n if (seen.has(key)) return\n seen.add(key)\n combos.push({ type, cards: sortCards(cards) })\n }\n\n if (lead === null) {\n for (const subset of generateCandidates(sorted)) {\n const classified = classify(subset, laiZiRanks)\n if (classified) push(classified.type, classified.cards)\n }\n return capCombos(combos)\n }\n\n if (!(lead.type === 'rocket' && decks === 1)) {\n for (const bomb of findBombs(sorted, laiZiRanks)) {\n const classified = classify(bomb, laiZiRanks)\n if (classified && beats(lead, classified, decks)) push(classified.type, classified.cards)\n }\n const rocket = sorted.filter((card) => isJokerRank(rankOf(card)))\n if (rocket.length >= 2) push('rocket', rocket)\n }\n\n for (const subset of generateFollowCandidates(sorted, lead)) {\n const classified = classify(subset, laiZiRanks)\n if (classified && beats(lead, classified, decks)) push(classified.type, classified.cards)\n }\n return capCombos(combos)\n}\n\nexport function smallestSolo(hand: readonly CardId[]): CardId | null {\n const sorted = sortCards(hand)\n return sorted[0] ?? null\n}\n\nfunction classifyTrioSolo(\n cards: readonly CardId[],\n entries: readonly [Rank, CardId[]][],\n): ClassifiedHand | null {\n if (cards.length !== 4) return null\n const trio = entries.find(([, group]) => group.length === 3)\n const kicker = entries.find(([, group]) => group.length === 1)\n if (!trio || !kicker) return null\n if (kicker[0] === 'BJ' && entries.some(([rank]) => rank === 'RJ')) return null\n if (kicker[0] === 'RJ' && entries.some(([rank]) => rank === 'BJ')) return null\n return { type: 'trioSolo', cards, key: rankValue(trio[0]), length: 4, wingLength: 1 }\n}\n\nfunction classifyTrioPair(\n cards: readonly CardId[],\n entries: readonly [Rank, CardId[]][],\n): ClassifiedHand | null {\n if (cards.length !== 5) return null\n const trio = entries.find(([, group]) => group.length === 3)\n const pair = entries.find(([, group]) => group.length === 2)\n if (!trio || !pair || pair[0] === trio[0]) return null\n return { type: 'trioPair', cards, key: rankValue(trio[0]), length: 5, wingLength: 2 }\n}\n\nfunction classifySeq(\n cards: readonly CardId[],\n entries: readonly [Rank, CardId[]][],\n): ClassifiedHand | null {\n if (cards.length < 5) return null\n if (!entries.every(([, group]) => group.length === 1)) return null\n if (!isConsecutive(entries.map(([rank]) => rank), 1)) return null\n return {\n type: 'seq',\n cards,\n key: rankValue(entries[entries.length - 1]![0]),\n length: cards.length,\n wingLength: 0,\n }\n}\n\nfunction classifySeqPair(\n cards: readonly CardId[],\n entries: readonly [Rank, CardId[]][],\n): ClassifiedHand | null {\n if (cards.length < 6 || cards.length % 2 !== 0) return null\n if (!entries.every(([, group]) => group.length === 2)) return null\n if (!isConsecutive(entries.map(([rank]) => rank), 1)) return null\n if (entries.length < 3) return null\n return {\n type: 'seqPair',\n cards,\n key: rankValue(entries[entries.length - 1]![0]),\n length: cards.length,\n wingLength: 0,\n }\n}\n\nfunction classifyBarePlane(\n cards: readonly CardId[],\n entries: readonly [Rank, CardId[]][],\n): ClassifiedHand | null {\n const trios = entries.filter(([, group]) => group.length === 3).map(([rank]) => rank)\n if (trios.length < 2) return null\n if (entries.some(([, group]) => group.length !== 3)) return null\n if (!isConsecutive(trios, 1)) return null\n return {\n type: 'plane',\n cards,\n key: rankValue(trios[trios.length - 1]!),\n length: cards.length,\n wingLength: 0,\n }\n}\n\nexport function classifyPlaneSolo(\n cards: readonly CardId[],\n entries: readonly [Rank, CardId[]][] = [...countRanks(cards).entries()].sort(\n (a, b) => rankValue(a[0]) - rankValue(b[0]),\n ),\n): ClassifiedHand | null {\n const available = entries\n .filter(([rank, group]) => group.length >= 3 && isNaturalSeqRank(rank) && rank !== '2')\n .map(([rank]) => rank)\n .sort((a, b) => rankValue(a) - rankValue(b))\n const maxK = Math.min(available.length, Math.floor(cards.length / 4))\n for (let k = maxK; k >= 2; k -= 1) {\n if (cards.length !== k * 4) continue\n const found: ClassifiedHand[] = []\n for (const body of consecutiveRuns(available, k)) {\n const leftover = leftoverAfterBody(cards, body, 3)\n if (leftover.length !== k) continue\n if (containsConsecutiveTrios(leftover)) continue\n found.push({\n type: 'planeSolo',\n cards,\n key: rankValue(body[body.length - 1]!),\n length: cards.length,\n wingLength: k,\n })\n }\n if (found.length > 0) {\n found.sort((a, b) => b.key - a.key)\n return found[0] ?? null\n }\n }\n return null\n}\n\nfunction classifyPlanePair(\n cards: readonly CardId[],\n entries: readonly [Rank, CardId[]][],\n): ClassifiedHand | null {\n const trios = entries.filter(([, group]) => group.length >= 3 && isNaturalSeqRank(group[0] ? rankOf(group[0]) : '3') && rankOf(group[0]!) !== '2')\n const trioRanks = entries\n .filter(([rank, group]) => group.length >= 3 && isNaturalSeqRank(rank) && rank !== '2')\n .map(([rank]) => rank)\n .sort((a, b) => rankValue(a) - rankValue(b))\n for (let k = Math.min(trioRanks.length, Math.floor(cards.length / 5)); k >= 2; k -= 1) {\n if (cards.length !== k * 5) continue\n for (const body of consecutiveRuns(trioRanks, k)) {\n const leftoverPairs = pairWings(cards, body)\n if (leftoverPairs === k) {\n return {\n type: 'planePair',\n cards,\n key: rankValue(body[body.length - 1]!),\n length: cards.length,\n wingLength: k,\n }\n }\n }\n }\n void trios\n return null\n}\n\nfunction classifyFourDualSolo(\n cards: readonly CardId[],\n entries: readonly [Rank, CardId[]][],\n): ClassifiedHand | null {\n if (cards.length !== 6) return null\n const quad = entries.find(([, group]) => group.length === 4)\n if (!quad) return null\n const leftovers = cards.filter((card) => rankOf(card) !== quad[0])\n if (leftovers.length !== 2) return null\n if (hasRocket(countRanks(leftovers))) return null\n return { type: 'fourDualSolo', cards, key: rankValue(quad[0]), length: 6, wingLength: 2 }\n}\n\nfunction classifyFourDualPair(\n cards: readonly CardId[],\n entries: readonly [Rank, CardId[]][],\n): ClassifiedHand | null {\n if (cards.length !== 8) return null\n const quad = entries.find(([, group]) => group.length === 4)\n if (!quad) return null\n const leftover = leftoverAfterBody(cards, [quad[0]], 4)\n const leftoverGroups = [...countRanks(leftover).entries()]\n if (leftoverGroups.some(([rank, group]) => rank === quad[0] || group.length !== 2)) return null\n if (leftoverGroups.length !== 2) return null\n return { type: 'fourDualPair', cards, key: rankValue(quad[0]), length: 8, wingLength: 4 }\n}\n\nfunction hasRocket(ranks: Map<Rank, CardId[]>): boolean {\n return (ranks.get('BJ')?.length ?? 0) >= 1 && (ranks.get('RJ')?.length ?? 0) >= 1\n}\n\nfunction jokerCount(ranks: Map<Rank, CardId[]>): number {\n return (ranks.get('BJ')?.length ?? 0) + (ranks.get('RJ')?.length ?? 0)\n}\n\nfunction classifyWithWilds(original: readonly CardId[], naturals: readonly CardId[], wildCount: number): ClassifiedHand | null {\n if (wildCount <= 0) return classifyNatural(original)\n const ranksToTry = wildAssignmentRanks(naturals, wildCount)\n let best: ClassifiedHand | null = null\n const choice = new Array<number>(wildCount).fill(0)\n const walk = (): void => {\n const virtuals = choice.map((index) => asCardId(`S${ranksToTry[index]}`))\n const result = classifyNatural([...naturals, ...virtuals])\n if (!result) return\n const hand: ClassifiedHand = { ...result, cards: sortCards(original), wild: true }\n if (!best || wildPreference(hand, best) > 0) best = hand\n }\n const rec = (depth: number): void => {\n if (depth === wildCount) {\n walk()\n return\n }\n for (let i = 0; i < ranksToTry.length; i += 1) {\n choice[depth] = i\n rec(depth + 1)\n }\n }\n rec(0)\n return best\n}\n\nfunction wildAssignmentRanks(naturals: readonly CardId[], wildCount: number): Rank[] {\n if (naturals.length === 0) return wildCount >= 4 ? ['2'] : [...WILDABLE_RANKS]\n const present = new Set(naturals.map((card) => rankOf(card)))\n if (wildCount >= 5) {\n return WILDABLE_RANKS.filter((rank) => present.has(rank) || neighborsOf(present).has(rank))\n }\n return [...WILDABLE_RANKS]\n}\n\nfunction neighborsOf(present: Set<Rank>): Set<Rank> {\n const out = new Set<Rank>()\n for (const rank of present) {\n const value = rankValue(rank)\n for (const candidate of WILDABLE_RANKS) {\n if (Math.abs(rankValue(candidate) - value) === 1) out.add(candidate)\n }\n }\n return out\n}\n\nfunction wildPreference(next: ClassifiedHand, prev: ClassifiedHand): number {\n const bombScore = (hand: ClassifiedHand): number => {\n if (hand.type === 'rocket') return 200 + hand.length\n if (hand.type === 'bomb') return 100 + hand.length\n return 0\n }\n const byBomb = bombScore(next) - bombScore(prev)\n if (byBomb !== 0) return byBomb\n if (next.length !== prev.length) return next.length - prev.length\n return next.key - prev.key\n}\n\nfunction isConsecutive(ranks: readonly Rank[], step: number): boolean {\n if (ranks.some((rank) => !isNaturalSeqRank(rank))) return false\n for (let i = 1; i < ranks.length; i += 1) {\n if (rankValue(ranks[i]!) - rankValue(ranks[i - 1]!) !== step) return false\n }\n return ranks.length > 0\n}\n\nfunction consecutiveRuns(ranks: readonly Rank[], length: number): Rank[][] {\n const runs: Rank[][] = []\n for (let i = 0; i + length <= ranks.length; i += 1) {\n const slice = ranks.slice(i, i + length)\n if (isConsecutive(slice, 1)) runs.push(slice)\n }\n return runs\n}\n\nfunction containsConsecutiveTrios(cards: readonly CardId[]): boolean {\n const groups = [...countRanks(cards).entries()]\n .filter(([, group]) => group.length >= 3)\n .map(([rank]) => rank)\n .filter((rank) => isNaturalSeqRank(rank) && rank !== '2')\n .sort((a, b) => rankValue(a) - rankValue(b))\n return consecutiveRuns(groups, 2).length > 0\n}\n\nfunction pairWings(cards: readonly CardId[], body: readonly Rank[]): number | null {\n const leftover = leftoverAfterBody(cards, body, 3)\n const groups = countRanks(leftover)\n if ([...groups.values()].some((group) => group.length !== 2 && group.length !== 4)) return null\n let pairs = 0\n for (const [rank, group] of groups) {\n if (body.includes(rank)) return null\n if (group.length === 2) pairs += 1\n else if (group.length === 4) pairs += 2\n else return null\n }\n return pairs\n}\n\nfunction leftoverAfterBody(cards: readonly CardId[], body: readonly Rank[], take: number): CardId[] {\n const used = new Map<Rank, number>()\n for (const rank of body) used.set(rank, take)\n const leftover: CardId[] = []\n for (const card of cards) {\n const rank = rankOf(card)\n const remaining = used.get(rank) ?? 0\n if (remaining > 0) used.set(rank, remaining - 1)\n else leftover.push(card)\n }\n return leftover\n}\n\nfunction findBombs(hand: readonly CardId[], laiZiRanks: readonly string[] = []): CardId[][] {\n const bombs: CardId[][] = []\n const ranks = countRanks(hand)\n const wilds = laiZiRanks.length === 0\n ? []\n : hand.filter((card) => isWildCard(card, laiZiRanks))\n for (const [rank, group] of ranks) {\n if (isJokerRank(rank)) continue\n const natural = laiZiRanks.includes(rank)\n ? group.filter((card) => !isWildCard(card, laiZiRanks))\n : group\n const total = natural.length + wilds.length\n if (natural.length + (laiZiRanks.includes(rank) ? 0 : 0) >= 4 || total >= 4) {\n if (natural.length >= 4) bombs.push(natural.slice(0, Math.max(4, natural.length)))\n if (wilds.length > 0 && total >= 4) bombs.push([...natural, ...wilds].slice(0, total))\n }\n }\n const jokers = hand.filter((card) => isJokerRank(rankOf(card)))\n if (jokers.length >= 2) bombs.push(jokers)\n return bombs\n}\n\nfunction generateCandidates(hand: readonly CardId[]): CardId[][] {\n const out: CardId[][] = []\n const ranks = countRanks(hand)\n for (const group of ranks.values()) {\n for (let n = 1; n <= group.length; n += 1) out.push(group.slice(0, n))\n }\n const rocket = hand.filter((card) => isJokerRank(rankOf(card)))\n if (rocket.length === 2) out.push(rocket)\n appendSeqs(out, ranks, 1, 5)\n appendSeqs(out, ranks, 2, 3)\n appendPlanes(out, hand, ranks)\n appendKickers(out, hand, ranks)\n return out\n}\n\nfunction generateFollowCandidates(hand: readonly CardId[], lead: ClassifiedHand): CardId[][] {\n return generateCandidates(hand).filter((subset) => subset.length === lead.length)\n}\n\nfunction appendSeqs(\n out: CardId[][],\n ranks: Map<Rank, CardId[]>,\n copies: number,\n minLen: number,\n): void {\n const usable = [...ranks.entries()]\n .filter(([rank, group]) => isNaturalSeqRank(rank) && group.length >= copies)\n .sort((a, b) => rankValue(a[0]) - rankValue(b[0]))\n for (let len = minLen; len <= usable.length; len += 1) {\n for (let i = 0; i + len <= usable.length; i += 1) {\n const slice = usable.slice(i, i + len)\n if (!isConsecutive(slice.map(([rank]) => rank), 1)) continue\n out.push(slice.flatMap(([, group]) => group.slice(0, copies)))\n }\n }\n}\n\nfunction appendPlanes(out: CardId[][], hand: readonly CardId[], ranks: Map<Rank, CardId[]>): void {\n const trioRanks = [...ranks.entries()]\n .filter(([rank, group]) => group.length >= 3 && isNaturalSeqRank(rank) && rank !== '2')\n .map(([rank]) => rank)\n .sort((a, b) => rankValue(a) - rankValue(b))\n for (let k = 2; k <= trioRanks.length; k += 1) {\n for (const body of consecutiveRuns(trioRanks, k)) {\n const bodyCards = leftoverInverse(hand, body, 3)\n out.push(bodyCards)\n const rest = leftoverAfterBody(hand, body, 3)\n for (const wings of combinations(rest, k)) out.push([...bodyCards, ...wings])\n const pairs = pairCombinations(rest, k)\n for (const wings of pairs) out.push([...bodyCards, ...wings])\n }\n }\n}\n\nfunction leftoverInverse(hand: readonly CardId[], body: readonly Rank[], take: number): CardId[] {\n const used = new Map<Rank, number>()\n for (const rank of body) used.set(rank, take)\n const taken: CardId[] = []\n for (const card of hand) {\n const rank = rankOf(card)\n const remaining = used.get(rank) ?? 0\n if (remaining > 0) {\n used.set(rank, remaining - 1)\n taken.push(card)\n }\n }\n return taken\n}\n\nfunction appendKickers(out: CardId[][], hand: readonly CardId[], ranks: Map<Rank, CardId[]>): void {\n for (const [rank, group] of ranks) {\n if (group.length >= 3) {\n const trio = group.slice(0, 3)\n for (const card of hand) {\n if (rankOf(card) === rank) continue\n out.push([...trio, card])\n }\n for (const [other, otherGroup] of ranks) {\n if (other === rank || otherGroup.length < 2) continue\n out.push([...trio, ...otherGroup.slice(0, 2)])\n }\n }\n if (group.length === 4) {\n const rest = hand.filter((card) => rankOf(card) !== rank)\n for (const wings of combinations(rest, 2)) out.push([...group, ...wings])\n const pairRanks = [...ranks.entries()].filter(([r, g]) => r !== rank && g.length >= 2)\n for (let i = 0; i < pairRanks.length; i += 1) {\n for (let j = i + 1; j < pairRanks.length; j += 1) {\n out.push([\n ...group,\n ...pairRanks[i]![1].slice(0, 2),\n ...pairRanks[j]![1].slice(0, 2),\n ])\n }\n }\n }\n }\n}\n\nfunction combinations(items: readonly CardId[], k: number): CardId[][] {\n const out: CardId[][] = []\n const walk = (start: number, acc: CardId[]): void => {\n if (acc.length === k) {\n out.push([...acc])\n return\n }\n for (let i = start; i < items.length; i += 1) {\n acc.push(items[i]!)\n walk(i + 1, acc)\n acc.pop()\n }\n }\n if (k <= items.length) walk(0, [])\n return out\n}\n\nfunction pairCombinations(items: readonly CardId[], k: number): CardId[][] {\n const groups = [...countRanks(items).entries()].filter(([, group]) => group.length >= 2)\n const out: CardId[][] = []\n const walk = (start: number, acc: CardId[], taken: number): void => {\n if (taken === k) {\n out.push([...acc])\n return\n }\n for (let i = start; i < groups.length; i += 1) {\n const group = groups[i]![1]\n acc.push(...group.slice(0, 2))\n walk(i + 1, acc, taken + 1)\n acc.splice(acc.length - 2, 2)\n if (group.length >= 4 && taken + 2 <= k) {\n acc.push(...group.slice(0, 4))\n walk(i + 1, acc, taken + 2)\n acc.splice(acc.length - 4, 4)\n }\n }\n }\n walk(0, [], 0)\n return out\n}\n\nfunction capCombos(combos: LegalCombo[]): LegalCombo[] {\n if (combos.length <= 200) return combos\n return combos.slice(0, 200)\n}\n","import {\n asHandId, decksFor, nextSeat, type BidAction, type BidScore, type CardId,\n type DoubleAction, type Hand, type HandId, type Play, type RoomId, type Seat, type SeatCount,\n} from '../types.ts'\nimport { sortCards } from './cards.ts'\nimport { dealCards, giveBottom, pickLaiZiRanks, randomSeat } from './deal.ts'\nimport { beats, classify, enumerateLegal, smallestSolo, type ClassifiedHand } from './hands.ts'\n\nexport type EnginePhase = 'bidding' | 'doubling' | 'playing' | 'settled' | 'redeal'\n\nexport interface EngineState {\n phase: EnginePhase\n seatCount: SeatCount\n laiZi: boolean\n hand: MutableHand\n firstLeadSeq: number | null\n successfulLandlordPlaysAfterLead: number\n farmerPlayed: boolean[]\n bids: Array<BidAction | null>\n bidTurn: Seat\n called: boolean\n actedBid: boolean[]\n refusedCall: boolean[]\n robBackDone: boolean\n highestBidder: Seat | null\n passesInRow: number\n doubleDeadlineAt: number\n farmerDoubleDone: boolean[]\n landlordDoubleDone: boolean\n}\n\nexport interface MutableHand {\n handId: Hand['handId']\n roomId: Hand['roomId']\n dealerSeat: Seat\n bottom: CardId[]\n hands: CardId[][]\n bid: BidScore\n landlordSeat: Seat | null\n farmerDoubledBySeat: Partial<Record<Seat, number>>\n landlordReDouble: number\n mingPaiBySeat: Partial<Record<Seat, boolean>>\n mingPaiMult: number\n bombCount: number\n rocketCount: number\n spring: 'none' | 'spring' | 'anti'\n table: Play[]\n turnSeat: Seat\n leadSeat: Seat\n laiZiRanks: string[]\n}\n\nexport type PlayResult = {\n readonly ok: true\n readonly play: Play\n readonly emptied: boolean\n} | {\n readonly ok: false\n readonly code: 'illegal' | 'not-your-turn' | 'phase'\n readonly reason: string\n}\n\nexport function createHand(\n roomId: RoomId,\n dealerSeat: Seat | null,\n seq: number,\n options: { seatCount?: SeatCount; laiZi?: boolean } = {},\n): EngineState {\n const seatCount = options.seatCount ?? 3\n const laiZi = options.laiZi === true\n const deal = dealCards(seatCount)\n const start = dealerSeat ?? randomSeat(seatCount)\n const laiZiRanks = laiZi ? pickLaiZiRanks(2) : []\n const hand: MutableHand = {\n handId: asHandId(`hand_${roomId}_${seq}`),\n roomId,\n dealerSeat: start,\n bottom: [...deal.bottom],\n hands: deal.hands.map((cards) => [...cards]),\n bid: 0,\n landlordSeat: null,\n farmerDoubledBySeat: {},\n landlordReDouble: 1,\n mingPaiBySeat: {},\n mingPaiMult: 1,\n bombCount: 0,\n rocketCount: 0,\n spring: 'none',\n table: [],\n turnSeat: start,\n leadSeat: start,\n laiZiRanks,\n }\n return {\n phase: 'bidding',\n seatCount,\n laiZi,\n hand,\n firstLeadSeq: null,\n successfulLandlordPlaysAfterLead: 0,\n farmerPlayed: Array.from({ length: seatCount }, () => false),\n bids: Array.from({ length: seatCount }, () => null),\n bidTurn: start,\n called: false,\n actedBid: Array.from({ length: seatCount }, () => false),\n refusedCall: Array.from({ length: seatCount }, () => false),\n robBackDone: false,\n highestBidder: null,\n passesInRow: 0,\n doubleDeadlineAt: 0,\n farmerDoubleDone: Array.from({ length: seatCount }, () => false),\n landlordDoubleDone: false,\n }\n}\n\nexport function applyBid(state: EngineState, seat: Seat, action: BidAction): PlayResult {\n if (state.phase !== 'bidding') return { ok: false, code: 'phase', reason: 'not bidding' }\n if (state.bidTurn !== seat) return { ok: false, code: 'not-your-turn', reason: 'not your bid' }\n const robBack = state.actedBid[seat] === true\n if (robBack && action === 'call') return { ok: false, code: 'illegal', reason: 'already called' }\n if (action === 'call') {\n if (state.called) return { ok: false, code: 'illegal', reason: 'already called' }\n state.called = true\n state.highestBidder = seat\n state.hand.bid = 1\n const othersPassed = state.refusedCall.filter(Boolean).length === state.seatCount - 1\n if (othersPassed) {\n state.bids[seat] = action\n state.actedBid[seat] = true\n lockLandlord(state, seat, 1)\n return { ok: true, play: dummyPlay(seat), emptied: false }\n }\n } else if (action === 'rob') {\n if (!state.called) return { ok: false, code: 'illegal', reason: 'nobody called yet' }\n if (state.refusedCall[seat]) return { ok: false, code: 'illegal', reason: 'passed call cannot rob' }\n state.highestBidder = seat\n state.hand.bid = Math.max(1, state.hand.bid) * 2\n } else if (!state.called) {\n state.refusedCall[seat] = true\n }\n state.bids[seat] = action\n if (robBack) state.robBackDone = true\n else state.actedBid[seat] = true\n const next = nextBidder(state, seat)\n if (next === null) {\n if (!state.called || state.highestBidder === null || state.hand.bid === 0) {\n state.phase = 'redeal'\n return { ok: true, play: dummyPlay(seat), emptied: false }\n }\n lockLandlord(state, state.highestBidder, state.hand.bid)\n return { ok: true, play: dummyPlay(seat), emptied: false }\n }\n state.bidTurn = next\n return { ok: true, play: dummyPlay(seat), emptied: false }\n}\n\nexport function applyMingPai(state: EngineState, seat: Seat): PlayResult {\n if (state.hand.mingPaiBySeat[seat]) return { ok: false, code: 'illegal', reason: 'already ming pai' }\n if (state.phase === 'bidding' && state.hand.landlordSeat === null) {\n state.hand.mingPaiBySeat[seat] = true\n state.hand.mingPaiMult = Math.max(state.hand.mingPaiMult, 3)\n if (!state.called && state.actedBid.every((acted) => !acted)) state.bidTurn = seat\n return { ok: true, play: dummyPlay(seat), emptied: false }\n }\n if (state.phase === 'doubling' || state.phase === 'playing') {\n state.hand.mingPaiBySeat[seat] = true\n state.hand.mingPaiMult = Math.max(state.hand.mingPaiMult, 2)\n return { ok: true, play: dummyPlay(seat), emptied: false }\n }\n return { ok: false, code: 'phase', reason: 'cannot ming pai now' }\n}\n\nexport function applyDouble(\n state: EngineState,\n seat: Seat,\n action: DoubleAction,\n now = Date.now(),\n): PlayResult {\n if (state.phase !== 'doubling') return { ok: false, code: 'phase', reason: 'not doubling' }\n if (now > state.doubleDeadlineAt) {\n finishDoubling(state)\n return { ok: false, code: 'phase', reason: 'double window closed' }\n }\n const landlord = state.hand.landlordSeat\n if (landlord === null) return { ok: false, code: 'phase', reason: 'no landlord' }\n const factor = action === 'reDouble' ? 4 : action === 'double' ? 2 : 1\n if (seat === landlord) {\n if (state.landlordDoubleDone) return { ok: false, code: 'illegal', reason: 'already answered' }\n state.hand.landlordReDouble = factor\n state.landlordDoubleDone = true\n if (farmersFinished(state)) finishDoubling(state)\n return { ok: true, play: dummyPlay(seat), emptied: false }\n }\n if (state.farmerDoubleDone[seat]) return { ok: false, code: 'illegal', reason: 'already answered' }\n state.farmerDoubleDone[seat] = true\n if (factor > 1) state.hand.farmerDoubledBySeat[seat] = factor\n if (farmersFinished(state) && state.landlordDoubleDone) finishDoubling(state)\n return { ok: true, play: dummyPlay(seat), emptied: false }\n}\n\nexport function expireDoubling(state: EngineState, now = Date.now()): void {\n if (state.phase !== 'doubling') return\n if (now >= state.doubleDeadlineAt) finishDoubling(state)\n}\n\nexport function applyPlay(state: EngineState, seat: Seat, cards: readonly CardId[], seq: number, ts: string): PlayResult {\n if (state.phase !== 'playing') return { ok: false, code: 'phase', reason: 'not playing' }\n if (state.hand.turnSeat !== seat) return { ok: false, code: 'not-your-turn', reason: 'not your turn' }\n const handCards = state.hand.hands[seat] ?? []\n if (!containsAll(handCards, cards)) return { ok: false, code: 'illegal', reason: 'cards not in hand' }\n const classified = classify(cards, state.hand.laiZiRanks)\n if (!classified) return { ok: false, code: 'illegal', reason: 'unknown hand type' }\n const lead = currentLead(state)\n const isLead = lead === null || state.hand.leadSeat === seat\n if (isLead) {\n const play = commitPlay(state, seat, classified, seq, ts)\n return { ok: true, play, emptied: state.hand.hands[seat]!.length === 0 }\n }\n if (!beats(lead, classified, decksFor(state.seatCount))) {\n return { ok: false, code: 'illegal', reason: 'does not beat lead' }\n }\n const play = commitPlay(state, seat, classified, seq, ts)\n return { ok: true, play, emptied: (state.hand.hands[seat] ?? []).length === 0 }\n}\n\nexport function applyPass(state: EngineState, seat: Seat, seq: number, ts: string): PlayResult {\n if (state.phase !== 'playing') return { ok: false, code: 'phase', reason: 'not playing' }\n if (state.hand.turnSeat !== seat) return { ok: false, code: 'not-your-turn', reason: 'not your turn' }\n if (state.hand.leadSeat === seat || currentLead(state) === null) {\n return { ok: false, code: 'illegal', reason: 'cannot pass when leading' }\n }\n const play: Play = { seat, type: 'pass', cards: [], seq, ts }\n state.hand.table.push(play)\n state.passesInRow += 1\n if (state.passesInRow >= state.seatCount - 1) {\n state.hand.leadSeat = nextSeat(seat, state.seatCount)\n state.hand.turnSeat = state.hand.leadSeat\n state.passesInRow = 0\n } else {\n state.hand.turnSeat = nextSeat(seat, state.seatCount)\n }\n return { ok: true, play, emptied: false }\n}\n\nexport function autoTimeout(state: EngineState, seq: number, ts: string): PlayResult | null {\n if (state.phase !== 'playing') return null\n const seat = state.hand.turnSeat\n if (state.hand.leadSeat === seat || currentLead(state) === null) {\n const card = smallestSolo(state.hand.hands[seat] ?? [])\n if (!card) return null\n return applyPlay(state, seat, [card], seq, ts)\n }\n return applyPass(state, seat, seq, ts)\n}\n\nexport function legalFor(state: EngineState, seat: Seat) {\n if (state.phase !== 'playing' || state.hand.turnSeat !== seat) {\n return { canPass: false, combos: [] as const }\n }\n const lead = currentLead(state)\n const canPass = !(state.hand.leadSeat === seat || lead === null)\n return {\n canPass,\n combos: enumerateLegal(state.hand.hands[seat] ?? [], lead, state.hand.laiZiRanks, decksFor(state.seatCount)),\n }\n}\n\nexport function finishHand(state: EngineState, winnerSeat: Seat): void {\n const landlord = state.hand.landlordSeat\n if (landlord === null) return\n const landlordEmptied = winnerSeat === landlord\n const farmersNeverPlayed = state.hand.hands\n .map((_, seat) => seat as Seat)\n .filter((seat) => seat !== landlord)\n .every((seat) => !state.farmerPlayed[seat])\n const spring = landlordEmptied && farmersNeverPlayed\n const anti = !landlordEmptied && state.successfulLandlordPlaysAfterLead === 0\n state.hand.spring = spring ? 'spring' : anti ? 'anti' : 'none'\n state.phase = 'settled'\n}\n\nexport function snapshotHand(state: EngineState): Hand {\n return {\n ...state.hand,\n hands: state.hand.hands.map((cards) => sortCards(cards)),\n bottom: [...state.hand.bottom],\n table: [...state.hand.table],\n farmerDoubledBySeat: { ...state.hand.farmerDoubledBySeat },\n mingPaiBySeat: { ...state.hand.mingPaiBySeat },\n laiZiRanks: [...state.hand.laiZiRanks],\n }\n}\n\nfunction nextBidder(state: EngineState, from: Seat): Seat | null {\n let seat = nextSeat(from, state.seatCount)\n for (let i = 0; i < state.seatCount; i += 1) {\n if (!state.actedBid[seat]) return seat\n seat = nextSeat(seat, state.seatCount)\n }\n const caller = firstCaller(state)\n if (\n state.called\n && !state.robBackDone\n && caller !== null\n && state.highestBidder !== caller\n && !state.refusedCall[caller]\n ) {\n return caller\n }\n return null\n}\n\nfunction firstCaller(state: EngineState): Seat | null {\n const index = state.bids.findIndex((bid) => bid === 'call')\n return index >= 0 ? index as Seat : null\n}\n\nfunction lockLandlord(state: EngineState, seat: Seat, bid: BidScore): void {\n state.hand.landlordSeat = seat\n state.hand.bid = bid\n state.hand.hands = giveBottom(state.hand.hands, state.hand.bottom, seat)\n state.hand.turnSeat = seat\n state.hand.leadSeat = seat\n state.phase = 'doubling'\n state.doubleDeadlineAt = Date.now() + 8_000\n}\n\nfunction finishDoubling(state: EngineState): void {\n if (state.phase !== 'doubling') return\n state.phase = 'playing'\n state.passesInRow = 0\n}\n\nfunction farmersFinished(state: EngineState): boolean {\n const landlord = state.hand.landlordSeat\n return state.hand.hands.every((_, index) => {\n const seat = index as Seat\n return seat === landlord || state.farmerDoubleDone[seat]\n })\n}\n\nfunction currentLead(state: EngineState): ClassifiedHand | null {\n for (let i = state.hand.table.length - 1; i >= 0; i -= 1) {\n const play = state.hand.table[i]\n if (!play || play.type === 'pass') continue\n const classified = classify(play.cards, state.hand.laiZiRanks)\n return classified\n }\n return null\n}\n\nfunction commitPlay(\n state: EngineState,\n seat: Seat,\n classified: ClassifiedHand,\n seq: number,\n ts: string,\n): Play {\n const play: Play = { seat, type: classified.type, cards: classified.cards, seq, ts }\n const held = state.hand.hands[seat]\n if (!held) return play\n removeCards(held, classified.cards)\n state.hand.table.push(play)\n if (classified.type === 'bomb') state.hand.bombCount += 1\n if (classified.type === 'rocket') state.hand.rocketCount += 1\n const landlord = state.hand.landlordSeat\n if (landlord !== null && seat !== landlord) state.farmerPlayed[seat] = true\n if (state.firstLeadSeq === null) {\n state.firstLeadSeq = seq\n } else if (landlord !== null && seat === landlord) {\n state.successfulLandlordPlaysAfterLead += 1\n }\n state.hand.leadSeat = seat\n state.hand.turnSeat = nextSeat(seat, state.seatCount)\n state.passesInRow = 0\n if ((state.hand.hands[seat] ?? []).length === 0) finishHand(state, seat)\n return play\n}\n\nfunction containsAll(hand: readonly CardId[], cards: readonly CardId[]): boolean {\n const bag = new Map<CardId, number>()\n for (const card of hand) bag.set(card, (bag.get(card) ?? 0) + 1)\n for (const card of cards) {\n const count = bag.get(card) ?? 0\n if (count === 0) return false\n bag.set(card, count - 1)\n }\n return true\n}\n\nfunction removeCards(hand: CardId[], cards: readonly CardId[]): void {\n for (const card of cards) {\n const index = hand.indexOf(card)\n if (index >= 0) hand.splice(index, 1)\n }\n}\n\nfunction dummyPlay(seat: Seat): Play {\n return { seat, type: 'pass', cards: [], seq: 0, ts: new Date().toISOString() }\n}\n\nexport function createHandId(roomId: RoomId, seq: number): HandId {\n return asHandId(`hand_${roomId}_${seq}`)\n}\n","import {\n asSettlementId, asTokenAtomString, type BidScore, type Hand, type PlayerId,\n type Seat, type Settlement,\n} from '../types.ts'\nimport { formatM } from '../settle/math.ts'\n\nexport interface ScoreInput {\n readonly hand: Pick<Hand, 'handId' | 'bid' | 'landlordSeat' | 'farmerDoubledBySeat' | 'landlordReDouble' | 'mingPaiMult' | 'bombCount' | 'rocketCount' | 'spring'>\n readonly stakeAtoms: bigint\n readonly maxMultiplier: number\n readonly winner: 'landlord' | 'farmers'\n readonly playerIds: readonly PlayerId[]\n}\n\nfunction personalMult(value: number | boolean | undefined): bigint {\n if (value === true) return 2n\n if (typeof value === 'number' && value > 1) return BigInt(value)\n return 1n\n}\n\nexport function scoreHand(input: ScoreInput): Settlement {\n const { hand, stakeAtoms, maxMultiplier, winner, playerIds } = input\n if (hand.landlordSeat === null) throw new Error('cannot score without a landlord')\n if (hand.bid === 0) throw new Error('cannot score a void bid')\n const springFlag = hand.spring === 'none' ? 0 : 1\n const ming = hand.mingPaiMult > 1 ? hand.mingPaiMult : 1\n const raw = Math.max(1, hand.bid) * (2 ** (hand.bombCount + hand.rocketCount + springFlag)) * ming\n const multiplier = Math.min(raw, maxMultiplier)\n const unitAtoms = stakeAtoms * BigInt(multiplier)\n const landlord = hand.landlordSeat\n const landlordPersonal = personalMult(hand.landlordReDouble)\n const farmers = playerIds\n .map((_, index) => index as Seat)\n .filter((seat) => seat !== landlord)\n const farmerPays = new Map<Seat, bigint>()\n for (const seat of farmers) {\n farmerPays.set(seat, unitAtoms * personalMult(hand.farmerDoubledBySeat[seat]) * landlordPersonal)\n }\n const sign = winner === 'landlord' ? 1n : -1n\n const landlordTotal = farmers.reduce((sum, seat) => sum + (farmerPays.get(seat) ?? 0n), 0n)\n const deltas = playerIds.map((playerId, index) => {\n const seat = index as Seat\n if (seat === landlord) return { seat, playerId, atoms: asTokenAtomString(sign * landlordTotal) }\n return { seat, playerId, atoms: asTokenAtomString(-sign * (farmerPays.get(seat) ?? 0n)) }\n })\n const formula = [\n `${formatM(stakeAtoms)} × min(叫抢${hand.bid} × 2^(${hand.bombCount}炸 + ${hand.rocketCount}王炸 + ${springFlag}春天) × 明牌${ming}, ${maxMultiplier})`,\n landlordPersonal > 1n ? `× 地主${landlordPersonal.toString()}` : '',\n `= ${formatM(unitAtoms)}`,\n ].filter(Boolean).join(' ')\n return {\n settlementId: asSettlementId(`set_${hand.handId}`),\n handId: hand.handId,\n winner,\n unitAtoms: asTokenAtomString(unitAtoms),\n deltas,\n rakeAtoms: asTokenAtomString(0n),\n formula,\n }\n}\n\nexport function describeBid(bid: BidScore): string {\n return bid === 0 ? '未叫' : `${bid}倍`\n}\n","import { randomId } from '../crypto.ts'\nimport { asPlayerId, type PlayerId } from '../types.ts'\nimport { MAX_DISPLAY_NAME, MAX_ROOM_TITLE } from '../invariant.ts'\n\nexport function newPlayerId(): PlayerId {\n return asPlayerId(randomId('pl'))\n}\n\nexport function sanitizeDisplayName(raw: string): string {\n const trimmed = raw.trim().replace(/\\s+/g, ' ')\n if (trimmed.length === 0) return '玩家'\n return trimmed.slice(0, MAX_DISPLAY_NAME)\n}\n\nexport function sanitizeRoomTitle(raw: string, fallback = '好友局'): string {\n const trimmed = raw.trim().replace(/\\s+/g, ' ')\n if (trimmed.length === 0) return fallback\n return trimmed.slice(0, MAX_ROOM_TITLE)\n}\n\nexport function sanitizeAvatarUrl(raw: unknown, routePrefix: string): string | null {\n if (typeof raw !== 'string' || raw.length === 0) return null\n if (raw.startsWith(`${routePrefix}/api/avatars/`)) return raw\n try {\n const url = new URL(raw)\n if (url.protocol !== 'https:') return null\n return url.toString()\n } catch {\n return null\n }\n}\n","import { z } from 'zod'\n\nexport const TokenAtomString = z.string().regex(/^-?\\d+$/)\n\nexport const RoomRecordSchema = z.object({\n roomId: z.string(),\n roomCode: z.string(),\n title: z.string().default('好友局'),\n hostPlayerId: z.string(),\n phase: z.enum(['waiting', 'dealing', 'bidding', 'doubling', 'playing', 'settling', 'void', 'closed']),\n stakeAtoms: TokenAtomString,\n maxMultiplier: z.number().int().positive(),\n seatCount: z.union([z.literal(3), z.literal(4)]).default(3),\n laiZi: z.boolean().default(false),\n inviteExpiresAt: z.string(),\n createdAt: z.string(),\n shareable: z.boolean(),\n roomSecret: z.string(),\n sitInviteHash: z.string(),\n watchInviteHash: z.string(),\n lastLandlordSeat: z.number().int().min(0).max(3).nullable(),\n})\n\nexport const PlayerRecordSchema = z.object({\n playerId: z.string(),\n displayName: z.string(),\n avatarUrl: z.string().url().nullable(),\n availableAtoms: TokenAtomString,\n welcomeGranted: z.boolean(),\n createdAt: z.string(),\n})\n\nexport const TokenGrantSchema = z.object({\n grantId: z.string(),\n playerId: z.string(),\n roomId: z.string(),\n maxExposureAtoms: TokenAtomString,\n issuedAt: z.string(),\n expiresAt: z.string(),\n source: z.literal('host-welcome'),\n issuedBy: z.literal('room-host'),\n})\n\nexport const LedgerEntrySchema = z.object({\n entryId: z.string(),\n ts: z.string(),\n roomId: z.string().optional(),\n handId: z.string().optional(),\n settlementId: z.string().optional(),\n from: z.string(),\n to: z.string(),\n atoms: TokenAtomString,\n reason: z.enum(['welcome', 'freeze', 'unfreeze', 'settle', 'void']),\n prevHash: z.string(),\n hash: z.string(),\n})\n\nexport const HandCheckpointSchema = z.object({\n handId: z.string(),\n roomId: z.string(),\n sealedHands: z.string(),\n publicJson: z.string(),\n seq: z.number().int(),\n})\n\nexport type RoomRecord = z.infer<typeof RoomRecordSchema>\nexport type PlayerRecord = z.infer<typeof PlayerRecordSchema>\nexport type GrantRecord = z.infer<typeof TokenGrantSchema>\nexport type LedgerRecord = z.infer<typeof LedgerEntrySchema>\nexport type HandCheckpoint = z.infer<typeof HandCheckpointSchema>\n","import {\n HandCheckpointSchema, LedgerEntrySchema, PlayerRecordSchema, RoomRecordSchema, TokenGrantSchema,\n type GrantRecord, type HandCheckpoint, type LedgerRecord, type PlayerRecord, type RoomRecord,\n} from './schemas.ts'\n\nexport interface KvLike<V> {\n get(key: string): V | undefined\n put(key: string, value: V): Promise<void>\n delete(key: string): Promise<boolean>\n entries(): IterableIterator<[string, V]>\n}\n\nexport interface DomainLike {\n tables: {\n rooms: KvLike<RoomRecord>\n players: KvLike<PlayerRecord>\n grants: KvLike<GrantRecord>\n ledger: KvLike<LedgerRecord>\n hands: KvLike<HandCheckpoint>\n }\n close(): Promise<void>\n}\n\nexport const DOUDIZHU_DOMAIN_SPEC = {\n name: 'doudizhu',\n version: 1,\n tables: {\n rooms: RoomRecordSchema,\n players: PlayerRecordSchema,\n grants: TokenGrantSchema,\n ledger: LedgerEntrySchema,\n hands: HandCheckpointSchema,\n },\n} as const\n\nexport class MemoryKv<V> implements KvLike<V> {\n private readonly map = new Map<string, V>()\n get(key: string): V | undefined {\n return this.map.get(key)\n }\n async put(key: string, value: V): Promise<void> {\n this.map.set(key, value)\n }\n async delete(key: string): Promise<boolean> {\n return this.map.delete(key)\n }\n entries(): IterableIterator<[string, V]> {\n return this.map.entries()\n }\n}\n\nexport function createMemoryDomain(): DomainLike {\n return {\n tables: {\n rooms: new MemoryKv(),\n players: new MemoryKv(),\n grants: new MemoryKv(),\n ledger: new MemoryKv(),\n hands: new MemoryKv(),\n },\n async close() {},\n }\n}\n\nexport async function openDoudizhuDomain(storage: unknown): Promise<DomainLike | null> {\n if (!storage || typeof storage !== 'object') return null\n const facility = storage as {\n open?: (spec: unknown) => Promise<{\n table: (name: string) => KvLike<unknown>\n close: () => Promise<void>\n }>\n }\n if (typeof facility.open !== 'function') return null\n try {\n const spec = {\n name: 'doudizhu',\n version: 1,\n tables: {\n rooms: { valueSchema: RoomRecordSchema },\n players: { valueSchema: PlayerRecordSchema },\n grants: { valueSchema: TokenGrantSchema },\n ledger: { valueSchema: LedgerEntrySchema },\n hands: { valueSchema: HandCheckpointSchema },\n },\n }\n const domain = await facility.open(spec)\n return {\n tables: {\n rooms: domain.table('rooms') as KvLike<RoomRecord>,\n players: domain.table('players') as KvLike<PlayerRecord>,\n grants: domain.table('grants') as KvLike<GrantRecord>,\n ledger: domain.table('ledger') as KvLike<LedgerRecord>,\n hands: domain.table('hands') as KvLike<HandCheckpoint>,\n },\n close: () => domain.close(),\n }\n } catch {\n return null\n }\n}\n","export interface HealthCounters {\n roomsActive: number\n handsStarted: number\n handsVoided: number\n wsConnected: number\n cmdRejected: number\n settlementsCommitted: number\n needsAudit: boolean\n uptimeMs: number\n}\n\nexport function createCounters(startedAt = Date.now()): HealthCounters & {\n startedAt: number\n bump(key: Exclude<keyof HealthCounters, 'needsAudit' | 'uptimeMs' | 'roomsActive' | 'wsConnected'>): void\n} {\n return {\n startedAt,\n roomsActive: 0,\n handsStarted: 0,\n handsVoided: 0,\n wsConnected: 0,\n cmdRejected: 0,\n settlementsCommitted: 0,\n needsAudit: false,\n get uptimeMs() {\n return Date.now() - startedAt\n },\n bump(key) {\n this[key] += 1\n },\n }\n}\n","import { addHours, nowIso, randomId } from '../crypto.ts'\nimport type { DomainLike } from '../persist/domain.ts'\nimport type { GrantRecord } from '../persist/schemas.ts'\nimport { asGrantId, asTokenAtomString, type GrantId, type PlayerId, type RoomId, type TokenGrant } from '../types.ts'\n\nexport function issueHostGrant(\n playerId: PlayerId,\n roomId: RoomId,\n maxExposureAtoms: bigint,\n ttlHours = 24,\n): TokenGrant {\n const issuedAt = nowIso()\n return {\n grantId: asGrantId(randomId('gr')),\n playerId,\n roomId,\n maxExposureAtoms: asTokenAtomString(maxExposureAtoms),\n issuedAt,\n expiresAt: addHours(ttlHours),\n source: 'host-welcome',\n issuedBy: 'room-host',\n }\n}\n\nexport async function persistGrant(domain: DomainLike, grant: TokenGrant): Promise<void> {\n const record: GrantRecord = { ...grant }\n await domain.tables.grants.put(grant.grantId, record)\n}\n\nexport function grantRecord(domain: DomainLike, grantId: GrantId): GrantRecord | undefined {\n return domain.tables.grants.get(grantId)\n}\n","import { createHash } from 'node:crypto'\nimport { randomId } from '../crypto.ts'\nimport type { DomainLike } from '../persist/domain.ts'\nimport type { LedgerRecord, PlayerRecord } from '../persist/schemas.ts'\nimport { asEntryId, asPlayerId, asTokenAtomString, parseAtoms, type PlayerId, type TokenAtomString } from '../types.ts'\n\nexport type LedgerReason = LedgerRecord['reason']\n\nexport interface LedgerSnapshot {\n readonly availableAtoms: TokenAtomString\n readonly escrowAtoms: TokenAtomString\n readonly entries: readonly LedgerRecord[]\n}\n\nexport class Ledger {\n private tipHash = '0'.repeat(64)\n private readonly escrow = new Map<PlayerId, bigint>()\n\n constructor(\n private readonly domain: DomainLike,\n private welcomeAtoms: bigint,\n ) {\n for (const [, entry] of domain.tables.ledger.entries()) this.tipHash = entry.hash\n }\n\n getAvailable(playerId: PlayerId): bigint {\n const row = this.domain.tables.players.get(playerId)\n return row ? parseAtoms(row.availableAtoms) : 0n\n }\n\n getEscrow(playerId: PlayerId): bigint {\n return this.escrow.get(playerId) ?? 0n\n }\n\n snapshot(playerId: PlayerId): LedgerSnapshot {\n const entries = [...this.domain.tables.ledger.entries()]\n .map(([, entry]) => entry)\n .filter((entry) => entry.from === playerId || entry.to === playerId)\n return {\n availableAtoms: asTokenAtomString(this.getAvailable(playerId)),\n escrowAtoms: asTokenAtomString(this.getEscrow(playerId)),\n entries,\n }\n }\n\n async ensurePlayer(playerId: PlayerId, displayName: string, avatarUrl: string | null): Promise<PlayerRecord> {\n const existing = this.domain.tables.players.get(playerId)\n if (existing) {\n if (existing.displayName !== displayName) {\n const next = { ...existing, displayName }\n await this.domain.tables.players.put(playerId, next)\n return next\n }\n return existing\n }\n const created: PlayerRecord = {\n playerId,\n displayName,\n avatarUrl,\n availableAtoms: '0',\n welcomeGranted: false,\n createdAt: new Date().toISOString(),\n }\n await this.domain.tables.players.put(playerId, created)\n return this.grantWelcome(asPlayerId(playerId), created)\n }\n\n async grantWelcome(playerId: PlayerId, row = this.domain.tables.players.get(playerId)): Promise<PlayerRecord> {\n if (!row) throw new Error(`unknown player ${playerId}`)\n if (row.welcomeGranted) return row\n const next = await this.applyAvailable(playerId, this.welcomeAtoms, {\n reason: 'welcome',\n from: 'host',\n to: playerId,\n })\n const granted = { ...next, welcomeGranted: true }\n await this.domain.tables.players.put(playerId, granted)\n return granted\n }\n\n async freeze(playerId: PlayerId, atoms: bigint, roomId: string): Promise<void> {\n if (atoms <= 0n) throw new Error('freeze amount must be positive')\n if (this.getAvailable(playerId) < atoms) throw Object.assign(new Error('insufficient'), { code: 'insufficient' })\n await this.applyAvailable(playerId, -atoms, {\n reason: 'freeze',\n from: playerId,\n to: `escrow:${roomId}`,\n roomId,\n })\n this.escrow.set(playerId, this.getEscrow(playerId) + atoms)\n }\n\n async unfreeze(playerId: PlayerId, atoms: bigint, roomId: string, reason: 'unfreeze' | 'void' = 'unfreeze'): Promise<void> {\n const held = this.getEscrow(playerId)\n const amount = atoms < held ? atoms : held\n if (amount <= 0n) return\n this.escrow.set(playerId, held - amount)\n await this.applyAvailable(playerId, amount, {\n reason,\n from: `escrow:${roomId}`,\n to: playerId,\n roomId,\n })\n }\n\n async settleTransfer(\n from: PlayerId,\n to: PlayerId,\n atoms: bigint,\n meta: { roomId: string; handId: string; settlementId: string },\n ): Promise<void> {\n if (atoms <= 0n) return\n const held = this.getEscrow(from)\n if (held < atoms) {\n throw Object.assign(new Error('escrow insufficient'), { code: 'needsAudit' })\n }\n this.escrow.set(from, held - atoms)\n await this.append({\n from,\n to,\n atoms: asTokenAtomString(atoms),\n reason: 'settle',\n roomId: meta.roomId,\n handId: meta.handId,\n settlementId: meta.settlementId,\n })\n const winner = this.domain.tables.players.get(to)\n if (!winner) throw new Error(`unknown winner ${to}`)\n const next = { ...winner, availableAtoms: (parseAtoms(winner.availableAtoms) + atoms).toString() }\n await this.domain.tables.players.put(to, next)\n }\n\n replaceWelcome(welcomeAtoms: bigint): void {\n this.welcomeAtoms = welcomeAtoms\n }\n\n private async applyAvailable(\n playerId: PlayerId,\n delta: bigint,\n meta: { reason: LedgerReason; from: string; to: string; roomId?: string; handId?: string; settlementId?: string },\n ): Promise<PlayerRecord> {\n const row = this.domain.tables.players.get(playerId)\n if (!row) throw new Error(`unknown player ${playerId}`)\n const nextAvailable = parseAtoms(row.availableAtoms) + delta\n if (nextAvailable < 0n) throw Object.assign(new Error('insufficient'), { code: 'insufficient' })\n const next = { ...row, availableAtoms: nextAvailable.toString() }\n await this.domain.tables.players.put(playerId, next)\n await this.append({\n from: meta.from,\n to: meta.to,\n atoms: asTokenAtomString(delta < 0n ? -delta : delta),\n reason: meta.reason,\n roomId: meta.roomId,\n handId: meta.handId,\n settlementId: meta.settlementId,\n })\n return next\n }\n\n private async append(partial: Omit<LedgerRecord, 'entryId' | 'ts' | 'prevHash' | 'hash'>): Promise<LedgerRecord> {\n const entryId = asEntryId(randomId('led'))\n const ts = new Date().toISOString()\n const prevHash = this.tipHash\n const material = JSON.stringify({ ...partial, entryId, ts, prevHash })\n const hash = createHash('sha256').update(material).digest('hex')\n const entry: LedgerRecord = { ...partial, entryId, ts, prevHash, hash }\n await this.domain.tables.ledger.put(entryId, entry)\n this.tipHash = hash\n return entry\n }\n}\n","import { addHours, cookieValue, inviteHash, nowIso, randomId, randomRoomCode, randomToken, sha256Hex } from '../crypto.ts'\nimport {\n applyBid, applyDouble, applyMingPai, applyPass, applyPlay, autoTimeout, createHand, expireDoubling,\n legalFor, snapshotHand, type EngineState,\n} from '../engine/play.ts'\nimport { scoreHand } from '../engine/score.ts'\nimport { rankOf } from '../engine/cards.ts'\nimport { newPlayerId, sanitizeAvatarUrl, sanitizeDisplayName, sanitizeRoomTitle } from '../identity/player.ts'\nimport { MAX_CHAT, MAX_SPECTATORS } from '../invariant.ts'\nimport { createMemoryDomain, openDoudizhuDomain, type DomainLike } from '../persist/domain.ts'\nimport { createCounters, type HealthCounters } from '../settle/audit.ts'\nimport { issueHostGrant, persistGrant } from '../settle/grant.ts'\nimport { Ledger } from '../settle/ledger.ts'\nimport { seatCapAtoms } from '../settle/math.ts'\nimport {\n asRoomId, asTokenAtomString, dealtHandSize, decksFor, landlordHandSize, nextSeat, parseAtoms, prevSeat,\n type BidAction, type CardId, type ClientCommand, type DoubleAction, type PlayerId, type PlayerView,\n type PublicSettlement, type RejectCode, type Room, type RoomId, type Seat, type SeatCount, type SeatState,\n type ServerEvent,\n} from '../types.ts'\nimport { assertCreateEconomy, resolveConfig, validatePublicBaseUrl, type PluginConfig, type ResolvedConfig } from '../config.ts'\n\nexport interface JoinRequest {\n roomCode: string\n invite: string\n displayName: string\n role: 'sit' | 'watch'\n local?: boolean\n}\n\nexport interface JoinResult {\n playerId: PlayerId\n roomId: RoomId\n seat: Seat | null\n wsTicket: string\n cookie: string\n view: PlayerView\n}\n\nexport interface CreateRoomRequest {\n stakeM: number\n maxMultiplier: number\n seatCount?: SeatCount\n laiZi?: boolean\n hostDisplayName?: string\n title?: string\n}\n\nexport interface CreateRoomResult {\n roomId: RoomId\n roomCode: string\n sitUrl: string\n watchUrl: string\n seatCapAtoms: string\n shareable: boolean\n hostCookie: string\n hostPlayerId: PlayerId\n view: PlayerView\n}\n\nexport interface Session {\n cookie: string\n playerId: PlayerId\n roomId: RoomId\n kind: 'seat' | 'watch' | 'host'\n expiresAt: number\n}\n\ninterface Ticket {\n playerId: PlayerId\n roomId: RoomId\n expiresAt: number\n}\n\ninterface LiveRoom {\n room: Room\n secret: string\n sitInviteHash: string\n sitToken: string\n watchInviteHash: string\n watchToken: string\n sitConsumed: boolean\n lastLandlordSeat: Seat | null\n engine: EngineState | null\n deadlineAt: number | null\n seq: number\n events: ServerEvent[]\n chat: PlayerView['chat'][number][]\n nonces: Set<string>\n disconnectedAt: Map<PlayerId, number>\n autoPlay: Set<Seat>\n lastSettlement: PublicSettlement | null\n needsAudit: boolean\n}\n\nexport type ViewListener = (playerId: PlayerId, event: ServerEvent) => void\n\nexport class RoomManager {\n private config: ResolvedConfig\n private domain: DomainLike\n private ledger: Ledger\n private readonly rooms = new Map<RoomId, LiveRoom>()\n private readonly roomsByCode = new Map<string, RoomId>()\n private readonly sessions = new Map<string, Session>()\n private readonly tickets = new Map<string, Ticket>()\n private readonly hostCookies = new Set<string>()\n private readonly listeners = new Set<ViewListener>()\n private readonly timers = new Set<ReturnType<typeof setInterval>>()\n readonly counters = createCounters()\n private persistReady: Promise<void>\n\n constructor(\n private readonly ctx: { logger?: { warn(message: unknown): void; info(message: unknown): void }; webServer?: { port?: number } } | null,\n config: PluginConfig,\n storage: unknown,\n ) {\n this.config = resolveConfig(config)\n this.domain = createMemoryDomain()\n this.ledger = new Ledger(this.domain, this.config.welcomeAtoms)\n this.persistReady = this.hydrate(storage)\n const tick = setInterval(() => { this.onTick(Date.now()) }, 250)\n this.timers.add(tick)\n if (typeof tick === 'object' && 'unref' in tick) tick.unref()\n }\n\n async ready(): Promise<void> {\n await this.persistReady\n }\n\n replaceConfig(config: PluginConfig): void {\n this.config = resolveConfig(config)\n this.ledger.replaceWelcome(this.config.welcomeAtoms)\n }\n\n getConfig(): ResolvedConfig {\n return this.config\n }\n\n onEvent(listener: ViewListener): () => void {\n this.listeners.add(listener)\n return () => { this.listeners.delete(listener) }\n }\n\n async dispose(): Promise<void> {\n for (const timer of this.timers) clearInterval(timer)\n this.timers.clear()\n await this.domain.close()\n }\n\n health(): HealthCounters {\n return {\n roomsActive: [...this.rooms.values()].filter((room) => room.room.phase !== 'closed').length,\n handsStarted: this.counters.handsStarted,\n handsVoided: this.counters.handsVoided,\n wsConnected: this.counters.wsConnected,\n cmdRejected: this.counters.cmdRejected,\n settlementsCommitted: this.counters.settlementsCommitted,\n needsAudit: [...this.rooms.values()].some((room) => room.needsAudit) || this.counters.needsAudit,\n uptimeMs: this.counters.uptimeMs,\n }\n }\n\n async createRoom(req: CreateRoomRequest, hostCookie?: string): Promise<CreateRoomResult> {\n await this.ready()\n if (!this.config.enabled) throw fail('phase', 'plugin disabled')\n if (this.rooms.size >= this.config.maxRooms) throw fail('room-full', 'too many rooms')\n const seatCount: SeatCount = req.seatCount === 4 ? 4 : 3\n const laiZi = req.laiZi === true\n assertCreateEconomy(this.config.welcomeAtoms, req.stakeM, req.maxMultiplier, seatCount)\n const publicBase = this.config.publicBaseUrl ? validatePublicBaseUrl(this.config.publicBaseUrl) : ''\n const roomId = asRoomId(randomId('rm'))\n const roomCode = this.uniqueCode()\n const secret = randomToken()\n const sitToken = randomToken()\n const watchToken = randomToken()\n const hostPlayerId = newPlayerId()\n const displayName = sanitizeDisplayName(req.hostDisplayName ?? '房主')\n const title = sanitizeRoomTitle(req.title ?? '好友局')\n await this.ledger.ensurePlayer(hostPlayerId, displayName, null)\n const stakeAtoms = BigInt(req.stakeM) * 1_000_000n\n const now = nowIso()\n const room: Room = {\n roomId,\n roomCode,\n title,\n hostPlayerId,\n phase: 'waiting',\n stakeAtoms,\n maxMultiplier: req.maxMultiplier,\n seatCount,\n laiZi,\n seats: Array.from({ length: seatCount }, (_, index) => emptySeat(index as Seat)),\n spectatorIds: [],\n currentHandId: null,\n createdAt: now,\n inviteExpiresAt: addHours(this.config.inviteTtlHours),\n shareable: publicBase !== '',\n }\n const live: LiveRoom = {\n room,\n secret,\n sitInviteHash: inviteHash(secret, 'sit', sitToken),\n sitToken,\n watchInviteHash: inviteHash(secret, 'watch', watchToken),\n watchToken,\n sitConsumed: false,\n lastLandlordSeat: null,\n engine: null,\n deadlineAt: null,\n seq: 0,\n events: [],\n chat: [],\n nonces: new Set(),\n disconnectedAt: new Map(),\n autoPlay: new Set(),\n lastSettlement: null,\n needsAudit: false,\n }\n this.rooms.set(roomId, live)\n this.roomsByCode.set(roomCode, roomId)\n await this.persistRoom(live)\n const cookie = hostCookie ?? cookieValue()\n this.hostCookies.add(cookie)\n this.sessions.set(cookie, {\n cookie,\n playerId: hostPlayerId,\n roomId,\n kind: 'host',\n expiresAt: Date.now() + 86_400_000,\n })\n await this.sitInternal(live, hostPlayerId, 0, displayName, true)\n const origin = publicBase || `http://127.0.0.1:${this.listenPort()}`\n return {\n roomId,\n roomCode,\n sitUrl: `${origin}${this.config.routePrefix}/join?code=${roomCode}&invite=${sitToken}`,\n watchUrl: `${origin}${this.config.routePrefix}/join?code=${roomCode}&invite=${watchToken}&role=watch`,\n seatCapAtoms: seatCapAtoms(stakeAtoms, req.maxMultiplier, seatCount).toString(),\n shareable: room.shareable,\n hostCookie: cookie,\n hostPlayerId,\n view: this.viewFor(live, hostPlayerId),\n }\n }\n\n async join(req: JoinRequest): Promise<JoinResult> {\n await this.ready()\n const live = this.roomByCode(req.roomCode)\n if (!live || live.room.phase === 'closed') throw fail('expired', 'room not found')\n if (Date.parse(live.room.inviteExpiresAt) < Date.now()) throw fail('expired', 'invite expired')\n const displayName = sanitizeDisplayName(req.displayName)\n const playerId = newPlayerId()\n await this.ledger.ensurePlayer(playerId, displayName, sanitizeAvatarUrl(null, this.config.routePrefix))\n const invite = req.invite.trim()\n const sitOk = invite\n ? sha256Hex(`${live.secret}|sit|${invite}`) === live.sitInviteHash\n : Boolean(req.local)\n const watchOk = invite\n ? sha256Hex(`${live.secret}|watch|${invite}`) === live.watchInviteHash\n : false\n if (!sitOk && !watchOk) throw fail('auth', invite ? 'invalid invite' : 'room not found')\n const empty = this.firstEmptySeat(live)\n const canSit = live.room.phase === 'waiting' && empty !== null && req.role !== 'watch'\n let seat: Seat | null = null\n if (canSit) {\n await this.sitInternal(live, playerId, empty, displayName, true)\n seat = empty\n if (this.firstEmptySeat(live) === null) live.sitConsumed = true\n await this.maybeDeal(live)\n } else {\n if (!this.config.allowSpectators) throw fail('auth', 'spectators disabled')\n live.sitConsumed = live.sitConsumed || empty === null || live.room.phase !== 'waiting'\n this.addSpectator(live, playerId)\n }\n const cookie = cookieValue()\n this.sessions.set(cookie, {\n cookie,\n playerId,\n roomId: live.room.roomId,\n kind: seat === null ? 'watch' : 'seat',\n expiresAt: Date.now() + 86_400_000,\n })\n this.broadcast(live)\n return {\n playerId,\n roomId: live.room.roomId,\n seat,\n wsTicket: this.issueTicket(playerId, live.room.roomId),\n cookie,\n view: this.viewFor(live, playerId),\n }\n }\n\n sessionByCookie(cookie: string | undefined): Session | undefined {\n if (!cookie) return undefined\n const session = this.sessions.get(cookie)\n if (!session || session.expiresAt < Date.now()) return undefined\n return session\n }\n\n isHostCookie(cookie: string | undefined): boolean {\n return Boolean(cookie && this.hostCookies.has(cookie) && this.sessionByCookie(cookie))\n }\n\n consumeTicket(ticket: string): Ticket | undefined {\n const found = this.tickets.get(ticket)\n this.tickets.delete(ticket)\n if (!found || found.expiresAt < Date.now()) return undefined\n return found\n }\n\n issueTicket(playerId: PlayerId, roomId: RoomId): string {\n const ticket = randomToken()\n this.tickets.set(ticket, { playerId, roomId, expiresAt: Date.now() + 60_000 })\n return ticket\n }\n\n view(roomId: RoomId, playerId: PlayerId): PlayerView {\n return this.viewFor(this.requireRoom(roomId), playerId)\n }\n\n eventsSince(roomId: RoomId, playerId: PlayerId, seq: number): { events: ServerEvent[]; untilSeq: number } {\n const live = this.requireRoom(roomId)\n const events: ServerEvent[] = []\n if (live.seq > seq) events.push({ type: 'snapshot', seq: live.seq, view: this.viewFor(live, playerId) })\n if (live.lastSettlement) events.push({ type: 'settled', seq: live.seq, settlement: live.lastSettlement })\n if (events.length === 0) events.push({ type: 'snapshot', seq: live.seq, view: this.viewFor(live, playerId) })\n return { events, untilSeq: live.seq }\n }\n\n ledgerFor(playerId: PlayerId) {\n return this.ledger.snapshot(playerId)\n }\n\n async command(roomId: RoomId, playerId: PlayerId, command: ClientCommand, isHost = false): Promise<{ seq: number }> {\n const live = this.requireRoom(roomId)\n try {\n if ('nonce' in command && command.nonce) {\n if (live.nonces.has(command.nonce)) throw fail('duplicate-nonce', 'duplicate play nonce')\n live.nonces.add(command.nonce)\n }\n switch (command.type) {\n case 'sit':\n await this.sitInternal(live, playerId, command.seat, sanitizeDisplayName(command.displayName), false)\n break\n case 'stand':\n this.stand(live, playerId)\n break\n case 'ready':\n await this.setReady(live, playerId, command.ready)\n break\n case 'bid':\n this.bid(live, playerId, command.action)\n break\n case 'double':\n this.double(live, playerId, command.action)\n break\n case 'mingPai':\n this.mingPai(live, playerId)\n break\n case 'rename':\n this.rename(live, playerId, command.title, isHost)\n break\n case 'play':\n this.play(live, playerId, command.cards)\n break\n case 'pass':\n this.pass(live, playerId)\n break\n case 'chat':\n this.chat(live, playerId, command.text)\n break\n case 'rematch':\n await this.rematch(live, playerId)\n break\n case 'hostKick':\n if (!isHost && live.room.hostPlayerId !== playerId) throw fail('auth', 'host only')\n this.kick(live, command.playerId)\n break\n case 'hostClose':\n if (!isHost && live.room.hostPlayerId !== playerId) throw fail('auth', 'host only')\n await this.closeRoom(live)\n break\n case 'ping':\n this.emit(live, { type: 'pong', ts: Date.now() }, playerId)\n return { seq: live.seq }\n default:\n throw fail('illegal', 'unknown command')\n }\n this.broadcast(live)\n return { seq: live.seq }\n } catch (error) {\n this.counters.bump('cmdRejected')\n throw error\n }\n }\n\n markConnected(playerId: PlayerId, roomId: RoomId): void {\n const live = this.rooms.get(roomId)\n if (!live) return\n live.disconnectedAt.delete(playerId)\n live.autoPlay.delete(this.seatOf(live, playerId) ?? 0)\n this.setConnected(live, playerId, true)\n this.broadcast(live)\n }\n\n markDisconnected(playerId: PlayerId, roomId: RoomId): void {\n const live = this.rooms.get(roomId)\n if (!live) return\n live.disconnectedAt.set(playerId, Date.now())\n this.setConnected(live, playerId, false)\n this.broadcast(live)\n }\n\n listenPort = (): number => this.ctx?.webServer?.port ?? 3080\n\n private async hydrate(storage: unknown): Promise<void> {\n const opened = await openDoudizhuDomain(storage)\n if (!opened) return\n this.domain = opened\n this.ledger = new Ledger(this.domain, this.config.welcomeAtoms)\n }\n\n private uniqueCode(): string {\n for (let i = 0; i < 20; i += 1) {\n const code = randomRoomCode()\n if (!this.roomsByCode.has(code)) return code\n }\n throw new Error('could not allocate room code')\n }\n\n private roomByCode(code: string): LiveRoom | undefined {\n const id = this.roomsByCode.get(code)\n return id ? this.rooms.get(id) : undefined\n }\n\n private requireRoom(roomId: RoomId): LiveRoom {\n const live = this.rooms.get(roomId)\n if (!live) throw fail('expired', 'room not found')\n return live\n }\n\n private firstEmptySeat(live: LiveRoom): Seat | null {\n const empty = live.room.seats.find((seat) => seat.playerId === null)\n return empty ? empty.seat : null\n }\n\n private addSpectator(live: LiveRoom, playerId: PlayerId): void {\n if (live.room.spectatorIds.includes(playerId)) return\n if (live.room.spectatorIds.length >= MAX_SPECTATORS) throw fail('room-full', 'too many spectators')\n live.room = { ...live.room, spectatorIds: [...live.room.spectatorIds, playerId] }\n }\n\n private async sitInternal(live: LiveRoom, playerId: PlayerId, seat: Seat, displayName: string, isHost: boolean): Promise<void> {\n if (live.room.phase !== 'waiting') throw fail('phase', 'cannot sit now')\n const target = live.room.seats[seat]\n if (!target) throw fail('illegal', 'no such seat')\n if (target.playerId && target.playerId !== playerId) throw fail('room-full', 'seat taken')\n this.stand(live, playerId, false)\n const cap = seatCapAtoms(live.room.stakeAtoms, live.room.maxMultiplier, live.room.seatCount)\n const grant = issueHostGrant(playerId, live.room.roomId, cap)\n await persistGrant(this.domain, grant)\n const ready = isHost || live.room.phase === 'waiting'\n if (ready && this.ledger.getEscrow(playerId) < cap) {\n if (this.ledger.getAvailable(playerId) < cap) throw fail('insufficient', 'need more welcome atoms')\n await this.ledger.freeze(playerId, cap, live.room.roomId)\n }\n const seats = live.room.seats.map((item) => item.seat === seat\n ? {\n ...item,\n playerId,\n displayName,\n avatarUrl: null,\n ready,\n connected: true,\n role: 'empty' as const,\n grantId: grant.grantId,\n cardsLeft: 0,\n }\n : item)\n live.room = {\n ...live.room,\n seats,\n spectatorIds: live.room.spectatorIds.filter((id) => id !== playerId),\n }\n }\n\n private stand(live: LiveRoom, playerId: PlayerId, persist = true): void {\n if (live.room.phase !== 'waiting' && persist) throw fail('phase', 'cannot stand now')\n const seats = live.room.seats.map((item) => item.playerId === playerId ? emptySeat(item.seat) : item)\n live.room = { ...live.room, seats }\n live.sitConsumed = live.room.phase !== 'waiting' || this.firstEmptySeat(live) === null\n if (persist && this.ledger.getEscrow(playerId) > 0n) {\n void this.ledger.unfreeze(playerId, this.ledger.getEscrow(playerId), live.room.roomId)\n }\n }\n\n private async setReady(live: LiveRoom, playerId: PlayerId, ready: boolean): Promise<void> {\n if (live.room.phase !== 'waiting') throw fail('phase', 'not waiting')\n const seat = this.seatOf(live, playerId)\n if (seat === null) throw fail('illegal', 'not seated')\n if (ready) {\n const cap = seatCapAtoms(live.room.stakeAtoms, live.room.maxMultiplier, live.room.seatCount)\n if (this.ledger.getAvailable(playerId) < cap) throw fail('insufficient', 'need more welcome atoms')\n if (this.ledger.getEscrow(playerId) < cap) await this.ledger.freeze(playerId, cap, live.room.roomId)\n } else if (this.ledger.getEscrow(playerId) > 0n) {\n await this.ledger.unfreeze(playerId, this.ledger.getEscrow(playerId), live.room.roomId)\n }\n this.patchSeat(live, seat, { ready })\n await this.maybeDeal(live)\n }\n\n private async maybeDeal(live: LiveRoom): Promise<void> {\n const seated = live.room.seats\n if (!seated.every((seat) => seat.playerId && seat.ready && seat.grantId)) return\n const dealer = live.lastLandlordSeat === null ? null : nextSeat(live.lastLandlordSeat, live.room.seatCount)\n live.engine = createHand(live.room.roomId, dealer, this.counters.handsStarted + 1, {\n seatCount: live.room.seatCount,\n laiZi: live.room.laiZi,\n })\n this.counters.bump('handsStarted')\n live.room = {\n ...live.room,\n phase: 'bidding',\n currentHandId: live.engine.hand.handId,\n seats: seated.map((seat) => ({\n ...seat,\n role: 'farmer' as const,\n cardsLeft: dealtHandSize(live.room.seatCount),\n })),\n }\n live.deadlineAt = Date.now() + this.config.turnTimeoutMs\n }\n\n private bid(live: LiveRoom, playerId: PlayerId, action: BidAction): void {\n const engine = this.requireEngine(live, 'bidding')\n const seat = this.requireSeat(live, playerId)\n const result = applyBid(engine, seat, action)\n if (!result.ok) throw fail(result.code, result.reason)\n if (engine.phase === 'redeal') {\n const dealer = engine.hand.dealerSeat\n live.engine = createHand(live.room.roomId, dealer, this.counters.handsStarted + 1, {\n seatCount: live.room.seatCount,\n laiZi: live.room.laiZi,\n })\n this.counters.bump('handsStarted')\n live.room = { ...live.room, currentHandId: live.engine.hand.handId, phase: 'bidding' }\n live.deadlineAt = Date.now() + this.config.turnTimeoutMs\n return\n }\n if (engine.phase === 'doubling' && engine.hand.landlordSeat !== null) {\n this.patchSeat(live, engine.hand.landlordSeat, {\n role: 'landlord',\n cardsLeft: landlordHandSize(live.room.seatCount),\n })\n live.room = { ...live.room, phase: 'doubling' }\n live.deadlineAt = engine.doubleDeadlineAt\n }\n }\n\n private mingPai(live: LiveRoom, playerId: PlayerId): void {\n if (!live.engine) throw fail('phase', 'no hand')\n const seat = this.requireSeat(live, playerId)\n const result = applyMingPai(live.engine, seat)\n if (!result.ok) throw fail(result.code, result.reason)\n }\n\n private rename(live: LiveRoom, playerId: PlayerId, title: string, isHost: boolean): void {\n if (!isHost && live.room.hostPlayerId !== playerId) throw fail('auth', 'host only')\n live.room = { ...live.room, title: sanitizeRoomTitle(title, live.room.title) }\n void this.persistRoom(live)\n }\n\n private double(live: LiveRoom, playerId: PlayerId, action: DoubleAction): void {\n const engine = this.requireEngine(live, 'doubling')\n const seat = this.requireSeat(live, playerId)\n const result = applyDouble(engine, seat, action)\n if (!result.ok) throw fail(result.code, result.reason)\n if (engine.phase === 'playing') {\n live.room = { ...live.room, phase: 'playing' }\n live.deadlineAt = Date.now() + this.config.turnTimeoutMs\n }\n }\n\n private play(live: LiveRoom, playerId: PlayerId, cards: CardId[]): void {\n const engine = this.requireEngine(live, 'playing')\n const seat = this.requireSeat(live, playerId)\n const result = applyPlay(engine, seat, cards, live.seq + 1, nowIso())\n if (!result.ok) throw fail(result.code, result.reason)\n this.patchSeat(live, seat, { cardsLeft: engine.hand.hands[seat]?.length ?? 0 })\n if (engine.phase === 'settled') void this.settle(live)\n else live.deadlineAt = Date.now() + this.config.turnTimeoutMs\n }\n\n private pass(live: LiveRoom, playerId: PlayerId): void {\n const engine = this.requireEngine(live, 'playing')\n const seat = this.requireSeat(live, playerId)\n const result = applyPass(engine, seat, live.seq + 1, nowIso())\n if (!result.ok) throw fail(result.code, result.reason)\n live.deadlineAt = Date.now() + this.config.turnTimeoutMs\n }\n\n private chat(live: LiveRoom, playerId: PlayerId, text: string): void {\n const cleaned = text.replace(/[\\u0000-\\u001f]/g, '').slice(0, MAX_CHAT)\n if (!cleaned) return\n const name = this.displayNameOf(live, playerId)\n live.chat.push({ playerId, displayName: name, text: cleaned, ts: nowIso() })\n live.chat = live.chat.slice(-50)\n }\n\n private async rematch(live: LiveRoom, playerId: PlayerId): Promise<void> {\n if (live.room.phase !== 'waiting') throw fail('phase', 'not waiting')\n const seated = this.seatOf(live, playerId) !== null || live.room.hostPlayerId === playerId\n if (!seated) throw fail('illegal', 'not seated')\n for (const seat of live.room.seats) {\n if (seat.playerId) await this.setReady(live, seat.playerId, true)\n }\n }\n\n private kick(live: LiveRoom, playerId: PlayerId): void {\n if (live.room.phase !== 'waiting') throw fail('phase', 'cannot kick now')\n this.stand(live, playerId)\n live.room = { ...live.room, spectatorIds: live.room.spectatorIds.filter((id) => id !== playerId) }\n void this.persistRoom(live)\n }\n\n private async closeRoom(live: LiveRoom): Promise<void> {\n if (live.engine && (live.room.phase === 'playing' || live.room.phase === 'bidding' || live.room.phase === 'doubling')) {\n await this.voidHand(live)\n }\n for (const seat of live.room.seats) {\n if (seat.playerId && this.ledger.getEscrow(seat.playerId) > 0n) {\n await this.ledger.unfreeze(seat.playerId, this.ledger.getEscrow(seat.playerId), live.room.roomId)\n }\n }\n live.room = { ...live.room, phase: 'closed' }\n }\n\n private async settle(live: LiveRoom): Promise<void> {\n const engine = live.engine\n if (!engine || engine.hand.landlordSeat === null) return\n live.room = { ...live.room, phase: 'settling' }\n const playerIds = live.room.seats.map((seat) => seat.playerId) as PlayerId[]\n if (playerIds.some((id) => !id)) return\n const landlord = engine.hand.landlordSeat\n const winner = (engine.hand.hands[landlord] ?? []).length === 0 ? 'landlord' : 'farmers'\n const settlement = scoreHand({\n hand: snapshotHand(engine),\n stakeAtoms: live.room.stakeAtoms,\n maxMultiplier: live.room.maxMultiplier,\n winner,\n playerIds,\n })\n try {\n const landlordId = playerIds[landlord]\n if (!landlordId) throw fail('illegal', 'missing landlord')\n if (winner === 'landlord') {\n for (const delta of settlement.deltas) {\n const atoms = parseAtoms(delta.atoms)\n if (atoms < 0n) {\n await this.ledger.settleTransfer(delta.playerId, landlordId, -atoms, {\n roomId: live.room.roomId,\n handId: settlement.handId,\n settlementId: settlement.settlementId,\n })\n }\n }\n } else {\n for (const delta of settlement.deltas) {\n const atoms = parseAtoms(delta.atoms)\n if (atoms > 0n) {\n await this.ledger.settleTransfer(landlordId, delta.playerId, atoms, {\n roomId: live.room.roomId,\n handId: settlement.handId,\n settlementId: settlement.settlementId,\n })\n }\n }\n }\n for (const seat of live.room.seats) {\n if (seat.playerId) await this.ledger.unfreeze(seat.playerId, this.ledger.getEscrow(seat.playerId), live.room.roomId)\n }\n } catch (error) {\n live.needsAudit = true\n this.counters.needsAudit = true\n this.ctx?.logger?.warn(error)\n throw fail('illegal', 'settlement failed')\n }\n const publicSettlement: PublicSettlement = {\n ...settlement,\n trustNote: '好友局信任房主机器上的账本。积分 ≠ DeepSeek 平台余额。',\n }\n live.lastSettlement = publicSettlement\n live.lastLandlordSeat = landlord\n this.counters.bump('settlementsCommitted')\n live.engine = null\n live.room = {\n ...live.room,\n phase: 'waiting',\n currentHandId: null,\n seats: live.room.seats.map((seat) => ({\n ...seat,\n ready: false,\n role: 'empty' as const,\n cardsLeft: 0,\n })),\n }\n live.deadlineAt = null\n this.push(live, { type: 'settled', seq: ++live.seq, settlement: publicSettlement })\n }\n\n private async voidHand(live: LiveRoom): Promise<void> {\n for (const seat of live.room.seats) {\n if (seat.playerId) await this.ledger.unfreeze(seat.playerId, this.ledger.getEscrow(seat.playerId), live.room.roomId, 'void')\n }\n live.engine = null\n live.room = {\n ...live.room,\n phase: 'waiting',\n currentHandId: null,\n seats: live.room.seats.map((seat) => ({ ...seat, ready: false, role: 'empty' as const, cardsLeft: 0 })),\n }\n this.counters.bump('handsVoided')\n }\n\n private onTick(now: number): void {\n for (const live of this.rooms.values()) {\n if (live.room.phase === 'doubling' && live.engine) {\n expireDoubling(live.engine, now)\n if (live.engine.phase === 'playing') {\n live.room = { ...live.room, phase: 'playing' }\n live.deadlineAt = now + this.config.turnTimeoutMs\n this.broadcast(live)\n }\n }\n if (live.deadlineAt && now >= live.deadlineAt && live.engine && live.room.phase === 'playing') {\n const result = autoTimeout(live.engine, live.seq + 1, nowIso())\n if (result) {\n const seat = live.engine.hand.turnSeat\n const previous = prevSeat(seat, live.room.seatCount)\n this.patchSeat(live, previous, { cardsLeft: live.engine.hand.hands[previous]!.length })\n if (live.engine.phase === 'settled') void this.settle(live)\n else live.deadlineAt = now + this.config.turnTimeoutMs\n this.broadcast(live)\n }\n }\n for (const [playerId, at] of live.disconnectedAt) {\n if (now - at < this.config.reconnectWindowMs) continue\n const seat = this.seatOf(live, playerId)\n if (seat !== null && (live.room.phase === 'playing' || live.room.phase === 'bidding' || live.room.phase === 'doubling')) {\n live.autoPlay.add(seat)\n }\n }\n const seated = live.room.seats.filter((seat) => seat.playerId)\n if (seated.length > 0 && seated.every((seat) => !seat.connected) && live.disconnectedAt.size === live.room.seatCount) {\n const oldest = Math.min(...live.disconnectedAt.values())\n if (now - oldest > this.config.reconnectWindowMs && live.engine) {\n void this.voidHand(live)\n this.broadcast(live)\n }\n }\n }\n }\n\n private requireEngine(live: LiveRoom, phase: EngineState['phase']): EngineState {\n if (!live.engine || live.engine.phase !== phase) throw fail('phase', `expected ${phase}`)\n return live.engine\n }\n\n private requireSeat(live: LiveRoom, playerId: PlayerId): Seat {\n const seat = this.seatOf(live, playerId)\n if (seat === null) throw fail('illegal', 'not seated')\n return seat\n }\n\n private seatOf(live: LiveRoom, playerId: PlayerId): Seat | null {\n return live.room.seats.find((seat) => seat.playerId === playerId)?.seat ?? null\n }\n\n private displayNameOf(live: LiveRoom, playerId: PlayerId): string {\n return live.room.seats.find((seat) => seat.playerId === playerId)?.displayName\n ?? '玩家'\n }\n\n private patchSeat(live: LiveRoom, seat: Seat, patch: Partial<SeatState>): void {\n live.room = {\n ...live.room,\n seats: live.room.seats.map((item) => item.seat === seat ? { ...item, ...patch } : item),\n }\n }\n\n private setConnected(live: LiveRoom, playerId: PlayerId, connected: boolean): void {\n const seat = this.seatOf(live, playerId)\n if (seat !== null) this.patchSeat(live, seat, { connected })\n }\n\n private viewFor(live: LiveRoom, playerId: PlayerId): PlayerView {\n const seat = this.seatOf(live, playerId)\n const spectator = seat === null\n const engine = live.engine\n const cards = seat !== null && engine ? [...(engine.hand.hands[seat] ?? [])] : []\n const legal = seat !== null && engine ? legalFor(engine, seat) : { canPass: false, combos: [] }\n const bottomRevealed = Boolean(engine && engine.hand.landlordSeat !== null)\n const remainingRanks = spectator && this.config.spectatorCardCounter\n ? this.remainingRanks(live)\n : null\n return {\n room: {\n ...live.room,\n stakeAtoms: asTokenAtomString(live.room.stakeAtoms),\n seats: live.room.seats.map((item) => ({\n ...item,\n cardsLeft: engine ? (engine.hand.hands[item.seat]?.length ?? 0) : item.cardsLeft,\n role: engine?.hand.landlordSeat === item.seat\n ? 'landlord'\n : item.playerId\n ? (engine ? 'farmer' : item.role)\n : 'empty',\n })),\n },\n you: { playerId, seat, spectator, cards },\n publicHands: live.room.seats.map((item, index) => (\n engine ? (engine.hand.hands[index]?.length ?? 0) : item.cardsLeft\n )),\n lastPlays: engine ? engine.hand.table.slice(-6) : [],\n bottom: bottomRevealed && engine ? [...engine.hand.bottom] : null,\n laiZiRanks: engine?.hand.laiZiRanks ?? [],\n bid: engine?.hand.bid ?? 0,\n auction: engine && engine.phase === 'bidding'\n ? { kind: engine.called ? 'rob' : 'call', multiplier: Math.max(1, engine.hand.bid) }\n : null,\n revealedBySeat: this.revealedHands(live),\n mingPaiBySeat: engine ? { ...engine.hand.mingPaiBySeat } : {},\n turnSeat: engine?.hand.turnSeat ?? null,\n leadSeat: engine?.hand.leadSeat ?? null,\n deadlineAt: live.deadlineAt ? new Date(live.deadlineAt).toISOString() : null,\n yourAvailableAtoms: asTokenAtomString(this.ledger.getAvailable(playerId)),\n yourEscrowAtoms: asTokenAtomString(this.ledger.getEscrow(playerId)),\n legal,\n remainingRanks,\n chat: live.chat.slice(-20),\n }\n }\n\n private revealedHands(live: LiveRoom): Partial<Record<Seat, CardId[]>> {\n const engine = live.engine\n if (!engine) return {}\n const out: Partial<Record<Seat, CardId[]>> = {}\n for (const [raw, shown] of Object.entries(engine.hand.mingPaiBySeat)) {\n if (!shown) continue\n const seat = Number(raw) as Seat\n out[seat] = [...(engine.hand.hands[seat] ?? [])]\n }\n return out\n }\n\n private remainingRanks(live: LiveRoom): Record<string, number> | null {\n if (!live.engine) return null\n const seen = new Map<string, number>()\n const bump = (card: CardId): void => {\n const rank = rankOf(card)\n seen.set(rank, (seen.get(rank) ?? 0) + 1)\n }\n for (const play of live.engine.hand.table) {\n for (const card of play.cards) bump(card)\n }\n if (live.engine.hand.landlordSeat !== null) {\n for (const card of live.engine.hand.bottom) bump(card)\n }\n const decks = decksFor(live.room.seatCount)\n const totals: Record<string, number> = {\n '3': 4 * decks, '4': 4 * decks, '5': 4 * decks, '6': 4 * decks, '7': 4 * decks,\n '8': 4 * decks, '9': 4 * decks, '10': 4 * decks, J: 4 * decks, Q: 4 * decks,\n K: 4 * decks, A: 4 * decks, '2': 4 * decks, BJ: decks, RJ: decks,\n }\n const remaining: Record<string, number> = {}\n for (const [rank, total] of Object.entries(totals)) {\n remaining[rank] = total - (seen.get(rank) ?? 0)\n }\n return remaining\n }\n\n private async persistRoom(live: LiveRoom): Promise<void> {\n await this.domain.tables.rooms.put(live.room.roomId, {\n roomId: live.room.roomId,\n roomCode: live.room.roomCode,\n title: live.room.title,\n hostPlayerId: live.room.hostPlayerId,\n phase: live.room.phase,\n stakeAtoms: live.room.stakeAtoms.toString(),\n maxMultiplier: live.room.maxMultiplier,\n seatCount: live.room.seatCount,\n laiZi: live.room.laiZi,\n inviteExpiresAt: live.room.inviteExpiresAt,\n createdAt: live.room.createdAt,\n shareable: live.room.shareable,\n roomSecret: live.secret,\n sitInviteHash: live.sitInviteHash,\n watchInviteHash: live.watchInviteHash,\n lastLandlordSeat: live.lastLandlordSeat,\n })\n }\n\n private broadcast(live: LiveRoom): void {\n live.seq += 1\n const ids = new Set<PlayerId>([\n ...live.room.seats.map((seat) => seat.playerId).filter((id): id is PlayerId => Boolean(id)),\n ...live.room.spectatorIds,\n ])\n for (const playerId of ids) {\n this.emit(live, { type: 'snapshot', seq: live.seq, view: this.viewFor(live, playerId) }, playerId)\n }\n }\n\n private push(live: LiveRoom, event: ServerEvent): void {\n live.events.push(event)\n live.events = live.events.slice(-200)\n const ids = new Set<PlayerId>([\n ...live.room.seats.map((seat) => seat.playerId).filter((id): id is PlayerId => Boolean(id)),\n ...live.room.spectatorIds,\n ])\n for (const playerId of ids) this.emit(live, event, playerId)\n }\n\n private emit(_live: LiveRoom, event: ServerEvent, playerId: PlayerId): void {\n for (const listener of this.listeners) listener(playerId, event)\n }\n}\n\nfunction emptySeat(seat: Seat): SeatState {\n return {\n seat,\n playerId: null,\n displayName: null,\n avatarUrl: null,\n ready: false,\n connected: false,\n role: 'empty',\n grantId: null,\n cardsLeft: 0,\n }\n}\n\nexport function fail(code: RejectCode, reason: string): Error & { code: RejectCode } {\n return Object.assign(new Error(reason), { code })\n}\n","import type { IncomingMessage } from '@deepseek-ai/cordis'\n\nexport function isLoopbackHost(hostHeader: string | undefined, remoteAddress: string | undefined): boolean {\n const host = (hostHeader ?? '').split(':')[0]?.replace(/^\\[|\\]$/g, '').toLowerCase()\n const remote = (remoteAddress ?? '').replace(/^::ffff:/, '')\n const loopbackHosts = new Set(['127.0.0.1', 'localhost', '::1'])\n const loopbackRemotes = new Set(['127.0.0.1', '::1', '::ffff:127.0.0.1'])\n return loopbackHosts.has(host ?? '') || loopbackRemotes.has(remote)\n}\n\nexport function requestOrigin(req: IncomingMessage): string | null {\n const origin = header(req, 'origin')\n if (origin) {\n try { return new URL(origin).origin } catch { return null }\n }\n const referer = header(req, 'referer')\n if (referer) {\n try { return new URL(referer).origin } catch { return null }\n }\n return null\n}\n\nexport function originAllowed(origin: string | null, publicBaseUrl: string, browser: boolean): boolean {\n if (!origin) return !browser\n if (isLoopbackOrigin(origin)) return true\n if (publicBaseUrl && origin === publicBaseUrl) return true\n return false\n}\n\nexport function isLoopbackOrigin(origin: string): boolean {\n try {\n const url = new URL(origin)\n return url.hostname === '127.0.0.1' || url.hostname === 'localhost' || url.hostname === '::1' || url.hostname === '[::1]'\n } catch {\n return false\n }\n}\n\nexport function isBrowserRequest(req: IncomingMessage): boolean {\n return Boolean(header(req, 'origin') || header(req, 'sec-fetch-site'))\n}\n\nexport function assertOrigin(req: IncomingMessage, publicBaseUrl: string): void {\n if (header(req, 'sec-fetch-site') === 'cross-site') {\n throw Object.assign(new Error('cross-site forbidden'), { status: 403, code: 'auth' })\n }\n if (!originAllowed(requestOrigin(req), publicBaseUrl, isBrowserRequest(req))) {\n throw Object.assign(new Error('origin not allowed'), { status: 403, code: 'auth' })\n }\n}\n\nexport function assertMutatingHeaders(req: IncomingMessage): void {\n const requested = header(req, 'x-requested-with')\n const contentType = header(req, 'content-type') ?? ''\n if (requested === 'doudizhu') return\n if (contentType.includes('application/json')) return\n throw Object.assign(new Error('missing csrf header'), { status: 403, code: 'auth' })\n}\n\nexport function parseCookies(req: IncomingMessage): Record<string, string> {\n const raw = header(req, 'cookie') ?? ''\n const out: Record<string, string> = {}\n for (const part of raw.split(';')) {\n const index = part.indexOf('=')\n if (index < 0) continue\n const key = part.slice(0, index).trim()\n const value = part.slice(index + 1).trim()\n if (key) out[key] = decodeURIComponent(value)\n }\n return out\n}\n\nexport function cookieHeader(\n name: string,\n value: string,\n opts: { secure: boolean; sameSite: 'Lax' | 'Strict'; maxAge?: number },\n): string {\n const parts = [\n `${name}=${encodeURIComponent(value)}`,\n 'Path=/doudizhu',\n 'HttpOnly',\n `SameSite=${opts.sameSite}`,\n `Max-Age=${opts.maxAge ?? 86400}`,\n ]\n if (opts.secure) parts.push('Secure')\n return parts.join('; ')\n}\n\nexport function header(req: IncomingMessage, name: string): string | undefined {\n const value = req.headers[name]\n return Array.isArray(value) ? value[0] : value\n}\n","/** JSON wire encoding: bigint atoms become decimal strings. */\nexport function encodeJson(body: unknown): string {\n return JSON.stringify(body, (_key, value) => (typeof value === 'bigint' ? value.toString() : value))\n}\n","import { readFileSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst here = dirname(fileURLToPath(import.meta.url))\n\nexport function joinPageHtml(): string {\n const candidates = [\n join(here, 'join/index.html'),\n join(here, '../join/index.html'),\n join(process.cwd(), 'lib/join/index.html'),\n join(process.cwd(), 'src/join/join.html'),\n ]\n for (const path of candidates) {\n try {\n return readFileSync(path, 'utf8')\n } catch {\n // try next\n }\n }\n return `<!doctype html><html><head><meta charset=\"utf-8\"><title>斗地主</title></head><body><p>加入页尚未构建。请运行 pnpm build。</p></body></html>`\n}\n\nexport function joinAsset(name: string): { body: Buffer; type: string } | null {\n const candidates = [\n join(here, 'join', name),\n join(here, '../join', name),\n join(process.cwd(), 'lib/join', name),\n join(process.cwd(), 'src/client/assets', name),\n ]\n for (const path of candidates) {\n try {\n const body = readFileSync(path)\n return { body, type: typeOf(name) }\n } catch {\n // try next\n }\n }\n return null\n}\n\nfunction typeOf(name: string): string {\n if (name.endsWith('.js')) return 'application/javascript; charset=utf-8'\n if (name.endsWith('.css')) return 'text/css; charset=utf-8'\n if (name.endsWith('.svg')) return 'image/svg+xml'\n if (name.endsWith('.jpg') || name.endsWith('.jpeg')) return 'image/jpeg'\n if (name.endsWith('.png')) return 'image/png'\n if (name.endsWith('.map')) return 'application/json'\n return 'application/octet-stream'\n}\n","import type { IncomingMessage, ServerResponse, WebServer } from '@deepseek-ai/cordis'\nimport type { RoomManager } from '../room/RoomManager.ts'\nimport { fail } from '../room/RoomManager.ts'\nimport type { ClientCommand, RejectCode } from '../types.ts'\nimport {\n assertMutatingHeaders, assertOrigin, cookieHeader, header, isLoopbackHost, parseCookies,\n} from './auth.ts'\nimport { encodeJson } from './json.ts'\nimport { joinAsset, joinPageHtml } from './join-page.ts'\n\nexport function registerDouDizhuHttp(server: WebServer, manager: RoomManager): Array<() => void> {\n const prefix = manager.getConfig().routePrefix\n return [\n server.register({\n kind: 'prefix',\n path: prefix,\n handler: (req, res) => handle(req as IncomingMessage, res as ServerResponse, manager, prefix),\n }),\n ]\n}\n\nasync function handle(req: IncomingMessage, res: ServerResponse, manager: RoomManager, prefix: string): Promise<void> {\n try {\n const url = new URL(req.url ?? '/', 'http://127.0.0.1')\n const path = url.pathname\n if (req.method === 'GET' && (path === prefix || path === `${prefix}/` || path === `${prefix}/join`)) {\n html(res, joinPageHtml())\n return\n }\n if (req.method === 'GET' && path.startsWith(`${prefix}/assets/`)) {\n const name = path.slice(`${prefix}/assets/`.length)\n const asset = joinAsset(name)\n if (!asset) {\n send(res, 404, '')\n return\n }\n send(res, 200, asset.body, { 'content-type': asset.type, 'cache-control': 'no-store' })\n return\n }\n if (req.method === 'GET' && (path === `${prefix}/join.js` || path === `${prefix}/index.js`)) {\n const asset = joinAsset('index.js')\n if (!asset) {\n send(res, 404, '')\n return\n }\n send(res, 200, asset.body, { 'content-type': asset.type, 'cache-control': 'no-store' })\n return\n }\n if (path.startsWith(`${prefix}/api/`)) {\n await handleApi(req, res, manager, prefix, url)\n return\n }\n send(res, 404, encodeJson({ error: 'expired', message: 'not found' }), {\n 'content-type': 'application/json',\n })\n } catch (error) {\n writeError(res, error)\n }\n}\n\nasync function handleApi(\n req: IncomingMessage,\n res: ServerResponse,\n manager: RoomManager,\n prefix: string,\n url: URL,\n): Promise<void> {\n const config = manager.getConfig()\n const path = url.pathname.slice(prefix.length)\n const cookies = parseCookies(req)\n const secure = config.publicBaseUrl.startsWith('https://')\n\n if (req.method === 'POST' && path === '/api/join') {\n assertOrigin(req, config.publicBaseUrl)\n assertMutatingHeaders(req)\n const body = await readJson<{ roomCode: string; invite: string; displayName: string; role: 'sit' | 'watch' }>(req)\n const loopback = isLoopbackHost(header(req, 'host'), req.socket.remoteAddress)\n const result = await manager.join({\n roomCode: String(body.roomCode ?? ''),\n invite: String(body.invite ?? ''),\n displayName: String(body.displayName ?? ''),\n role: body.role === 'watch' ? 'watch' : 'sit',\n local: loopback,\n })\n json(res, 200, {\n playerId: result.playerId,\n roomId: result.roomId,\n seat: result.seat,\n wsTicket: result.wsTicket,\n view: result.view,\n }, [cookieHeader('ddz_seat', result.cookie, { secure, sameSite: 'Lax' })])\n return\n }\n\n if (req.method === 'POST' && path === '/api/rooms') {\n assertOrigin(req, config.publicBaseUrl)\n assertMutatingHeaders(req)\n const loopback = isLoopbackHost(header(req, 'host'), req.socket.remoteAddress)\n if (!loopback && !manager.isHostCookie(cookies.ddz_host)) {\n throw Object.assign(new Error('createRoom requires loopback or host cookie'), { status: 403, code: 'auth' })\n }\n const body = await readJson<{\n stakeM?: number\n maxMultiplier?: number\n seatCount?: 3 | 4\n laiZi?: boolean\n hostDisplayName?: string\n title?: string\n }>(req)\n try {\n const created = await manager.createRoom({\n stakeM: Number(body.stakeM ?? config.defaultStakeM),\n maxMultiplier: Number(body.maxMultiplier ?? config.defaultMaxMultiplier),\n seatCount: body.seatCount === 4 ? 4 : 3,\n laiZi: body.laiZi === true,\n ...(body.hostDisplayName ? { hostDisplayName: body.hostDisplayName } : {}),\n ...(body.title ? { title: body.title } : {}),\n }, cookies.ddz_host)\n json(res, 200, {\n roomId: created.roomId,\n roomCode: created.roomCode,\n sitUrl: created.sitUrl,\n watchUrl: created.watchUrl,\n seatCapAtoms: created.seatCapAtoms,\n shareable: created.shareable,\n hostPlayerId: created.hostPlayerId,\n view: created.view,\n wsTicket: manager.issueTicket(created.hostPlayerId, created.roomId),\n }, [\n cookieHeader('ddz_host', created.hostCookie, { secure, sameSite: 'Strict' }),\n cookieHeader('ddz_seat', created.hostCookie, { secure, sameSite: 'Lax' }),\n ])\n } catch (error) {\n if (error instanceof Error && error.name === 'welcome-below-seatcap') {\n throw Object.assign(error, { status: 409, code: 'insufficient' })\n }\n throw error\n }\n return\n }\n\n if (req.method === 'GET' && path === '/api/health') {\n if (!manager.isHostCookie(cookies.ddz_host)) throw Object.assign(new Error('host only'), { status: 403, code: 'auth' })\n json(res, 200, manager.health())\n return\n }\n\n if (req.method === 'GET' && path === '/api/me/ledger') {\n const session = manager.sessionByCookie(cookies.ddz_seat)\n if (!session) throw Object.assign(new Error('not joined'), { status: 403, code: 'auth' })\n json(res, 200, manager.ledgerFor(session.playerId))\n return\n }\n\n const roomMatch = /^\\/api\\/rooms\\/([^/]+)(?:\\/(since|command))?$/.exec(path)\n if (!roomMatch) {\n send(res, 404, encodeJson({ error: 'expired', message: 'not found' }), {\n 'content-type': 'application/json',\n })\n return\n }\n const roomId = roomMatch[1]!\n const tail = roomMatch[2]\n const session = manager.sessionByCookie(cookies.ddz_seat)\n if (!session || session.roomId !== roomId) {\n throw Object.assign(new Error('not in this room'), { status: 403, code: 'auth' })\n }\n\n if (req.method === 'GET' && !tail) {\n json(res, 200, manager.view(session.roomId, session.playerId))\n return\n }\n if (req.method === 'GET' && tail === 'since') {\n const seq = Number(url.searchParams.get('seq') ?? '0')\n json(res, 200, manager.eventsSince(session.roomId, session.playerId, Number.isFinite(seq) ? seq : 0))\n return\n }\n if (req.method === 'POST' && tail === 'command') {\n assertOrigin(req, config.publicBaseUrl)\n assertMutatingHeaders(req)\n const command = await readJson<ClientCommand>(req)\n try {\n const result = await manager.command(session.roomId, session.playerId, command, manager.isHostCookie(cookies.ddz_host))\n json(res, 200, result)\n } catch (error) {\n const code = (error as { code?: RejectCode }).code ?? 'illegal'\n send(res, 409, encodeJson({ error: code, message: error instanceof Error ? error.message : 'conflict' }), {\n 'content-type': 'application/json',\n })\n }\n return\n }\n send(res, 404, '')\n}\n\nasync function readJson<T>(req: IncomingMessage): Promise<T> {\n const chunks: Buffer[] = []\n let size = 0\n await new Promise<void>((resolve, reject) => {\n req.on('data', (chunk) => {\n const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)\n size += buf.length\n if (size > 64 * 1024) {\n reject(Object.assign(new Error('payload too large'), { status: 400, code: 'illegal' }))\n return\n }\n chunks.push(buf)\n })\n req.on('end', () => { resolve() })\n req.on('error', reject)\n })\n if (chunks.length === 0) return {} as T\n try {\n return JSON.parse(Buffer.concat(chunks).toString('utf8')) as T\n } catch {\n throw Object.assign(new Error('invalid json'), { status: 400, code: 'illegal' })\n }\n}\n\nfunction json(res: ServerResponse, status: number, body: unknown, cookies?: string[]): void {\n const headers: Record<string, string | string[]> = {\n 'content-type': 'application/json; charset=utf-8',\n 'cache-control': 'no-store',\n }\n if (cookies && cookies.length > 0) headers['set-cookie'] = cookies\n send(res, status, encodeJson(body), headers)\n}\n\nfunction html(res: ServerResponse, body: string): void {\n send(res, 200, body, { 'content-type': 'text/html; charset=utf-8', 'cache-control': 'no-store' })\n}\n\nfunction send(\n res: ServerResponse,\n status: number,\n body: string | Buffer,\n headers: Record<string, string | string[]> = {},\n): void {\n if (res.headersSent) return\n res.writeHead(status, headers)\n res.end(body)\n}\n\nfunction writeError(res: ServerResponse, error: unknown): void {\n const status = (error as { status?: number }).status\n ?? ((error as { code?: string }).code === 'auth' ? 403 : 400)\n const code = (error as { code?: string }).code ?? 'illegal'\n if (error instanceof Error && error.message === 'welcome-below-seatcap') {\n send(res, 409, encodeJson({ error: 'insufficient', message: 'welcome-below-seatcap' }), {\n 'content-type': 'application/json',\n })\n return\n }\n const coded = error as { code?: RejectCode }\n if (error instanceof Error && coded.code) {\n send(res, statusFrom(coded), encodeJson({ error: coded.code, message: error.message }), {\n 'content-type': 'application/json',\n })\n return\n }\n void fail\n send(res, status, encodeJson({ error: code, message: error instanceof Error ? error.message : 'error' }), {\n 'content-type': 'application/json',\n })\n}\n\nfunction statusFrom(error: { code?: RejectCode }): number {\n if (error.code === 'auth') return 403\n if (error.code === 'expired') return 404\n if (error.code === 'room-full' || error.code === 'insufficient' || error.code === 'phase' || error.code === 'duplicate-nonce') return 409\n return 400\n}\n","import type { IncomingMessage } from 'node:http'\nimport type { Duplex } from 'node:stream'\nimport { WebSocket, WebSocketServer } from 'ws'\nimport type { WebServer } from '@deepseek-ai/cordis'\nimport { MAX_FRAME_BYTES, SUBPROTOCOL } from '../invariant.ts'\nimport type { RoomManager } from '../room/RoomManager.ts'\nimport type { ClientCommand, PlayerId, RoomId, ServerEvent } from '../types.ts'\nimport { assertOrigin, header } from './auth.ts'\nimport { encodeJson } from './json.ts'\n\ninterface SocketState {\n playerId: PlayerId\n roomId: RoomId\n lastSeen: number\n}\n\nexport function registerDouDizhuSocket(server: WebServer, manager: RoomManager): () => void {\n const wss = new WebSocketServer({ noServer: true, handleProtocols: (protocols) => (\n protocols.has(SUBPROTOCOL) ? SUBPROTOCOL : false\n ) })\n const sockets = new Map<WebSocket, SocketState>()\n const off = manager.onEvent((playerId, event) => {\n for (const [ws, state] of sockets) {\n if (state.playerId === playerId && ws.readyState === WebSocket.OPEN) send(ws, event)\n }\n })\n const heartbeat = setInterval(() => {\n const now = Date.now()\n for (const [ws, state] of sockets) {\n if (now - state.lastSeen > manager.getConfig().disconnectAfterMs) {\n ws.close()\n }\n }\n }, manager.getConfig().heartbeatMs)\n if (typeof heartbeat === 'object' && 'unref' in heartbeat) heartbeat.unref()\n\n const disposeRoute = server.registerUpgrade({\n path: `${manager.getConfig().routePrefix}/ws`,\n handler: (req, socket, head) => {\n try {\n assertOrigin(req as never, manager.getConfig().publicBaseUrl)\n } catch {\n socket.write('HTTP/1.1 403 Forbidden\\r\\nConnection: close\\r\\n\\r\\n')\n socket.destroy()\n return\n }\n const url = new URL(req.url ?? '/', 'http://127.0.0.1')\n const ticket = url.searchParams.get('ticket') ?? ''\n const consumed = manager.consumeTicket(ticket)\n if (!consumed) {\n socket.write('HTTP/1.1 401 Unauthorized\\r\\nConnection: close\\r\\n\\r\\n')\n socket.destroy()\n return\n }\n wss.handleUpgrade(req as never, socket as never, head, (ws) => {\n const state: SocketState = { playerId: consumed.playerId, roomId: consumed.roomId, lastSeen: Date.now() }\n sockets.set(ws, state)\n manager.counters.wsConnected += 1\n manager.markConnected(state.playerId, state.roomId)\n send(ws, { type: 'snapshot', seq: 0, view: manager.view(state.roomId, state.playerId) })\n ws.on('message', (data) => {\n if (Buffer.byteLength(data.toString()) > MAX_FRAME_BYTES) {\n ws.close()\n return\n }\n state.lastSeen = Date.now()\n void onMessage(ws, state, data.toString(), manager)\n })\n ws.on('close', () => {\n sockets.delete(ws)\n manager.counters.wsConnected = Math.max(0, manager.counters.wsConnected - 1)\n manager.markDisconnected(state.playerId, state.roomId)\n })\n })\n },\n })\n\n return () => {\n clearInterval(heartbeat)\n off()\n disposeRoute()\n wss.close()\n }\n}\n\nasync function onMessage(ws: WebSocket, state: SocketState, raw: string, manager: RoomManager): Promise<void> {\n let command: ClientCommand\n try {\n command = JSON.parse(raw) as ClientCommand\n } catch {\n send(ws, { type: 'reject', code: 'illegal', reason: 'invalid json' })\n return\n }\n try {\n const result = await manager.command(state.roomId, state.playerId, command)\n if (command.type === 'ping') send(ws, { type: 'pong', ts: Date.now() })\n void result\n } catch (error) {\n const code = (error as { code?: ServerEvent extends { code: infer C } ? C : never }).code ?? 'illegal'\n send(ws, { type: 'reject', code: (error as { code?: 'illegal' }).code ?? 'illegal', reason: error instanceof Error ? error.message : 'rejected' })\n void code\n }\n}\n\nfunction send(ws: WebSocket, event: ServerEvent): void {\n if (ws.readyState === WebSocket.OPEN) ws.send(encodeJson(event))\n}\n\nexport function protocolOf(req: IncomingMessage): string | undefined {\n return header(req as never, 'sec-websocket-protocol')\n}\n\nexport type { Duplex }\n","import type { Context } from '@deepseek-ai/cordis'\nimport { installSettingsSection, settingsNamespace } from '@deepseek-ai/dsh-settings'\nimport { Config, resolveConfig, type PluginConfig } from './config.ts'\nimport { registerDouDizhuHttp } from './net/routes.ts'\nimport { registerDouDizhuSocket } from './net/ws.ts'\nimport { RoomManager } from './room/RoomManager.ts'\n\nexport const name = 'doudizhu'\nexport const inject = ['webServer', 'settings']\nexport { Config }\nexport const DOUDIZHU_NS = settingsNamespace('doudizhu')\n\nexport function apply(ctx: Context, config: PluginConfig): void {\n const storage = ctx.get('storageDomain')\n const manager = new RoomManager(ctx, config, storage)\n manager.listenPort = () => ctx.webServer.port\n ctx.effect(() => {\n const stop = [\n ...registerDouDizhuHttp(ctx.webServer, manager),\n registerDouDizhuSocket(ctx.webServer, manager),\n ]\n return () => {\n for (const dispose of stop) dispose()\n void manager.dispose()\n }\n })\n let currentConfig = (): PluginConfig => config\n installSettingsSection(ctx, DOUDIZHU_NS, Config, config, {\n setSource: (current) => {\n currentConfig = current\n manager.replaceConfig(current())\n },\n onChange: () => { manager.replaceConfig(currentConfig()) },\n validate: (value) => { resolveConfig(value) },\n })\n ctx.inject(['systemPrompt'], (promptCtx) => {\n promptCtx.systemPrompt?.section({\n name: 'plugin:doudizhu',\n order: 200,\n text: '本机已安装 dsh-poker。开房走侧栏 New Session 下面的「斗地主」,首页可创建或加入,支持 3 人/4 人、经典/癞子。结算是 Host 欢迎积分账本,不是 DeepSeek 平台余额。',\n })\n })\n}\n"],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ //#region src/invariant.ts
2
+ const PACKAGE_NAME = "dsh-poker";
3
+ const CLIENT_ID = "dsh-poker";
4
+ const SUBPROTOCOL = "doudizhu.v1";
5
+ const MAX_FRAME_BYTES = 64 * 1024;
6
+ const MAX_DISPLAY_NAME = 16;
7
+ const MAX_ROOM_TITLE = 24;
8
+ const MAX_CHAT = 80;
9
+ const MAX_SPECTATORS = 16;
10
+
11
+ //#endregion
12
+ export { MAX_ROOM_TITLE as a, SUBPROTOCOL as c, MAX_FRAME_BYTES as i, MAX_CHAT as n, MAX_SPECTATORS as o, MAX_DISPLAY_NAME as r, PACKAGE_NAME as s, CLIENT_ID as t };
13
+ //# sourceMappingURL=invariant-C3ZiFskw.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invariant-C3ZiFskw.js","names":[],"sources":["../src/invariant.ts"],"sourcesContent":["export const PACKAGE_NAME = 'dsh-poker'\nexport const CLIENT_ID = 'dsh-poker'\nexport const SUBPROTOCOL = 'doudizhu.v1'\nexport const MAX_FRAME_BYTES = 64 * 1024\nexport const MAX_DISPLAY_NAME = 16\nexport const MAX_ROOM_TITLE = 24\nexport const MAX_CHAT = 80\nexport const MAX_SPECTATORS = 16\n"],"mappings":";AAAA,MAAa,eAAe;AAC5B,MAAa,YAAY;AACzB,MAAa,cAAc;AAC3B,MAAa,kBAAkB,KAAK;AACpC,MAAa,mBAAmB;AAChC,MAAa,iBAAiB;AAC9B,MAAa,WAAW;AACxB,MAAa,iBAAiB"}
@@ -0,0 +1,3 @@
1
+ import { a as MAX_ROOM_TITLE, c as SUBPROTOCOL, i as MAX_FRAME_BYTES, n as MAX_CHAT, o as MAX_SPECTATORS, r as MAX_DISPLAY_NAME, s as PACKAGE_NAME, t as CLIENT_ID } from "./invariant-C3ZiFskw.js";
2
+
3
+ export { CLIENT_ID, MAX_CHAT, MAX_DISPLAY_NAME, MAX_FRAME_BYTES, MAX_ROOM_TITLE, MAX_SPECTATORS, PACKAGE_NAME, SUBPROTOCOL };
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,13 @@
1
+ <!doctype html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>斗地主</title>
7
+ <link rel="stylesheet" href="/doudizhu/assets/join.css" />
8
+ </head>
9
+ <body>
10
+ <div id="root"></div>
11
+ <script src="/doudizhu/join.js"></script>
12
+ </body>
13
+ </html>