@pokertools/engine 1.0.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 (76) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +607 -0
  3. package/dist/actions/betting.d.ts +21 -0
  4. package/dist/actions/betting.js +410 -0
  5. package/dist/actions/dealing.d.ts +9 -0
  6. package/dist/actions/dealing.js +206 -0
  7. package/dist/actions/management.d.ts +9 -0
  8. package/dist/actions/management.js +58 -0
  9. package/dist/actions/showdownActions.d.ts +9 -0
  10. package/dist/actions/showdownActions.js +119 -0
  11. package/dist/actions/special.d.ts +14 -0
  12. package/dist/actions/special.js +98 -0
  13. package/dist/actions/streetProgression.d.ts +13 -0
  14. package/dist/actions/streetProgression.js +157 -0
  15. package/dist/actions/tournament.d.ts +5 -0
  16. package/dist/actions/tournament.js +38 -0
  17. package/dist/actions/validation.d.ts +6 -0
  18. package/dist/actions/validation.js +182 -0
  19. package/dist/engine/PokerEngine.d.ts +92 -0
  20. package/dist/engine/PokerEngine.js +246 -0
  21. package/dist/engine/gameReducer.d.ts +10 -0
  22. package/dist/engine/gameReducer.js +135 -0
  23. package/dist/errors/ConfigError.d.ts +8 -0
  24. package/dist/errors/ConfigError.js +15 -0
  25. package/dist/errors/CriticalStateError.d.ts +8 -0
  26. package/dist/errors/CriticalStateError.js +15 -0
  27. package/dist/errors/ErrorCodes.d.ts +38 -0
  28. package/dist/errors/ErrorCodes.js +46 -0
  29. package/dist/errors/IllegalActionError.d.ts +9 -0
  30. package/dist/errors/IllegalActionError.js +15 -0
  31. package/dist/errors/PokerEngineError.d.ts +8 -0
  32. package/dist/errors/PokerEngineError.js +19 -0
  33. package/dist/errors/index.d.ts +5 -0
  34. package/dist/errors/index.js +22 -0
  35. package/dist/history/exporter.d.ts +28 -0
  36. package/dist/history/exporter.js +60 -0
  37. package/dist/history/formats/json.d.ts +14 -0
  38. package/dist/history/formats/json.js +46 -0
  39. package/dist/history/formats/pokerstars.d.ts +10 -0
  40. package/dist/history/formats/pokerstars.js +188 -0
  41. package/dist/history/handHistoryBuilder.d.ts +10 -0
  42. package/dist/history/handHistoryBuilder.js +179 -0
  43. package/dist/history/types.d.ts +73 -0
  44. package/dist/history/types.js +5 -0
  45. package/dist/index.d.ts +8 -0
  46. package/dist/index.js +38 -0
  47. package/dist/rules/actionOrder.d.ts +14 -0
  48. package/dist/rules/actionOrder.js +211 -0
  49. package/dist/rules/blinds.d.ts +24 -0
  50. package/dist/rules/blinds.js +64 -0
  51. package/dist/rules/headsUp.d.ts +15 -0
  52. package/dist/rules/headsUp.js +44 -0
  53. package/dist/rules/showdown.d.ts +9 -0
  54. package/dist/rules/showdown.js +164 -0
  55. package/dist/rules/sidePots.d.ts +32 -0
  56. package/dist/rules/sidePots.js +173 -0
  57. package/dist/tsconfig.tsbuildinfo +1 -0
  58. package/dist/utils/cardUtils.d.ts +12 -0
  59. package/dist/utils/cardUtils.js +30 -0
  60. package/dist/utils/constants.d.ts +38 -0
  61. package/dist/utils/constants.js +41 -0
  62. package/dist/utils/deck.d.ts +46 -0
  63. package/dist/utils/deck.js +126 -0
  64. package/dist/utils/invariants.d.ts +39 -0
  65. package/dist/utils/invariants.js +163 -0
  66. package/dist/utils/positioning.d.ts +36 -0
  67. package/dist/utils/positioning.js +97 -0
  68. package/dist/utils/rake.d.ts +13 -0
  69. package/dist/utils/rake.js +45 -0
  70. package/dist/utils/serialization.d.ts +53 -0
  71. package/dist/utils/serialization.js +106 -0
  72. package/dist/utils/validation.d.ts +20 -0
  73. package/dist/utils/validation.js +52 -0
  74. package/dist/utils/viewMasking.d.ts +20 -0
  75. package/dist/utils/viewMasking.js +90 -0
  76. package/package.json +58 -0
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ // Export all errors
18
+ __exportStar(require("./PokerEngineError"), exports);
19
+ __exportStar(require("./CriticalStateError"), exports);
20
+ __exportStar(require("./IllegalActionError"), exports);
21
+ __exportStar(require("./ConfigError"), exports);
22
+ __exportStar(require("./ErrorCodes"), exports);
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Main hand history exporter
3
+ */
4
+ import { GameState } from "@pokertools/types";
5
+ import { HandHistory, ExportOptions } from "./types";
6
+ /**
7
+ * Export hand history from game state
8
+ *
9
+ * @param state Final game state (after hand is complete)
10
+ * @param options Export options
11
+ * @returns Formatted hand history string
12
+ */
13
+ export declare function exportHandHistory(state: GameState, options?: ExportOptions): string;
14
+ /**
15
+ * Build hand history object without formatting
16
+ *
17
+ * @param state Final game state
18
+ * @returns Structured hand history object
19
+ */
20
+ export declare function getHandHistory(state: GameState): HandHistory;
21
+ /**
22
+ * Export multiple hands to a single file
23
+ *
24
+ * @param states Array of final game states
25
+ * @param options Export options
26
+ * @returns Formatted multi-hand history
27
+ */
28
+ export declare function exportMultipleHands(states: GameState[], options?: ExportOptions): string;
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ /**
3
+ * Main hand history exporter
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.exportHandHistory = exportHandHistory;
7
+ exports.getHandHistory = getHandHistory;
8
+ exports.exportMultipleHands = exportMultipleHands;
9
+ const handHistoryBuilder_1 = require("./handHistoryBuilder");
10
+ const pokerstars_1 = require("./formats/pokerstars");
11
+ const json_1 = require("./formats/json");
12
+ /**
13
+ * Export hand history from game state
14
+ *
15
+ * @param state Final game state (after hand is complete)
16
+ * @param options Export options
17
+ * @returns Formatted hand history string
18
+ */
19
+ function exportHandHistory(state, options = { format: "json" }) {
20
+ // Build structured history
21
+ const history = (0, handHistoryBuilder_1.buildHandHistory)(state);
22
+ // Export to requested format
23
+ switch (options.format) {
24
+ case "pokerstars":
25
+ return (0, pokerstars_1.exportToPokerStars)(history, options);
26
+ case "json":
27
+ return (0, json_1.exportToJSON)(history, options);
28
+ case "compact":
29
+ return (0, json_1.exportToJSON)(history, { ...options, format: "compact" });
30
+ default:
31
+ return (0, json_1.exportToJSON)(history, options);
32
+ }
33
+ }
34
+ /**
35
+ * Build hand history object without formatting
36
+ *
37
+ * @param state Final game state
38
+ * @returns Structured hand history object
39
+ */
40
+ function getHandHistory(state) {
41
+ return (0, handHistoryBuilder_1.buildHandHistory)(state);
42
+ }
43
+ /**
44
+ * Export multiple hands to a single file
45
+ *
46
+ * @param states Array of final game states
47
+ * @param options Export options
48
+ * @returns Formatted multi-hand history
49
+ */
50
+ function exportMultipleHands(states, options = { format: "json" }) {
51
+ if (options.format === "pokerstars") {
52
+ // PokerStars format: separate hands with blank lines
53
+ return states.map((state) => exportHandHistory(state, options)).join("\n\n\n");
54
+ }
55
+ else {
56
+ // JSON format: array of hands
57
+ const histories = states.map((state) => (0, handHistoryBuilder_1.buildHandHistory)(state));
58
+ return JSON.stringify(histories, null, options.format === "compact" ? 0 : 2);
59
+ }
60
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * JSON hand history format exporter
3
+ *
4
+ * Machine-readable format for analysis and storage
5
+ */
6
+ import { HandHistory, ExportOptions } from "../types";
7
+ /**
8
+ * Export hand history to JSON format
9
+ */
10
+ export declare function exportToJSON(history: HandHistory, options?: ExportOptions): string;
11
+ /**
12
+ * Parse hand history from JSON
13
+ */
14
+ export declare function parseFromJSON(json: string): HandHistory;
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ /**
3
+ * JSON hand history format exporter
4
+ *
5
+ * Machine-readable format for analysis and storage
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.exportToJSON = exportToJSON;
9
+ exports.parseFromJSON = parseFromJSON;
10
+ /**
11
+ * Export hand history to JSON format
12
+ */
13
+ function exportToJSON(history, options = { format: "json" }) {
14
+ // Create sanitized version based on options
15
+ const sanitized = sanitizeHandHistory(history, options);
16
+ if (options.format === "compact") {
17
+ return JSON.stringify(sanitized);
18
+ }
19
+ return JSON.stringify(sanitized, null, 2);
20
+ }
21
+ /**
22
+ * Sanitize hand history based on export options
23
+ */
24
+ function sanitizeHandHistory(history, options) {
25
+ if (options.includeHoleCards) {
26
+ return history;
27
+ }
28
+ // Remove hole cards for privacy
29
+ return {
30
+ ...history,
31
+ players: history.players.map((player) => ({
32
+ ...player,
33
+ cards: undefined,
34
+ })),
35
+ winners: history.winners.map((winner) => ({
36
+ ...winner,
37
+ hand: winner.handRank ? winner.hand : undefined, // Keep if shown at showdown
38
+ })),
39
+ };
40
+ }
41
+ /**
42
+ * Parse hand history from JSON
43
+ */
44
+ function parseFromJSON(json) {
45
+ return JSON.parse(json);
46
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * PokerStars hand history format exporter
3
+ *
4
+ * Format specification based on PokerStars hand history text format
5
+ */
6
+ import { HandHistory, ExportOptions } from "../types";
7
+ /**
8
+ * Export hand history to PokerStars format
9
+ */
10
+ export declare function exportToPokerStars(history: HandHistory, options?: ExportOptions): string;
@@ -0,0 +1,188 @@
1
+ "use strict";
2
+ /**
3
+ * PokerStars hand history format exporter
4
+ *
5
+ * Format specification based on PokerStars hand history text format
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.exportToPokerStars = exportToPokerStars;
9
+ /**
10
+ * Export hand history to PokerStars format
11
+ */
12
+ function exportToPokerStars(history, options = { format: "pokerstars" }) {
13
+ const lines = [];
14
+ // Header line
15
+ lines.push(buildHeader(history));
16
+ lines.push(buildTableInfo(history));
17
+ // Button and seats
18
+ lines.push(`Button: seat #${history.buttonSeat + 1}`);
19
+ lines.push("");
20
+ // Player stacks
21
+ for (const player of history.players) {
22
+ lines.push(`Seat ${player.seat + 1}: ${player.name} ($${player.startingStack.toFixed(2)} in chips)`);
23
+ }
24
+ lines.push("");
25
+ // Blinds and antes
26
+ lines.push(buildBlindsLine(history));
27
+ if (history.stakes.ante > 0) {
28
+ lines.push(buildAntesLine(history));
29
+ }
30
+ // Hole cards (dealt to)
31
+ lines.push(buildHoleCardsLine(history, options));
32
+ // Each street
33
+ for (const street of history.streets) {
34
+ lines.push("");
35
+ lines.push(buildStreetHeader(street));
36
+ for (const action of street.actions) {
37
+ lines.push(buildActionLine(action, history));
38
+ }
39
+ }
40
+ // Summary
41
+ lines.push("");
42
+ lines.push(buildSummary(history));
43
+ return lines.join("\n");
44
+ }
45
+ /**
46
+ * Build header line
47
+ */
48
+ function buildHeader(history) {
49
+ const date = new Date(history.timestamp);
50
+ const dateStr = formatPokerStarsDate(date);
51
+ return `PokerStars Hand #${history.handId}: Hold'em No Limit ($${history.stakes.smallBlind}/$${history.stakes.bigBlind}) - ${dateStr}`;
52
+ }
53
+ /**
54
+ * Build table info line
55
+ */
56
+ function buildTableInfo(history) {
57
+ return `Table '${history.tableName}' ${history.maxPlayers}-max`;
58
+ }
59
+ /**
60
+ * Build blinds posting line
61
+ */
62
+ function buildBlindsLine(history) {
63
+ const lines = [];
64
+ // Find SB and BB seats (button+1 and button+2)
65
+ const sbSeat = (history.buttonSeat + 1) % history.maxPlayers;
66
+ const bbSeat = (history.buttonSeat + 2) % history.maxPlayers;
67
+ const sbPlayer = history.players.find((p) => p.seat === sbSeat);
68
+ const bbPlayer = history.players.find((p) => p.seat === bbSeat);
69
+ if (sbPlayer) {
70
+ lines.push(`${sbPlayer.name}: posts small blind $${history.stakes.smallBlind.toFixed(2)}`);
71
+ }
72
+ if (bbPlayer) {
73
+ lines.push(`${bbPlayer.name}: posts big blind $${history.stakes.bigBlind.toFixed(2)}`);
74
+ }
75
+ return lines.join("\n");
76
+ }
77
+ /**
78
+ * Build antes line
79
+ */
80
+ function buildAntesLine(history) {
81
+ const lines = [];
82
+ for (const player of history.players) {
83
+ lines.push(`${player.name}: posts ante $${history.stakes.ante.toFixed(2)}`);
84
+ }
85
+ return lines.join("\n");
86
+ }
87
+ /**
88
+ * Build hole cards line
89
+ */
90
+ function buildHoleCardsLine(history, options) {
91
+ // In PokerStars format, only show hero's cards
92
+ // For analysis mode, show all cards
93
+ if (options.includeHoleCards) {
94
+ const lines = ["*** HOLE CARDS ***"];
95
+ for (const player of history.players) {
96
+ if (player.cards && player.cards.length > 0) {
97
+ lines.push(`Dealt to ${player.name} [${player.cards.join(" ")}]`);
98
+ }
99
+ }
100
+ return lines.join("\n");
101
+ }
102
+ else {
103
+ return "*** HOLE CARDS ***";
104
+ }
105
+ }
106
+ /**
107
+ * Build street header
108
+ */
109
+ function buildStreetHeader(street) {
110
+ switch (street.street) {
111
+ case "FLOP" /* Street.FLOP */:
112
+ return `*** FLOP *** [${street.board.join(" ")}]`;
113
+ case "TURN" /* Street.TURN */:
114
+ return `*** TURN *** [${street.board.slice(0, 3).join(" ")}] [${street.board[3]}]`;
115
+ case "RIVER" /* Street.RIVER */:
116
+ return `*** RIVER *** [${street.board.slice(0, 4).join(" ")}] [${street.board[4]}]`;
117
+ case "SHOWDOWN" /* Street.SHOWDOWN */:
118
+ return "*** SHOW DOWN ***";
119
+ default:
120
+ return "";
121
+ }
122
+ }
123
+ /**
124
+ * Build action line
125
+ */
126
+ function buildActionLine(action, history) {
127
+ const player = history.players.find((p) => p.seat === action.seat);
128
+ const name = player?.name ?? `Player ${action.seat + 1}`;
129
+ switch (action.action.type) {
130
+ case "FOLD" /* ActionType.FOLD */:
131
+ return `${name}: folds`;
132
+ case "CHECK" /* ActionType.CHECK */:
133
+ return `${name}: checks`;
134
+ case "CALL" /* ActionType.CALL */:
135
+ const callAmount = action.amount ?? 0;
136
+ if (action.isAllIn) {
137
+ return `${name}: calls $${callAmount.toFixed(2)} and is all-in`;
138
+ }
139
+ return `${name}: calls $${callAmount.toFixed(2)}`;
140
+ case "BET" /* ActionType.BET */:
141
+ const betAmount = action.amount ?? 0;
142
+ if (action.isAllIn) {
143
+ return `${name}: bets $${betAmount.toFixed(2)} and is all-in`;
144
+ }
145
+ return `${name}: bets $${betAmount.toFixed(2)}`;
146
+ case "RAISE" /* ActionType.RAISE */:
147
+ const raiseAmount = action.amount ?? 0;
148
+ if (action.isAllIn) {
149
+ return `${name}: raises to $${raiseAmount.toFixed(2)} and is all-in`;
150
+ }
151
+ return `${name}: raises to $${raiseAmount.toFixed(2)}`;
152
+ default:
153
+ return `${name}: ${action.action.type.toLowerCase()}`;
154
+ }
155
+ }
156
+ /**
157
+ * Build summary section
158
+ */
159
+ function buildSummary(history) {
160
+ const lines = ["*** SUMMARY ***"];
161
+ lines.push(`Total pot $${history.totalPot.toFixed(2)}`);
162
+ lines.push(`Button: seat #${history.buttonSeat + 1}`);
163
+ // Board
164
+ const lastStreet = history.streets[history.streets.length - 1];
165
+ if (lastStreet && lastStreet.board.length > 0) {
166
+ lines.push(`Board [${lastStreet.board.join(" ")}]`);
167
+ }
168
+ // Winners
169
+ for (const winner of history.winners) {
170
+ const handInfo = winner.handRank ? ` with ${winner.handRank}` : "";
171
+ lines.push(`Seat ${winner.seat + 1}: ${winner.playerName} won ($${winner.amount.toFixed(2)})${handInfo}`);
172
+ }
173
+ return lines.join("\n");
174
+ }
175
+ /**
176
+ * Format date in PokerStars format
177
+ */
178
+ function formatPokerStarsDate(date) {
179
+ // Use UTC to avoid timezone misrepresentation
180
+ // Server's local time zone should not affect hand history exports
181
+ const year = date.getUTCFullYear();
182
+ const month = String(date.getUTCMonth() + 1).padStart(2, "0");
183
+ const day = String(date.getUTCDate()).padStart(2, "0");
184
+ const hours = String(date.getUTCHours()).padStart(2, "0");
185
+ const minutes = String(date.getUTCMinutes()).padStart(2, "0");
186
+ const seconds = String(date.getUTCSeconds()).padStart(2, "0");
187
+ return `${year}/${month}/${day} ${hours}:${minutes}:${seconds} UTC`;
188
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Build hand history from game state and action history
3
+ */
4
+ import { GameState } from "@pokertools/types";
5
+ import { HandHistory } from "./types";
6
+ /**
7
+ * Build complete hand history from final game state
8
+ * Call this after a hand is complete (winners determined)
9
+ */
10
+ export declare function buildHandHistory(finalState: GameState, tableName?: string, gameType?: "Cash" | "Tournament"): HandHistory;
@@ -0,0 +1,179 @@
1
+ "use strict";
2
+ /**
3
+ * Build hand history from game state and action history
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.buildHandHistory = buildHandHistory;
7
+ /**
8
+ * Build complete hand history from final game state
9
+ * Call this after a hand is complete (winners determined)
10
+ */
11
+ function buildHandHistory(finalState, tableName = "Table 1", gameType = "Cash") {
12
+ const players = buildPlayerHistory(finalState);
13
+ const streets = buildStreetHistory(finalState);
14
+ const winners = buildWinnerHistory(finalState);
15
+ const totalPot = winners.reduce((sum, w) => sum + w.amount, 0);
16
+ return {
17
+ handId: finalState.handId,
18
+ timestamp: finalState.timestamp,
19
+ tableName,
20
+ gameType,
21
+ stakes: {
22
+ smallBlind: finalState.smallBlind,
23
+ bigBlind: finalState.bigBlind,
24
+ ante: finalState.ante,
25
+ },
26
+ maxPlayers: finalState.maxPlayers,
27
+ buttonSeat: finalState.buttonSeat ?? 0,
28
+ players,
29
+ streets,
30
+ winners,
31
+ totalPot,
32
+ };
33
+ }
34
+ /**
35
+ * Build player history records
36
+ */
37
+ function buildPlayerHistory(state) {
38
+ const players = [];
39
+ for (const player of state.players) {
40
+ if (!player)
41
+ continue;
42
+ // Calculate starting stack (current + invested)
43
+ const startingStack = player.stack + player.totalInvestedThisHand;
44
+ players.push({
45
+ seat: player.seat,
46
+ name: player.name,
47
+ startingStack,
48
+ endingStack: player.stack,
49
+ cards: player.hand ? [...player.hand] : undefined,
50
+ });
51
+ }
52
+ return players;
53
+ }
54
+ /**
55
+ * Build street-by-street history
56
+ */
57
+ function buildStreetHistory(state) {
58
+ const streets = [];
59
+ const actionsByStreet = groupActionsByStreet(state);
60
+ // Build history for each street that had actions
61
+ for (const [street, actions] of actionsByStreet) {
62
+ const board = getBoardForStreet(state, street);
63
+ const pot = calculatePotAtStreet(actions);
64
+ streets.push({
65
+ street,
66
+ board,
67
+ actions,
68
+ pot,
69
+ });
70
+ }
71
+ return streets;
72
+ }
73
+ /**
74
+ * Group actions by street
75
+ */
76
+ function groupActionsByStreet(state) {
77
+ const grouped = new Map();
78
+ // Initialize with all streets
79
+ grouped.set("PREFLOP" /* Street.PREFLOP */, []);
80
+ grouped.set("FLOP" /* Street.FLOP */, []);
81
+ grouped.set("TURN" /* Street.TURN */, []);
82
+ grouped.set("RIVER" /* Street.RIVER */, []);
83
+ grouped.set("SHOWDOWN" /* Street.SHOWDOWN */, []);
84
+ // Group action history by street
85
+ for (const record of state.actionHistory) {
86
+ if (record.seat === null)
87
+ continue; // Skip table-level actions
88
+ const street = record.street ?? state.street;
89
+ const existing = grouped.get(street) ?? [];
90
+ const actionRecord = {
91
+ seat: record.seat,
92
+ playerName: state.players[record.seat]?.name ?? `Player ${record.seat}`,
93
+ action: record.action,
94
+ amount: getActionAmount(record.action),
95
+ isAllIn: isAllInAction(record.action, state, record.seat),
96
+ timestamp: record.action.timestamp ?? 0,
97
+ };
98
+ existing.push(actionRecord);
99
+ grouped.set(street, existing);
100
+ }
101
+ // Remove empty streets
102
+ for (const [street, actions] of grouped) {
103
+ if (actions.length === 0) {
104
+ grouped.delete(street);
105
+ }
106
+ }
107
+ return grouped;
108
+ }
109
+ /**
110
+ * Get board cards for a given street
111
+ */
112
+ function getBoardForStreet(state, street) {
113
+ switch (street) {
114
+ case "PREFLOP" /* Street.PREFLOP */:
115
+ return [];
116
+ case "FLOP" /* Street.FLOP */:
117
+ return state.board.slice(0, 3);
118
+ case "TURN" /* Street.TURN */:
119
+ return state.board.slice(0, 4);
120
+ case "RIVER" /* Street.RIVER */:
121
+ case "SHOWDOWN" /* Street.SHOWDOWN */:
122
+ return state.board.slice(0, 5);
123
+ default:
124
+ return [];
125
+ }
126
+ }
127
+ /**
128
+ * Calculate pot size at end of street actions
129
+ */
130
+ function calculatePotAtStreet(actions) {
131
+ let pot = 0;
132
+ for (const action of actions) {
133
+ if (action.amount) {
134
+ pot += action.amount;
135
+ }
136
+ }
137
+ return pot;
138
+ }
139
+ /**
140
+ * Extract amount from action
141
+ */
142
+ function getActionAmount(action) {
143
+ switch (action.type) {
144
+ case "BET" /* ActionType.BET */:
145
+ case "RAISE" /* ActionType.RAISE */:
146
+ return action.amount;
147
+ case "CALL" /* ActionType.CALL */:
148
+ // Call amount would need to be tracked in action history
149
+ return undefined;
150
+ default:
151
+ return undefined;
152
+ }
153
+ }
154
+ /**
155
+ * Check if action resulted in all-in
156
+ */
157
+ function isAllInAction(action, state, seat) {
158
+ const player = state.players[seat];
159
+ if (!player)
160
+ return false;
161
+ return player.stack === 0 && player.totalInvestedThisHand > 0;
162
+ }
163
+ /**
164
+ * Build winner records
165
+ */
166
+ function buildWinnerHistory(state) {
167
+ if (!state.winners)
168
+ return [];
169
+ return state.winners.map((winner) => {
170
+ const player = state.players[winner.seat];
171
+ return {
172
+ seat: winner.seat,
173
+ playerName: player?.name ?? `Player ${winner.seat}`,
174
+ amount: winner.amount,
175
+ hand: winner.hand ?? undefined,
176
+ handRank: winner.handRank ?? undefined,
177
+ };
178
+ });
179
+ }
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Hand history types
3
+ */
4
+ import { Street, Action } from "@pokertools/types";
5
+ /**
6
+ * Hand history record
7
+ * Contains complete information about a single hand
8
+ */
9
+ export interface HandHistory {
10
+ readonly handId: string;
11
+ readonly timestamp: number;
12
+ readonly tableName: string;
13
+ readonly gameType: "Cash" | "Tournament";
14
+ readonly stakes: {
15
+ readonly smallBlind: number;
16
+ readonly bigBlind: number;
17
+ readonly ante: number;
18
+ };
19
+ readonly maxPlayers: number;
20
+ readonly buttonSeat: number;
21
+ readonly players: readonly HandHistoryPlayer[];
22
+ readonly streets: readonly StreetHistory[];
23
+ readonly winners: readonly WinnerRecord[];
24
+ readonly totalPot: number;
25
+ }
26
+ /**
27
+ * Player information in hand history
28
+ */
29
+ export interface HandHistoryPlayer {
30
+ readonly seat: number;
31
+ readonly name: string;
32
+ readonly startingStack: number;
33
+ readonly endingStack: number;
34
+ readonly cards?: readonly string[];
35
+ }
36
+ /**
37
+ * History for a single street
38
+ */
39
+ export interface StreetHistory {
40
+ readonly street: Street;
41
+ readonly board: readonly string[];
42
+ readonly actions: readonly ActionRecord[];
43
+ readonly pot: number;
44
+ }
45
+ /**
46
+ * Action record in history
47
+ */
48
+ export interface ActionRecord {
49
+ readonly seat: number;
50
+ readonly playerName: string;
51
+ readonly action: Action;
52
+ readonly amount?: number;
53
+ readonly isAllIn?: boolean;
54
+ readonly timestamp: number;
55
+ }
56
+ /**
57
+ * Winner record
58
+ */
59
+ export interface WinnerRecord {
60
+ readonly seat: number;
61
+ readonly playerName: string;
62
+ readonly amount: number;
63
+ readonly hand?: readonly string[];
64
+ readonly handRank?: string;
65
+ }
66
+ /**
67
+ * Hand history format options
68
+ */
69
+ export interface ExportOptions {
70
+ readonly format: "pokerstars" | "json" | "compact";
71
+ readonly includeHoleCards?: boolean;
72
+ readonly timezone?: string;
73
+ }
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ /**
3
+ * Hand history types
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,8 @@
1
+ export { PokerEngine } from "./engine/PokerEngine";
2
+ export * from "@pokertools/types";
3
+ export * from "./errors";
4
+ export { createSnapshot, restoreFromSnapshot, Snapshot } from "./utils/serialization";
5
+ export { createPublicView } from "./utils/viewMasking";
6
+ export { calculateTotalChips, auditChipConservation } from "./utils/invariants";
7
+ export { exportHandHistory, getHandHistory, exportMultipleHands } from "./history/exporter";
8
+ export type { HandHistory, HandHistoryPlayer, StreetHistory, ExportOptions } from "./history/types";