diff --git a/README.md b/README.md index 8a3233d..5a1b2ca 100644 --- a/README.md +++ b/README.md @@ -19,11 +19,12 @@ Add `@poker-apprentice/hand-history-parser` as a dependency. ### `parseHand` -This promise-based function can be used to parse hand histories from any [support poker site](#supported-poker-sites). To use it, simply pass the contents of an individual hand history. +This promise-based function can be used to parse hand histories from any [support poker site](#supported-poker-sites). To use it, simply pass the contents of an individual hand history along with the filename. ```ts -// assumes `hand` is a string containing the hand history file contents -const handHistory = parseHand(hand) +// Assumes `hand` is a string containing the hand history file contents & `filename` +// is a string containing the hand history filename. +const handHistory = parseHand({ hand, filename }) .then((handHistory) => console.log(handHistory)) .catch((err) => console.error(err)); ``` @@ -31,9 +32,10 @@ const handHistory = parseHand(hand) If preferred, an async/await implementation can be used instead. ```ts -// assumes `hand` is a string containing the hand history file contents +// Assumes `hand` is a string containing the hand history file contents & `filename` +// is a string containing the hand history filename. try { - const handHistory = await parseHand(hand); + const handHistory = await parseHand({ hand, filename }); console.log(handHistory); } catch (err) { console.error(err); @@ -274,9 +276,9 @@ This package currently supports the following poker sites & networks: | Site | Network | Cash Games | Tournaments | Hold'em | Omaha | Omaha-8 | Stud | Currencies | | -------- | -------- | :--------: | :---------: | :-----: | :-----: | :-----: | :--: | ---------- | -| Bodog | Ignition | ✅ | ❌ | ✅ | ✅ | ✅ | N/A | USD | -| Bovada | Ignition | ✅ | ❌ | ✅ | ✅ | ✅ | N/A | USD | -| Ignition | Ignition | ✅ | ❌ | ✅ | ✅ | ✅ | N/A | USD | +| Bodog | Ignition | ✅ | ✅ | ✅ | ✅ | ✅ | N/A | USD | +| Bovada | Ignition | ✅ | ✅ | ✅ | ✅ | ✅ | N/A | USD | +| Ignition | Ignition | ✅ | ✅ | ✅ | ✅ | ✅ | N/A | USD | The parser is built in a way that it is relatively straightforward to extend with new poker sites. The main thing that is missing for this to happen is a combination of sample hand histories to implement against & time. @@ -298,17 +300,18 @@ The architecture is as straightforward as possible, with the most complex part i ```bash yarn install ``` -1. Add a `.g4` grammar file under `grammar/[PokerSite].g4`. +1. Add a `.g4` grammar file under `grammar/[PokerNetwork].g4`. It is recommended that this file be parsed by different types of lines that appear within the file. These lines can typically be broken down into one of: game metadata, player metadata, & player actions. These three types of information comprise the data that must be returned by a new hand history parser. 1. Build all ANTLR grammar files: ```bash yarn build:grammar ``` -1. Add a visitor for your poker site under `networks/[pokersite]/[PokerSite]HandHistoryVisitor.ts`. +1. Add a visitor for your poker site under `networks/[pokernetwork]/[PokerNetwork]HandHistoryVisitor.ts`. The visitor is responsible for returning a value representing the current node that is being parsed bym ANTLR. Ideally each line being parsed can return an object representing one of the three types of metadata outlined above. However, it may be necessary to utilize a custom return type that may or may not wrap the shared `Action`, `Player`, and `GameInfo` types depending on the poker site's hand history structure. -1. Add a parser for your poker site under `networks/[pokersite]/parseHand.ts`. +1. Add a parser for your poker site under `networks/[pokernetwork]/parseHand.ts`. This function is intended to delegate to the Visitor class implemented above, massaging the return value into a `HandHistory` object as a return value. -1. Include tests for common & uncommon parsing scenarios that demonstrate the `parseHand` function is working as intended. Common & uncommon scenarios include: +1. Add an if-condition for your poker site within `parseSite.ts`. +1. Include tests for common & uncommon parsing scenarios that demonstrate the `parseHand` and `parseSite` functions are working as intended. Common & uncommon scenarios include for the `parseHand` function include: - Multiple variants (e.g.: Hold'em, Omaha, Omaha-8, etc.), - Multiple betting structures (i.e.: limit, no-limit, pot-limit, spread-limit, cap-limit), - Multiple currencies, diff --git a/package.json b/package.json index 47df3d7..0552692 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,8 @@ "@poker-apprentice/types": "^1.4.0", "antlr4ts": "^0.5.0-alpha.4", "assert-never": "^1.2.1", - "bignumber.js": "^9.1.2" + "bignumber.js": "^9.1.2", + "lodash": "^4.17.21" }, "devDependencies": { "@babel/plugin-transform-runtime": "^7.22.10", @@ -55,6 +56,7 @@ "@rollup/plugin-node-resolve": "^15.2.1", "@rollup/plugin-terser": "^0.4.3", "@types/jest": "^29.5.4", + "@types/lodash": "^4.14.202", "@typescript-eslint/eslint-plugin": "^6.5.0", "@typescript-eslint/parser": "^6.5.0", "antlr4ts-cli": "^0.5.0-alpha.4", diff --git a/src/__fixtures__/hands/bovada.ts b/src/__fixtures__/hands/bovada.ts index 741ced5..5adaa91 100644 --- a/src/__fixtures__/hands/bovada.ts +++ b/src/__fixtures__/hands/bovada.ts @@ -677,3 +677,59 @@ Seat+2: UTG lost with One pair [5s Ad-Ad Ac Kh 7h 6s] Seat+3: UTG+1 Folded before the FLOP Seat+4: Dealer Folded before the FLOP Seat+6: Big Blind $14.82 with One pair [As Qs-As Ac Kh Qs 7h]`; + +export const HAND_TOURNAMENT = `Bovada Hand #4561202298: OMAHA HiLo Tournament #47895702 TBL#2, Normal- Level 10 (200/400) - 2022-06-25 22:24:22 +Seat 30: UTG+2 (4,619 in chips) +Seat 37: UTG+3 (6,282 in chips) +Seat 86: Dealer (4,300 in chips) +Seat 16: Small Blind (11,003 in chips) +Seat 89: Big Blind [ME] (3,000 in chips) +Seat 61: UTG (5,714 in chips) +Seat 68: UTG+1 (15,486 in chips) +Dealer : Set dealer [4] +UTG+2 : Ante chip 40 +UTG+3 : Ante chip 40 +Dealer : Ante chip 40 +Small Blind : Ante chip 40 +Big Blind [ME] : Ante chip 40 +UTG : Ante chip 40 +UTG+1 : Ante chip 40 +Small Blind : Small blind 200 +Big Blind [ME] : Big blind 400 +*** HOLE CARDS *** +UTG+2 : Card dealt to a spot [9d 5c 2c Td] +UTG+3 : Card dealt to a spot [4c Ad Th 8h] +Dealer : Card dealt to a spot [6h Ac 3s 7s] +Small Blind : Card dealt to a spot [Qd 3d Ts 7d] +Big Blind [ME] : Card dealt to a spot [Js As Jd 5s] +UTG : Card dealt to a spot [6s 8c 7c Ah] +UTG+1 : Card dealt to a spot [8s 9c 2h 3h] +UTG : Folds +UTG+1 : Folds +UTG+2 : Raises 800 to 800 +UTG+3 : Folds +Dealer : Fold(Blind Disconnected) +Small Blind : Sit out +Small Blind : Folds(timeout) +Small Blind : Re-join +Big Blind [ME] : Call 400 +*** FLOP *** [2d 4d Qh] +Big Blind [ME] : Checks +UTG+2 : Bets 2080 +Big Blind [ME] : All-in(raise) 2160 to 2160 +UTG+2 : Call 80 +*** TURN *** [2d 4d Qh] [Ks] +*** RIVER *** [2d 4d Qh Ks] [Kh] +UTG+2 : Showdown [2c Td 2d Ks Kh] (Two pair) +Big Blind [ME] : Showdown [Js Jd Qh Ks Kh] (Two pair) +Big Blind [ME] : Hand Result 6400 +*** SUMMARY *** +Total Pot(6400) +Board [2d 4d Qh Ks Kh] +Seat+30: UTG+2 lose with Two pair [9d 5c 2c Td-2c Td 2d Ks Kh] +Seat+37: UTG+3 Folded on the FLOP +Seat+86: Dealer Folded on the FLOP +Seat+16: Small Blind Folded on the FLOP +Seat+89: Big Blind HI 6400 with Two pair [Js As Jd 5s-Js Jd Qh Ks Kh] +Seat+61: UTG Folded on the FLOP +Seat+68: UTG+1 Folded on the FLOP`; diff --git a/src/grammar/Ignition.g4 b/src/grammar/Ignition.g4 index 2b29197..2538a22 100644 --- a/src/grammar/Ignition.g4 +++ b/src/grammar/Ignition.g4 @@ -3,7 +3,8 @@ grammar Ignition; // file entry handHistory: line | ((line EOL)+ line); line: - ( lineMeta + ( lineMetaCash + | lineMetaTournament | linePlayer | lineDealer | lineSmallBlind @@ -23,11 +24,12 @@ line: ); // lines of text -lineMeta : site 'Hand' '#' handNumber fastFold? ('TBL#' | 'ID#') tableNumber variant bettingStructure '-' timestamp; +lineMetaCash : site 'Hand' '#' handNumber fastFold? ('TBL#' | 'ID#') tableNumber variant bettingStructure '-' timestamp; +lineMetaTournament: site 'Hand' '#' handNumber COLON variant 'Tournament' '#' tournamentNumber 'TBL#' tableNumber ',' tournamentSpeed '-' 'Level' tournamentLevel '(' chipCount '/' chipCount ')' '-' timestamp; linePlayer : 'Seat' seatNumber COLON position ME? '(' chipCount 'in chips)'; lineDealer : ('Dealer' ME? COLON)? 'Set dealer' ('[' INT ']')?; -lineSmallBlind : ('Small Blind' | 'Dealer') ME? COLON 'Small Blind' chipCount; -lineBigBlind : 'Big Blind' ME? COLON 'Big blind' chipCount; +lineSmallBlind : ('Small Blind' | 'Dealer') ME? COLON ('Small Blind' | 'Small blind') chipCount; +lineBigBlind : 'Big Blind' ME? COLON ('Big Blind' | 'Big blind') chipCount; linePost : position ME? COLON 'Posts' DEAD? 'chip' chipCount; lineStreet : '***' STREET '***' boardSections?; lineHandsDealt : position ME? COLON 'Card dealt to a spot' hand; @@ -39,8 +41,10 @@ lineMisc : | 'Seat sit out' | 'Seat stand' | 'Seat re-join' + | 'Re-join' | 'Table enter user' | 'Table leave user' + | 'Sit out' | 'Sitout' forcedActionReason | 'Enter' forcedActionReason | 'Leave' forcedActionReason @@ -49,7 +53,7 @@ lineAction : position ME? COLON action; lineMuck : position ME? COLON 'Does not show' hand '(' handStrength ')'; lineUncalled : position ME? COLON 'Return uncalled portion of bet' chipCount; lineShowdown : position ME? COLON showdownAction hand? '(' handStrength ')'; -lineResult : position ME? COLON 'Hand result' ('-' SIDEPOT)? chipCount; +lineResult : position ME? COLON ('Hand Result' | 'Hand result') ('-' SIDEPOT)? chipCount; lineTotalPot : 'Total Pot' '(' chipCount ')'; lineBoard : 'Board' board; lineActionSummary: @@ -67,13 +71,16 @@ lineActionSummary: handNumber : INT; seatNumber : INT; tableNumber : INT; +tournamentNumber: INT; +tournamentLevel: INT; +tournamentSpeed: 'Normal' | 'Turbo'; timestamp : INT '-' INT '-' INT INT ':' INT ':' INT; site : 'Bodog' | 'Bovada' | 'Ignition'; variant : 'HOLDEM' | 'OMAHA' | 'OMAHA HiLo' | 'HOLDEMZonePoker' | 'OMAHAZonePoker'; bettingStructure : 'No Limit' | 'Pot Limit' | 'Limit'; fastFold : FASTFOLD; -position : 'Small Blind' | 'Big Blind' | 'UTG' | 'UTG+1' | 'UTG+2' | 'Dealer'; -chipCount : '$' value; +position : 'Small Blind' | 'Big Blind' | 'UTG' | 'UTG+1' | 'UTG+2' | 'UTG+3' | 'UTG+4' | 'UTG+5' | 'Dealer'; +chipCount : '$'? value; value : (INT ',')* INT ('.' INT)?; board : '[' cards ']'; boardSections : board+; @@ -82,19 +89,20 @@ handAndBoard : '[' cards '-' cards ']'; cards : card+; card : CARD; handStrength : 'High Card' | 'One pair' | 'Two pair' | 'Three of a kind' | 'Straight' | 'Flush' | 'Full House' | 'Four of a kind' | 'Straight Flush' | 'Royal Straight Flush'; -action : actionFold | actionCheck | actionBet | actionCall | actionRaise | actionAllIn | actionAllInRaise; -actionFold : 'Folds' (forcedActionReason | '& shows' hand)?; +action : actionFold | actionCheck | actionBet | actionCall | actionRaise | actionAllIn | actionAllInRaise | actionAnte; +actionFold : ('Fold' | 'Folds') (forcedActionReason | '& shows' hand)?; actionCheck : 'Checks' forcedActionReason?; actionBet : 'Bets' chipCount; -actionCall : 'Calls' chipCount; +actionCall : ('Call' | 'Calls') chipCount; actionRaise : 'Raises' chipCount 'to' chipCount; actionAllIn : 'All-in' chipCount; actionAllInRaise : 'All-in' '(raise)' chipCount 'to' chipCount; +actionAnte : 'Ante chip' chipCount; forcedActionReason : '(' ~')'* ')'; showdownAction: 'Showdown' | 'Mucks'; winHighResult: chipCount 'with' handStrength (hand | handAndBoard); winLowResult : chipCount (hand | handAndBoard); -loseResult : 'lost with' handStrength (hand | handAndBoard); +loseResult : ('lose with' | 'lost with') handStrength (hand | handAndBoard); // lexer rules ME : '[ME]'; diff --git a/src/grammar/Site.g4 b/src/grammar/Site.g4 deleted file mode 100644 index f489285..0000000 --- a/src/grammar/Site.g4 +++ /dev/null @@ -1,24 +0,0 @@ -grammar Site; - -/** - * When new sites are added, include them here as a possible parsing path. - * (e.g.: `site: siteBovada | siteGG | sitePokerStars`) - */ -site: siteBovada; - -/** - * These lines must remain in sync with the `lineMeta` definition found in each of the individual - * site grammar files. - */ -siteBovada: bovadaSite 'Hand' '#' INT bovadaFastFold? ('TBL#' | 'ID#') INT bovadaVariant bovadaBettingStructure '-' bovadaTimestamp; - -// lexer rules for individual sites & networks -bovadaSite: 'Bodog' | 'Bovada' | 'Ignition'; -bovadaFastFold: 'Zone Poker'; -bovadaVariant: 'HOLDEM' | 'OMAHA' | 'OMAHA HiLo' | 'HOLDEMZonePoker' | 'OMAHAZonePoker'; -bovadaBettingStructure: 'No Limit' | 'Pot Limit' | 'Limit'; -bovadaTimestamp: INT '-' INT '-' INT INT ':' INT ':' INT; - -// lexer rules -INT: [0-9]+; -WS: [ \t]+ -> skip; diff --git a/src/networks/all/SiteSwitchVisitor.ts b/src/networks/all/SiteSwitchVisitor.ts deleted file mode 100644 index e1403d4..0000000 --- a/src/networks/all/SiteSwitchVisitor.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { AbstractParseTreeVisitor } from 'antlr4ts/tree/AbstractParseTreeVisitor'; -import { SiteBovadaContext } from '~/grammar/SiteParser'; -import { SiteVisitor } from '~/grammar/SiteVisitor'; -import { Site } from '~/types'; - -export class SiteSwitchVisitor - extends AbstractParseTreeVisitor - implements SiteVisitor -{ - protected defaultResult(): undefined { - return undefined; - } - - visitSiteBovada(ctx: SiteBovadaContext): Site { - const site = ctx.bovadaSite().text; - switch (site) { - case 'Bodog': - return 'bodog'; - case 'Bovada': - return 'bovada'; - case 'Ignition': - return 'ignition'; - default: - throw new Error(`Unexpected site: "${ctx.text}"`); - } - } -} diff --git a/src/networks/ignition/IgnitionActionVisitor.ts b/src/networks/ignition/IgnitionActionVisitor.ts index c7a98b1..683c420 100644 --- a/src/networks/ignition/IgnitionActionVisitor.ts +++ b/src/networks/ignition/IgnitionActionVisitor.ts @@ -78,6 +78,13 @@ export class IgnitionActionVisitor return [{ type: 'raise', playerName, amount, totalBet, isAllIn: false }]; } + const anteAction = action.actionAnte(); + if (anteAction) { + const amount = new IgnitionChipCountVisitor().visit(anteAction.chipCount()).toString(); + const playerName = ctx.position().text; + return [{ type: 'post', postType: 'ante', playerName, amount }]; + } + throw new NotImplementedError(); } } diff --git a/src/networks/ignition/IgnitionHandHistoryVisitor.ts b/src/networks/ignition/IgnitionHandHistoryVisitor.ts index 47eeedb..c10ae58 100644 --- a/src/networks/ignition/IgnitionHandHistoryVisitor.ts +++ b/src/networks/ignition/IgnitionHandHistoryVisitor.ts @@ -8,7 +8,8 @@ import { HandStrengthContext, LineActionContext, LineHandsDealtContext, - LineMetaContext, + LineMetaCashContext, + LineMetaTournamentContext, LineMuckContext, LinePlayerContext, LinePostContext, @@ -22,7 +23,7 @@ import { VariantContext, } from '~/grammar/IgnitionParser'; import { IgnitionVisitor } from '~/grammar/IgnitionVisitor'; -import { BettingStructure, Position, Site, Street, Variant } from '~/types'; +import { BettingStructure, Position, Site, Street, TournamentSpeed, Variant } from '~/types'; import { IgnitionActionVisitor } from './IgnitionActionVisitor'; import { IgnitionChipCountVisitor } from './IgnitionChipCountVisitor'; import { Line } from './types'; @@ -88,6 +89,11 @@ const getPosition = (ctx: PositionContext): Position => { return 'UTG+1'; case 'UTG+2': return 'UTG+2'; + case 'UTG+3': + case 'UTG+4': + case 'UTG+5': + // TODO: this isn't accurate + return 'CO'; case 'Dealer': return 'BTN'; default: @@ -139,6 +145,17 @@ const getHandStrength = (ctx: HandStrengthContext): HandStrength => { } }; +const getTournamentSpeed = (speed: string): TournamentSpeed => { + switch (speed) { + case 'Turbo': + return 'turbo'; + case 'Normal': + return 'normal'; + default: + return 'normal'; + } +}; + export class IgnitionHandHistoryVisitor extends AbstractParseTreeVisitor implements IgnitionVisitor @@ -156,7 +173,7 @@ export class IgnitionHandHistoryVisitor return actions.map((action) => ({ type: 'action', action })); } - public visitLineMeta(ctx: LineMetaContext): Line[] { + public visitLineMetaCash(ctx: LineMetaCashContext): Line[] { const site = getSite(ctx.site()); const handNumber = ctx.handNumber().text; @@ -179,6 +196,7 @@ export class IgnitionHandHistoryVisitor return [ { type: 'meta', + gameType: 'cash', site, handNumber, tableNumber, @@ -190,6 +208,38 @@ export class IgnitionHandHistoryVisitor ]; } + public visitLineMetaTournament(ctx: LineMetaTournamentContext): Line[] { + const site = getSite(ctx.site()); + + const handNumber = ctx.handNumber().text; + const tableNumber = ctx.tableNumber().text; + + const variant = getVariant(ctx.variant()); + + const tournamentNumber = ctx.tournamentNumber().text; + const level = Number(ctx.tournamentLevel().text); + const speed = getTournamentSpeed(ctx.tournamentSpeed().text); + + const text = getSubstring(ctx.timestamp()); + const t = text.split(/\D/).map(Number); + const timestamp = new Date(t[0], t[1] - 1, t[2], t[3], t[4], t[5]); + + return [ + { + type: 'meta', + gameType: 'tournament', + site, + handNumber, + tableNumber, + tournamentNumber, + level, + speed, + variant, + timestamp, + }, + ]; + } + public visitLineSmallBlind(ctx: LineSmallBlindContext): Line[] { const chipCount = new IgnitionChipCountVisitor().visit(ctx.chipCount()).toString(); const playerName = 'Small Blind'; diff --git a/src/networks/ignition/parseFilename.test.ts b/src/networks/ignition/parseFilename.test.ts new file mode 100644 index 0000000..9794119 --- /dev/null +++ b/src/networks/ignition/parseFilename.test.ts @@ -0,0 +1,135 @@ +import { parseFilename } from './parseFilename'; + +describe('parseFilename', () => { + describe('cash games', () => { + it('parses ring games', () => { + const filename = + 'HH20220618-220216 - 12208188 - RING - $0.50-$1 - OMAHA - PL - TBL No.26894678.txt'; + expect(parseFilename(filename)).toMatchInlineSnapshot(` + { + "bettingStructure": "pot limit", + "bigBlind": "1", + "currency": "USD", + "isFastFold": false, + "smallBlind": "0.50", + "timestamp": 2022-06-18T22:02:16.000Z, + "type": "cash", + "variant": "omaha", + } + `); + }); + }); + + describe('tournaments', () => { + it('parses STT tournaments', () => { + const filename = + 'HH20220626-164936 - 6615315 - STT - Hyper Turbo (500 Chips) - $50-$2.50 - HOLDEM - NL -Tourney No.51169700.txt'; + expect(parseFilename(filename)).toMatchInlineSnapshot(` + { + "bettingStructure": "no limit", + "buyIn": "50", + "currency": "USD", + "entryFee": "2.50", + "format": "on-demand", + "guaranteedPrizePool": "0", + "isSatellite": false, + "name": "Hyper Turbo (500 Chips)", + "timestamp": 2022-06-26T16:49:36.000Z, + "tournamentNumber": "51169700", + "type": "tournament", + "variant": "holdem", + } + `); + }); + + it('parses MTT tournaments', () => { + const filename = + 'HH20220625-202000 - 6615318 - MTT - $300 Guaranteed (Beginner DS) - $4-$0.40 - OMAHA HiLo - PL -Tourney No.47895702.txt'; + expect(parseFilename(filename)).toMatchInlineSnapshot(` + { + "bettingStructure": "pot limit", + "buyIn": "4", + "currency": "USD", + "entryFee": "0.40", + "format": "freezeout", + "guaranteedPrizePool": "300", + "isSatellite": false, + "name": "$300 Guaranteed (Beginner DS)", + "timestamp": 2022-06-25T20:20:00.000Z, + "tournamentNumber": "47895702", + "type": "tournament", + "variant": "omaha-8", + } + `); + }); + + it('parses MSG tournaments', () => { + const filename = + 'HH20220626-165412 - 6615316 - MSG - 2-Table ($10 Knockout) - $25-$2.50 - HOLDEM - NL -Tourney No.51170049.txt'; + expect(parseFilename(filename)).toMatchInlineSnapshot(` + { + "bettingStructure": "no limit", + "buyIn": "25", + "currency": "USD", + "entryFee": "2.50", + "format": "on-demand", + "guaranteedPrizePool": "0", + "isSatellite": false, + "name": "2-Table ($10 Knockout)", + "timestamp": 2022-06-26T16:54:12.000Z, + "tournamentNumber": "51170049", + "type": "tournament", + "variant": "holdem", + } + `); + }); + + it('parses Jackpot Sit & Go tournaments', () => { + const filename = + 'HH20220619-004909 - 6556966 - Jackpot Sit & Go - Jackpot Sit And Go - $2 - TT$2-$0 - HOLDEM - NL -Tourney No.50991177.txt'; + expect(parseFilename(filename)).toMatchInlineSnapshot(`undefined`); + }); + + it('parses satellites', () => { + const filename = + 'HH20220626-082100 - 6615317 - MTT - Monthly Milly Sub-Satellite 2 Seats Gtd - $7-$0.70 - HOLDEM - NL -Tourney No.46767990.txt'; + expect(parseFilename(filename)).toMatchInlineSnapshot(` + { + "bettingStructure": "no limit", + "buyIn": "7", + "currency": "USD", + "entryFee": "0.70", + "format": "freezeout", + "guaranteedPrizePool": "0", + "isSatellite": true, + "name": "Monthly Milly Sub-Satellite 2 Seats Gtd", + "timestamp": 2022-06-26T08:21:00.000Z, + "tournamentNumber": "46767990", + "type": "tournament", + "variant": "holdem", + } + `); + }); + + it('parses guaranteed prize pools', () => { + const filename = + 'HH20220711-143000 - 6755578 - MTT - $15.000 Guaranteed (Monster Stack) - $100-$9 - HOLDEM - NL -Tourney No.50866781.txt'; + expect(parseFilename(filename)).toMatchInlineSnapshot(` + { + "bettingStructure": "no limit", + "buyIn": "100", + "currency": "USD", + "entryFee": "9", + "format": "freezeout", + "guaranteedPrizePool": "15000", + "isSatellite": false, + "name": "$15.000 Guaranteed (Monster Stack)", + "timestamp": 2022-07-11T14:30:00.000Z, + "tournamentNumber": "50866781", + "type": "tournament", + "variant": "holdem", + } + `); + }); + }); +}); diff --git a/src/networks/ignition/parseFilename.ts b/src/networks/ignition/parseFilename.ts new file mode 100644 index 0000000..6c923fd --- /dev/null +++ b/src/networks/ignition/parseFilename.ts @@ -0,0 +1,124 @@ +import { BettingStructure, TournamentFormat, Variant } from '~/types'; + +const CASH_REGEX = + /^HH(?\d+)-(?