@hellacardgames/speed 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 (38) hide show
  1. package/README.md +9 -0
  2. package/dist/GameEvent-DDQ4jXxt.d.cts +111 -0
  3. package/dist/GameEvent-DDQ4jXxt.d.cts.map +1 -0
  4. package/dist/GameEvent-DDQ4jXxt.d.mts +111 -0
  5. package/dist/GameEvent-DDQ4jXxt.d.mts.map +1 -0
  6. package/dist/client/index.cjs +126 -0
  7. package/dist/client/index.cjs.map +1 -0
  8. package/dist/client/index.d.cts +38 -0
  9. package/dist/client/index.d.cts.map +1 -0
  10. package/dist/client/index.d.mts +38 -0
  11. package/dist/client/index.d.mts.map +1 -0
  12. package/dist/client/index.mjs +125 -0
  13. package/dist/client/index.mjs.map +1 -0
  14. package/dist/manager/index.cjs +33 -0
  15. package/dist/manager/index.cjs.map +1 -0
  16. package/dist/manager/index.d.cts +109 -0
  17. package/dist/manager/index.d.cts.map +1 -0
  18. package/dist/manager/index.d.mts +109 -0
  19. package/dist/manager/index.d.mts.map +1 -0
  20. package/dist/manager/index.mjs +22 -0
  21. package/dist/manager/index.mjs.map +1 -0
  22. package/dist/server/index.cjs +204 -0
  23. package/dist/server/index.cjs.map +1 -0
  24. package/dist/server/index.d.cts +40 -0
  25. package/dist/server/index.d.cts.map +1 -0
  26. package/dist/server/index.d.mts +40 -0
  27. package/dist/server/index.d.mts.map +1 -0
  28. package/dist/server/index.mjs +203 -0
  29. package/dist/server/index.mjs.map +1 -0
  30. package/dist/startGame-5InkAgEp.d.cts +108 -0
  31. package/dist/startGame-5InkAgEp.d.cts.map +1 -0
  32. package/dist/startGame-BC9XfFzG.mjs +815 -0
  33. package/dist/startGame-BC9XfFzG.mjs.map +1 -0
  34. package/dist/startGame-Bhk__XWk.cjs +898 -0
  35. package/dist/startGame-Bhk__XWk.cjs.map +1 -0
  36. package/dist/startGame-ayp8a-Cl.d.mts +108 -0
  37. package/dist/startGame-ayp8a-Cl.d.mts.map +1 -0
  38. package/package.json +92 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"startGame-Bhk__XWk.cjs","names":[],"sources":["../src/manager/constants.ts","../src/manager/games.ts","../src/manager/actions/createGame.ts","../src/lib/emitEvent.ts","../src/lib/emitEventToPlayer.ts","../src/manager/actions/drawCard.ts","../src/manager/actions/getClientStateAndClearEvents.ts","../src/manager/actions/getEventsAndClearAcknowledged.ts","../src/manager/actions/getJoinableGames.ts","../src/manager/actions/joinGame.ts","../src/manager/actions/leaveGame.ts","../src/manager/lib/isCardPlayable.ts","../src/manager/lib/hasPlayableCard.ts","../src/manager/actions/playCard.ts","../src/lib/shuffleCards.ts","../src/manager/actions/reportNoPlayableCards.ts","../src/manager/actions/sendChat.ts","../src/manager/actions/startGame.ts"],"sourcesContent":["import type { Card } from \"./types/Card.js\";\n\nexport const GAME_KEY = \"speed\";\nexport const MAX_GAMES = 50;\nexport const WATCHDOG_INTERVAL_MS = 10000;\n\nexport const EXPIRY_EXTENSION_MS = 300000; // 5 minutes\nexport const MIN_PLAYERS = 2;\nexport const MAX_PLAYERS = 2;\nexport const MAX_HAND_SIZE = 5;\nexport const CAN_PLAY_AT_DELAY_MS = 3000;\n\nexport const CARDS: readonly Card[] = [\n { id: crypto.randomUUID(), rank: 2, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 3, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 4, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 5, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 6, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 7, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 8, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 9, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 10, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 11, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 12, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 13, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 14, suite: \"clubs\" },\n { id: crypto.randomUUID(), rank: 2, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 3, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 4, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 5, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 6, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 7, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 8, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 9, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 10, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 11, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 12, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 13, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 14, suite: \"diamonds\" },\n { id: crypto.randomUUID(), rank: 2, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 3, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 4, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 5, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 6, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 7, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 8, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 9, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 10, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 11, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 12, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 13, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 14, suite: \"hearts\" },\n { id: crypto.randomUUID(), rank: 2, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 3, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 4, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 5, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 6, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 7, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 8, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 9, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 10, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 11, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 12, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 13, suite: \"spades\" },\n { id: crypto.randomUUID(), rank: 14, suite: \"spades\" },\n];\n","import type { Game } from \"./types/Game.js\";\n\nexport const games = new Map<string, Game>();\n","import { EXPIRY_EXTENSION_MS, MAX_GAMES } from \"../constants.js\";\nimport { games } from \"../games.js\";\nimport type { Player } from \"../types/Player.js\";\nimport type { Game } from \"../types/Game.js\";\n\ntype CreateGameResult =\n | {\n readonly success: true;\n readonly gameId: string;\n readonly playerId: string;\n }\n | {\n readonly success: false;\n readonly error: \"maxGamesReached\";\n };\n\nexport function createGame(userId: string, username: string): CreateGameResult {\n if (games.size === MAX_GAMES) {\n return { success: false, error: \"maxGamesReached\" };\n }\n const player: Player = {\n id: crypto.randomUUID(),\n userId,\n username,\n events: [],\n hand: [],\n drawPile: [],\n sidePile: [],\n centerPile: [],\n hasNoPlayableCards: false,\n };\n const createdAt = Date.now();\n const game: Game = {\n status: \"open\",\n id: crypto.randomUUID(),\n createdAt,\n expiresAt: createdAt + EXPIRY_EXTENSION_MS,\n chatMessages: [],\n players: [player],\n };\n games.set(game.id, game);\n return { success: true, gameId: game.id, playerId: player.id };\n}\n","type Game = {\n readonly players: {\n readonly events: { readonly id: string }[];\n }[];\n};\n\ntype OmitId<T> = T extends unknown ? Omit<T, \"id\"> : never;\n\nexport function emitEvent<T extends Game>(\n game: T,\n data: OmitId<T[\"players\"][number][\"events\"][number]>,\n): void {\n const event = { ...data, id: crypto.randomUUID() };\n for (const p of game.players) {\n p.events.push(event);\n }\n}\n","type Player = {\n readonly events: { readonly id: string }[];\n};\n\ntype OmitId<T> = T extends unknown ? Omit<T, \"id\"> : never;\n\nexport function emitEventToPlayer<T extends Player>(\n player: T,\n data: OmitId<T[\"events\"][number]>,\n): void {\n player.events.push({ ...data, id: crypto.randomUUID() });\n}\n","import { emitEvent } from \"../../lib/emitEvent.js\";\nimport { emitEventToPlayer } from \"../../lib/emitEventToPlayer.js\";\nimport { EXPIRY_EXTENSION_MS, MAX_HAND_SIZE } from \"../constants.js\";\nimport { games } from \"../games.js\";\n\ntype DrawCardResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error:\n | \"gameNotFound\"\n | \"playerNotFound\"\n | \"invalidStatus\"\n | \"canPlayAtNotReached\"\n | \"handFull\"\n | \"drawPileEmpty\";\n };\n\nexport function drawCard(gameId: string, playerId: string): DrawCardResult {\n const game = games.get(gameId);\n if (!game) {\n return { success: false, error: \"gameNotFound\" };\n }\n const player = game.players.find((p) => p.id === playerId);\n if (!player) {\n return { success: false, error: \"playerNotFound\" };\n }\n if (game.status !== \"started\") {\n return { success: false, error: \"invalidStatus\" };\n }\n if (Date.now() < game.canPlayAt) {\n return { success: false, error: \"canPlayAtNotReached\" };\n }\n if (player.hand.length >= MAX_HAND_SIZE) {\n return { success: false, error: \"handFull\" };\n }\n if (player.drawPile.length === 0) {\n return { success: false, error: \"drawPileEmpty\" };\n }\n game.expiresAt = Date.now() + EXPIRY_EXTENSION_MS;\n emitEvent(game, { type: \"expirationUpdated\", expiresAt: game.expiresAt });\n const card = player.drawPile.pop()!;\n player.hand.push(card);\n emitEventToPlayer(player, { type: \"drewCard\", card });\n emitEvent(game, { type: \"playerDrewCard\", username: player.username });\n return { success: true };\n}\n","import { games } from \"../games.js\";\nimport type { ClientState } from \"../types/ClientState.js\";\n\ntype GetClientStateAndClearEventsResult =\n | {\n readonly success: true;\n readonly state: ClientState;\n }\n | {\n readonly success: false;\n readonly error: \"gameNotFound\" | \"playerNotFound\";\n };\n\nexport function getClientStateAndClearEvents(\n gameId: string,\n playerId: string,\n): GetClientStateAndClearEventsResult {\n const game = games.get(gameId);\n if (!game) {\n return { success: false, error: \"gameNotFound\" };\n }\n const player = game.players.find((p) => p.id === playerId);\n if (!player) {\n return { success: false, error: \"playerNotFound\" };\n }\n const state: ClientState = {\n status: game.status,\n gameId,\n playerId,\n username: player.username,\n players: game.players.map((p) => ({\n username: p.username,\n handSize: p.hand.length,\n drawPileSize: p.drawPile.length,\n sidePileSize: p.sidePile.length,\n centerPileTopCard: p.centerPile.length\n ? p.centerPile[p.centerPile.length - 1]!\n : null,\n })),\n expiresAt: game.expiresAt,\n chatMessages: game.chatMessages,\n hand: player.hand,\n canPlayAt: game.status === \"started\" ? game.canPlayAt : null,\n };\n player.events.length = 0;\n return { success: true, state };\n}\n","import { games } from \"../games.js\";\nimport type { GameEvent } from \"../types/GameEvent.js\";\n\ntype GetEventsAndClearAcknowledgedResult =\n | {\n readonly success: true;\n readonly events: readonly GameEvent[];\n }\n | {\n readonly success: false;\n readonly error: \"gameNotFound\" | \"playerNotFound\";\n };\n\nexport function getEventsAndClearAcknowledged(\n gameId: string,\n playerId: string,\n lastReadId: string | null,\n): GetEventsAndClearAcknowledgedResult {\n const game = games.get(gameId);\n if (!game) {\n return { success: false, error: \"gameNotFound\" };\n }\n const player = game.players.find((p) => p.id === playerId);\n if (!player) {\n return { success: false, error: \"playerNotFound\" };\n }\n const lastReadEventIndex = player.events.findIndex(\n (e) => e.id === lastReadId,\n );\n player.events.splice(0, lastReadEventIndex + 1);\n return { success: true, events: player.events };\n}\n","import { MAX_PLAYERS } from \"../constants.js\";\nimport { games } from \"../games.js\";\n\ntype GetJoinableGamesResult = {\n readonly success: true;\n readonly games: readonly {\n readonly id: string;\n readonly numPlayers: number;\n }[];\n};\n\nexport function getJoinableGames(): GetJoinableGamesResult {\n return {\n success: true,\n games: Array.from(games.values())\n .filter((g) => g.status === \"open\" && g.players.length < MAX_PLAYERS)\n .map((g) => ({\n id: g.id,\n numPlayers: g.players.length,\n })),\n };\n}\n","import { emitEvent } from \"../../lib/emitEvent.js\";\nimport { MAX_PLAYERS } from \"../constants.js\";\nimport { games } from \"../games.js\";\nimport type { Player } from \"../types/Player.js\";\n\ntype JoinGameResult =\n | {\n readonly success: true;\n readonly playerId: string;\n }\n | {\n readonly success: false;\n readonly error:\n | \"gameNotFound\"\n | \"invalidStatus\"\n | \"maxPlayersReached\"\n | \"alreadyInGame\";\n };\n\nexport function joinGame(\n gameId: string,\n userId: string,\n username: string,\n): JoinGameResult {\n const game = games.get(gameId);\n if (!game) {\n return { success: false, error: \"gameNotFound\" };\n }\n if (game.status !== \"open\") {\n return { success: false, error: \"invalidStatus\" };\n }\n if (game.players.length === MAX_PLAYERS) {\n return { success: false, error: \"maxPlayersReached\" };\n }\n if (game.players.find((p) => p.userId === userId)) {\n return { success: false, error: \"alreadyInGame\" };\n }\n const player: Player = {\n id: crypto.randomUUID(),\n userId,\n username,\n events: [],\n hand: [],\n drawPile: [],\n sidePile: [],\n centerPile: [],\n hasNoPlayableCards: false,\n };\n game.players.push(player);\n emitEvent(game, { type: \"playerJoined\", username });\n return { success: true, playerId: player.id };\n}\n","import { emitEvent } from \"../../lib/emitEvent.js\";\nimport { EXPIRY_EXTENSION_MS, MIN_PLAYERS } from \"../constants.js\";\nimport { games } from \"../games.js\";\nimport type { Game } from \"../types/Game.js\";\n\ntype LeaveGameResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error: \"gameNotFound\" | \"playerNotFound\";\n };\n\nexport function leaveGame(gameId: string, playerId: string): LeaveGameResult {\n const game = games.get(gameId);\n if (!game) {\n return { success: false, error: \"gameNotFound\" };\n }\n const playerIndex = game.players.findIndex((p) => p.id === playerId);\n if (playerIndex === -1) {\n return { success: false, error: \"playerNotFound\" };\n }\n\n const player = game.players[playerIndex]!;\n emitEvent(game, { type: \"playerLeft\", username: player.username });\n\n game.players.splice(playerIndex, 1);\n\n if (game.status === \"started\" && game.players.length < MIN_PLAYERS) {\n const forfeitedGame: Game = {\n ...game,\n status: \"forfeited\",\n expiresAt: Date.now() + EXPIRY_EXTENSION_MS,\n };\n games.set(game.id, forfeitedGame);\n emitEvent(forfeitedGame, { type: \"gameForfeited\" });\n emitEvent(forfeitedGame, {\n type: \"expirationUpdated\",\n expiresAt: forfeitedGame.expiresAt,\n });\n }\n if (game.players.length === 0) {\n games.delete(game.id);\n }\n return { success: true };\n}\n","import type { Card } from \"../types/Card.js\";\n\nexport function isCardPlayable(card: Card, targetPile: Card[]) {\n const targetCard = targetPile[targetPile.length - 1]!;\n const diff = Math.abs(card.rank - targetCard.rank) % 11;\n return diff === 1;\n}\n","import { isCardPlayable } from \"./isCardPlayable.js\";\nimport type { Game } from \"../types/Game.js\";\nimport type { Player } from \"../types/Player.js\";\n\nexport function hasPlayableCard(player: Player, game: Game) {\n const otherPlayer = game.players.find((p) => p !== player)!;\n const targetPile1 = player.centerPile;\n const targetPile2 = otherPlayer.centerPile;\n for (const card of player.hand) {\n if (\n isCardPlayable(card, targetPile1) ||\n isCardPlayable(card, targetPile2)\n ) {\n return true;\n }\n }\n return false;\n}\n","import { emitEvent } from \"../../lib/emitEvent.js\";\nimport { EXPIRY_EXTENSION_MS } from \"../constants.js\";\nimport { games } from \"../games.js\";\nimport { hasPlayableCard } from \"../lib/hasPlayableCard.js\";\nimport { isCardPlayable } from \"../lib/isCardPlayable.js\";\nimport type { Game } from \"../types/Game.js\";\n\ntype PlayCardResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error:\n | \"gameNotFound\"\n | \"playerNotFound\"\n | \"invalidStatus\"\n | \"canPlayAtNotReached\"\n | \"cardNotFound\"\n | \"cardNotPlayable\";\n };\n\nexport function playCard(\n gameId: string,\n playerId: string,\n cardId: string,\n isForOtherPlayerPile: boolean,\n): PlayCardResult {\n const game = games.get(gameId);\n if (!game) {\n return { success: false, error: \"gameNotFound\" };\n }\n const player = game.players.find((p) => p.id === playerId);\n if (!player) {\n return { success: false, error: \"playerNotFound\" };\n }\n if (game.status !== \"started\") {\n return { success: false, error: \"invalidStatus\" };\n }\n if (Date.now() < game.canPlayAt) {\n return { success: false, error: \"canPlayAtNotReached\" };\n }\n const cardIndex = player.hand.findIndex((c) => c.id === cardId);\n if (cardIndex === -1) {\n return { success: false, error: \"cardNotFound\" };\n }\n const otherPlayer = game.players.find((p) => p !== player)!;\n const targetPile = isForOtherPlayerPile\n ? otherPlayer.centerPile\n : player.centerPile;\n const card = player.hand[cardIndex]!;\n if (!isCardPlayable(card, targetPile)) {\n return { success: false, error: \"cardNotPlayable\" };\n }\n\n game.expiresAt = Date.now() + EXPIRY_EXTENSION_MS;\n emitEvent(game, { type: \"expirationUpdated\", expiresAt: game.expiresAt });\n\n player.hand.splice(cardIndex, 1);\n targetPile.push(card);\n emitEvent(game, {\n type: \"cardPlayed\",\n username: player.username,\n card,\n isForOtherPlayerPile,\n });\n\n if (otherPlayer.hasNoPlayableCards && hasPlayableCard(otherPlayer, game)) {\n otherPlayer.hasNoPlayableCards = false;\n }\n\n if (player.hand.length === 0 && player.drawPile.length === 0) {\n emitEvent(game, { type: \"gameCompleted\" });\n const completedGame: Game = {\n ...game,\n status: \"completed\",\n };\n games.set(game.id, completedGame);\n }\n\n return { success: true };\n}\n","type Card = { readonly id: string };\n\nexport function shuffleCards<T extends Card>(cards: T[]): void {\n for (let i = cards.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n [cards[i], cards[j]] = [cards[j]!, cards[i]!];\n }\n}\n","import { emitEvent } from \"../../lib/emitEvent.js\";\nimport { shuffleCards } from \"../../lib/shuffleCards.js\";\nimport {\n CAN_PLAY_AT_DELAY_MS,\n EXPIRY_EXTENSION_MS,\n MAX_HAND_SIZE,\n} from \"../constants.js\";\nimport { games } from \"../games.js\";\nimport { hasPlayableCard } from \"../lib/hasPlayableCard.js\";\n\ntype ReportNoPlayableCardsResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error:\n | \"gameNotFound\"\n | \"playerNotFound\"\n | \"invalidStatus\"\n | \"canPlayAtNotReached\"\n | \"alreadyReported\"\n | \"hasPlayableCard\"\n | \"canDraw\";\n };\n\nexport function reportNoPlayableCards(\n gameId: string,\n playerId: string,\n): ReportNoPlayableCardsResult {\n const game = games.get(gameId);\n if (!game) {\n return { success: false, error: \"gameNotFound\" };\n }\n const player = game.players.find((p) => p.id === playerId);\n if (!player) {\n return { success: false, error: \"playerNotFound\" };\n }\n if (game.status !== \"started\") {\n return { success: false, error: \"invalidStatus\" };\n }\n if (Date.now() < game.canPlayAt) {\n return { success: false, error: \"canPlayAtNotReached\" };\n }\n if (player.hasNoPlayableCards) {\n return { success: false, error: \"alreadyReported\" };\n }\n if (hasPlayableCard(player, game)) {\n return { success: false, error: \"hasPlayableCard\" };\n }\n if (player.drawPile.length > 0 && player.hand.length < MAX_HAND_SIZE) {\n return { success: false, error: \"canDraw\" };\n }\n\n game.expiresAt = Date.now() + EXPIRY_EXTENSION_MS;\n emitEvent(game, { type: \"expirationUpdated\", expiresAt: game.expiresAt });\n\n player.hasNoPlayableCards = true;\n\n const otherPlayer = game.players.find((p) => p !== player)!;\n if (otherPlayer.hasNoPlayableCards) {\n if (player.sidePile.length === 0 && otherPlayer.sidePile.length === 0) {\n const cards = [...player.centerPile, ...otherPlayer.centerPile];\n shuffleCards(cards);\n player.sidePile.push(...cards.splice(0, 5));\n emitEvent(game, {\n type: \"playerSidePileInitialized\",\n username: player.username,\n numCards: player.sidePile.length,\n });\n player.centerPile.push(...cards.splice(0, 1));\n emitEvent(game, {\n type: \"playerCenterPileInitialized\",\n username: player.username,\n card: player.centerPile[player.centerPile.length - 1]!,\n });\n otherPlayer.centerPile.push(...cards.splice(0, 1));\n emitEvent(game, {\n type: \"playerCenterPileInitialized\",\n username: otherPlayer.username,\n card: otherPlayer.centerPile[otherPlayer.centerPile.length - 1]!,\n });\n otherPlayer.sidePile.push(...cards.splice(0, 5));\n emitEvent(game, {\n type: \"playerSidePileInitialized\",\n username: otherPlayer.username,\n numCards: otherPlayer.sidePile.length,\n });\n } else {\n const playerSidePileCard = player.sidePile.pop()!;\n player.centerPile.push(playerSidePileCard);\n emitEvent(game, {\n type: \"cardDrawnFromSidePileToCenterPile\",\n username: player.username,\n card: playerSidePileCard,\n });\n const otherPlayerSidePileCard = otherPlayer.sidePile.pop()!;\n otherPlayer.centerPile.push(otherPlayerSidePileCard);\n emitEvent(game, {\n type: \"cardDrawnFromSidePileToCenterPile\",\n username: otherPlayer.username,\n card: otherPlayerSidePileCard,\n });\n }\n player.hasNoPlayableCards = false;\n otherPlayer.hasNoPlayableCards = false;\n game.canPlayAt = Date.now() + CAN_PLAY_AT_DELAY_MS;\n emitEvent(game, {\n type: \"canPlayAtUpdated\",\n canPlayAt: game.canPlayAt,\n });\n }\n\n return { success: true };\n}\n","import { emitEvent } from \"../../lib/emitEvent.js\";\nimport { games } from \"../games.js\";\nimport type { ChatMessage } from \"../types/ChatMessage.js\";\n\ntype SendChatResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error: \"gameNotFound\" | \"playerNotFound\";\n };\n\nexport function sendChat(\n gameId: string,\n playerId: string,\n text: string,\n): SendChatResult {\n const game = games.get(gameId);\n if (!game) {\n return { success: false, error: \"gameNotFound\" };\n }\n const player = game.players.find((p) => p.id === playerId);\n if (!player) {\n return { success: false, error: \"playerNotFound\" };\n }\n const message: ChatMessage = {\n id: crypto.randomUUID(),\n username: player.username,\n text,\n };\n game.chatMessages.push(message);\n emitEvent(game, { type: \"chat\", message });\n return { success: true };\n}\n","import { emitEvent } from \"../../lib/emitEvent.js\";\nimport { emitEventToPlayer } from \"../../lib/emitEventToPlayer.js\";\nimport { shuffleCards } from \"../../lib/shuffleCards.js\";\nimport {\n CAN_PLAY_AT_DELAY_MS,\n CARDS,\n EXPIRY_EXTENSION_MS,\n MIN_PLAYERS,\n} from \"../constants.js\";\nimport { games } from \"../games.js\";\nimport type { Game } from \"../types/Game.js\";\n\ntype StartGameResult =\n | {\n readonly success: true;\n }\n | {\n readonly success: false;\n readonly error:\n | \"gameNotFound\"\n | \"playerNotFound\"\n | \"invalidStatus\"\n | \"playerNotAdmin\"\n | \"minPlayersNotReached\";\n };\n\nexport function startGame(gameId: string, playerId: string): StartGameResult {\n const game = games.get(gameId);\n if (!game) {\n return { success: false, error: \"gameNotFound\" };\n }\n const player = game.players.find((p) => p.id === playerId);\n if (!player) {\n return { success: false, error: \"playerNotFound\" };\n }\n if (game.status !== \"open\") {\n return { success: false, error: \"invalidStatus\" };\n }\n if (game.players.indexOf(player) !== 0) {\n return { success: false, error: \"playerNotAdmin\" };\n }\n if (game.players.length < MIN_PLAYERS) {\n return { success: false, error: \"minPlayersNotReached\" };\n }\n const otherPlayer = game.players[1]!;\n const cards = [...CARDS];\n shuffleCards(cards);\n\n player.sidePile.push(...cards.splice(0, 5));\n emitEvent(game, {\n type: \"playerSidePileInitialized\",\n username: player.username,\n numCards: player.sidePile.length,\n });\n\n player.centerPile.push(...cards.splice(0, 1));\n emitEvent(game, {\n type: \"playerCenterPileInitialized\",\n username: player.username,\n card: player.centerPile[player.centerPile.length - 1]!,\n });\n\n otherPlayer.centerPile.push(...cards.splice(0, 1));\n emitEvent(game, {\n type: \"playerCenterPileInitialized\",\n username: otherPlayer.username,\n card: otherPlayer.centerPile[otherPlayer.centerPile.length - 1]!,\n });\n\n otherPlayer.sidePile.push(...cards.splice(0, 5));\n emitEvent(game, {\n type: \"playerSidePileInitialized\",\n username: otherPlayer.username,\n numCards: otherPlayer.sidePile.length,\n });\n\n player.drawPile.push(...cards.splice(0, 15));\n emitEvent(game, {\n type: \"playerDrawPileInitialized\",\n username: player.username,\n numCards: player.drawPile.length,\n });\n\n player.hand.push(...cards.splice(0, 5));\n emitEventToPlayer(player, { type: \"handInitialized\", cards: player.hand });\n emitEvent(game, {\n type: \"playerHandInitialized\",\n username: player.username,\n numCards: player.hand.length,\n });\n\n otherPlayer.drawPile.push(...cards.splice(0, 15));\n emitEvent(game, {\n type: \"playerDrawPileInitialized\",\n username: otherPlayer.username,\n numCards: otherPlayer.drawPile.length,\n });\n\n otherPlayer.hand.push(...cards.splice(0, 5));\n emitEventToPlayer(otherPlayer, {\n type: \"handInitialized\",\n cards: otherPlayer.hand,\n });\n emitEvent(game, {\n type: \"playerHandInitialized\",\n username: otherPlayer.username,\n numCards: otherPlayer.hand.length,\n });\n\n const startedGame: Game = {\n ...game,\n status: \"started\",\n expiresAt: Date.now() + EXPIRY_EXTENSION_MS,\n canPlayAt: Date.now() + CAN_PLAY_AT_DELAY_MS,\n };\n games.set(game.id, startedGame);\n emitEvent(startedGame, { type: \"gameStarted\" });\n emitEvent(startedGame, {\n type: \"expirationUpdated\",\n expiresAt: startedGame.expiresAt,\n });\n emitEvent(startedGame, {\n type: \"canPlayAtUpdated\",\n canPlayAt: startedGame.canPlayAt,\n });\n return { success: true };\n}\n"],"mappings":"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"}
@@ -0,0 +1,108 @@
1
+ import { n as ClientState, t as GameEvent } from "./GameEvent-DDQ4jXxt.mjs";
2
+
3
+ //#region src/server/actions/createGame.d.ts
4
+ type CreateGameResult = {
5
+ readonly success: true;
6
+ readonly gameId: string;
7
+ readonly playerId: string;
8
+ } | {
9
+ readonly success: false;
10
+ readonly error: "maxGamesReached";
11
+ };
12
+ declare function createGame(userId: string, username: string): CreateGameResult;
13
+ //#endregion
14
+ //#region src/server/actions/drawCard.d.ts
15
+ type DrawCardResult = {
16
+ readonly success: true;
17
+ } | {
18
+ readonly success: false;
19
+ readonly error: "invalidInput" | "gameNotFound" | "playerNotFound" | "invalidStatus" | "canPlayAtNotReached" | "handFull" | "drawPileEmpty";
20
+ };
21
+ declare function drawCard(input: unknown): DrawCardResult;
22
+ //#endregion
23
+ //#region src/server/actions/getClientStateAndClearEvents.d.ts
24
+ type GetClientStateAndClearEventsResult = {
25
+ readonly success: true;
26
+ readonly state: ClientState;
27
+ } | {
28
+ readonly success: false;
29
+ readonly error: "invalidInput" | "gameNotFound" | "playerNotFound";
30
+ };
31
+ declare function getClientStateAndClearEvents(input: unknown): GetClientStateAndClearEventsResult;
32
+ //#endregion
33
+ //#region src/server/actions/getEventsAndClearAcknowledged.d.ts
34
+ type GetEventsAndClearAcknowledgedResult = {
35
+ readonly success: true;
36
+ readonly events: readonly GameEvent[];
37
+ } | {
38
+ readonly success: false;
39
+ readonly error: "invalidInput" | "gameNotFound" | "playerNotFound";
40
+ };
41
+ declare function getEventsAndClearAcknowledged(input: unknown): GetEventsAndClearAcknowledgedResult;
42
+ //#endregion
43
+ //#region src/server/actions/getJoinableGames.d.ts
44
+ type GetJoinableGamesResult = {
45
+ readonly games: readonly {
46
+ readonly id: string;
47
+ readonly numPlayers: number;
48
+ }[];
49
+ };
50
+ declare function getJoinableGames(): GetJoinableGamesResult;
51
+ //#endregion
52
+ //#region src/server/actions/joinGame.d.ts
53
+ type JoinGameResult = {
54
+ readonly success: true;
55
+ readonly playerId: string;
56
+ } | {
57
+ readonly success: false;
58
+ readonly error: "invalidInput" | "gameNotFound" | "invalidStatus" | "maxPlayersReached" | "alreadyInGame";
59
+ };
60
+ declare function joinGame(input: unknown, userId: string, username: string): JoinGameResult;
61
+ //#endregion
62
+ //#region src/server/actions/leaveGame.d.ts
63
+ type LeaveGameResult = {
64
+ readonly success: true;
65
+ } | {
66
+ readonly success: false;
67
+ readonly error: "invalidInput" | "gameNotFound" | "playerNotFound";
68
+ };
69
+ declare function leaveGame(input: unknown): LeaveGameResult;
70
+ //#endregion
71
+ //#region src/server/actions/playCard.d.ts
72
+ type PlayCardResult = {
73
+ readonly success: true;
74
+ } | {
75
+ readonly success: false;
76
+ readonly error: "invalidInput" | "gameNotFound" | "playerNotFound" | "invalidStatus" | "canPlayAtNotReached" | "cardNotFound" | "cardNotPlayable";
77
+ };
78
+ declare function playCard(input: unknown): PlayCardResult;
79
+ //#endregion
80
+ //#region src/server/actions/reportNoPlayableCards.d.ts
81
+ type ReportNoPlayableCardsResult = {
82
+ readonly success: true;
83
+ } | {
84
+ readonly success: false;
85
+ readonly error: "invalidInput" | "gameNotFound" | "playerNotFound" | "invalidStatus" | "canPlayAtNotReached" | "alreadyReported" | "hasPlayableCard" | "canDraw";
86
+ };
87
+ declare function reportNoPlayableCards(input: unknown): ReportNoPlayableCardsResult;
88
+ //#endregion
89
+ //#region src/server/actions/sendChat.d.ts
90
+ type SendChatResult = {
91
+ readonly success: true;
92
+ } | {
93
+ readonly success: false;
94
+ readonly error: "invalidInput" | "gameNotFound" | "playerNotFound";
95
+ };
96
+ declare function sendChat(input: unknown): SendChatResult;
97
+ //#endregion
98
+ //#region src/server/actions/startGame.d.ts
99
+ type StartGameResult = {
100
+ readonly success: true;
101
+ } | {
102
+ readonly success: false;
103
+ readonly error: "invalidInput" | "gameNotFound" | "invalidStatus" | "playerNotFound" | "playerNotAdmin" | "minPlayersNotReached";
104
+ };
105
+ declare function startGame(input: unknown): StartGameResult;
106
+ //#endregion
107
+ export { createGame as S, GetClientStateAndClearEventsResult as _, ReportNoPlayableCardsResult as a, drawCard as b, playCard as c, JoinGameResult as d, joinGame as f, getEventsAndClearAcknowledged as g, GetEventsAndClearAcknowledgedResult as h, sendChat as i, LeaveGameResult as l, getJoinableGames as m, startGame as n, reportNoPlayableCards as o, GetJoinableGamesResult as p, SendChatResult as r, PlayCardResult as s, StartGameResult as t, leaveGame as u, getClientStateAndClearEvents as v, CreateGameResult as x, DrawCardResult as y };
108
+ //# sourceMappingURL=startGame-ayp8a-Cl.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"startGame-ayp8a-Cl.d.mts","names":[],"sources":["../src/server/actions/createGame.ts","../src/server/actions/drawCard.ts","../src/server/actions/getClientStateAndClearEvents.ts","../src/server/actions/getEventsAndClearAcknowledged.ts","../src/server/actions/getJoinableGames.ts","../src/server/actions/joinGame.ts","../src/server/actions/leaveGame.ts","../src/server/actions/playCard.ts","../src/server/actions/reportNoPlayableCards.ts","../src/server/actions/sendChat.ts","../src/server/actions/startGame.ts"],"mappings":";;;KAEY,gBAAA;EAAA,SAEG,OAAA;EAAA,SACA,MAAA;EAAA,SACA,QAAA;AAAA;EAAA,SAGA,OAAA;EAAA,SACA,KAAA;AAAA;AAAA,iBAGC,UAAA,CAAW,MAAA,UAAgB,QAAA,WAAmB,gBAAgB;;;KCVlE,cAAA;EAAA,SAEG,OAAA;AAAA;EAAA,SAGA,OAAA;EAAA,SACA,KAAA;AAAA;AAAA,iBAiBC,QAAA,CAAS,KAAA,YAAiB,cAAc;;;KCtB5C,kCAAA;EAAA,SAEG,OAAA;EAAA,SACA,KAAA,EAAO,WAAW;AAAA;EAAA,SAGlB,OAAA;EAAA,SACA,KAAA;AAAA;AAAA,iBAUC,4BAAA,CACd,KAAA,YACC,kCAAkC;;;KCnBzB,mCAAA;EAAA,SAEG,OAAA;EAAA,SACA,MAAA,WAAiB,SAAS;AAAA;EAAA,SAG1B,OAAA;EAAA,SACA,KAAA;AAAA;AAAA,iBAcC,6BAAA,CACd,KAAA,YACC,mCAAmC;;;KCzB1B,sBAAA;EAAA,SACD,KAAA;IAAA,SACE,EAAA;IAAA,SACA,UAAA;EAAA;AAAA;AAAA,iBAIG,gBAAA,IAAoB,sBAAsB;;;KCN9C,cAAA;EAAA,SAEG,OAAA;EAAA,SACA,QAAA;AAAA;EAAA,SAGA,OAAA;EAAA,SACA,KAAA;AAAA;AAAA,iBAcC,QAAA,CACd,KAAA,WACA,MAAA,UACA,QAAA,WACC,cAAc;;;KCzBL,eAAA;EAAA,SAEG,OAAA;AAAA;EAAA,SAGA,OAAA;EAAA,SACA,KAAA;AAAA;AAAA,iBAUC,SAAA,CAAU,KAAA,YAAiB,eAAe;;;KChB9C,cAAA;EAAA,SAEG,OAAA;AAAA;EAAA,SAGA,OAAA;EAAA,SACA,KAAA;AAAA;AAAA,iBAsBC,QAAA,CAAS,KAAA,YAAiB,cAAc;;;KC5B5C,2BAAA;EAAA,SAEG,OAAA;AAAA;EAAA,SAGA,OAAA;EAAA,SACA,KAAA;AAAA;AAAA,iBAkBC,qBAAA,CACd,KAAA,YACC,2BAA2B;;;KC1BlB,cAAA;EAAA,SAEG,OAAA;AAAA;EAAA,SAGA,OAAA;EAAA,SACA,KAAA;AAAA;AAAA,iBAWC,QAAA,CAAS,KAAA,YAAiB,cAAc;;;KCjB5C,eAAA;EAAA,SAEG,OAAA;AAAA;EAAA,SAGA,OAAA;EAAA,SACA,KAAA;AAAA;AAAA,iBAgBC,SAAA,CAAU,KAAA,YAAiB,eAAe"}
package/package.json ADDED
@@ -0,0 +1,92 @@
1
+ {
2
+ "name": "@hellacardgames/speed",
3
+ "version": "0.1.0",
4
+ "description": "A TypeScript library implementing the Speed card game.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": {
8
+ "name": "Richard Gieg"
9
+ },
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/hellacardgames/speed.git"
13
+ },
14
+ "bugs": {
15
+ "url": "https://github.com/hellacardgames/speed/issues"
16
+ },
17
+ "homepage": "https://github.com/hellacardgames/speed#readme",
18
+ "main": "./dist/game/index.cjs",
19
+ "types": "./dist/game/index.d.cts",
20
+ "exports": {
21
+ ".": {
22
+ "import": {
23
+ "types": "./dist/game/index.d.mts",
24
+ "default": "./dist/game/index.mjs"
25
+ },
26
+ "require": {
27
+ "types": "./dist/game/index.d.cts",
28
+ "default": "./dist/game/index.cjs"
29
+ }
30
+ },
31
+ "./manager": {
32
+ "import": {
33
+ "types": "./dist/manager/index.d.mts",
34
+ "default": "./dist/manager/index.mjs"
35
+ },
36
+ "require": {
37
+ "types": "./dist/manager/index.d.cts",
38
+ "default": "./dist/manager/index.cjs"
39
+ }
40
+ },
41
+ "./server": {
42
+ "import": {
43
+ "types": "./dist/server/index.d.mts",
44
+ "default": "./dist/server/index.mjs"
45
+ },
46
+ "require": {
47
+ "types": "./dist/server/index.d.cts",
48
+ "default": "./dist/server/index.cjs"
49
+ }
50
+ },
51
+ "./client": {
52
+ "import": {
53
+ "types": "./dist/client/index.d.mts",
54
+ "default": "./dist/client/index.mjs"
55
+ },
56
+ "require": {
57
+ "types": "./dist/client/index.d.cts",
58
+ "default": "./dist/client/index.cjs"
59
+ }
60
+ }
61
+ },
62
+ "files": [
63
+ "dist"
64
+ ],
65
+ "scripts": {
66
+ "build": "tsdown",
67
+ "typecheck": "tsc --noEmit",
68
+ "format": "prettier --write .",
69
+ "format:check": "prettier --check .",
70
+ "lint": "eslint .",
71
+ "lint:fix": "eslint . --fix",
72
+ "test": "vitest run",
73
+ "test:watch": "vitest"
74
+ },
75
+ "devDependencies": {
76
+ "@eslint/js": "^10.0.1",
77
+ "@types/node": "^26.1.0",
78
+ "eslint": "^10.6.0",
79
+ "eslint-config-prettier": "^10.1.8",
80
+ "globals": "^17.7.0",
81
+ "jiti": "^2.7.0",
82
+ "prettier": "^3.9.4",
83
+ "tsdown": "^0.22.3",
84
+ "typescript": "^6.0.3",
85
+ "typescript-eslint": "^8.62.1",
86
+ "vitest": "^4.1.9"
87
+ },
88
+ "dependencies": {
89
+ "@hellacardgames/lib": "^0.5.1",
90
+ "zod": "^4.4.3"
91
+ }
92
+ }