From d98a28ea0ad19b648569027578375b19c0a9880f Mon Sep 17 00:00:00 2001 From: Chaz Littlejohn Date: Mon, 4 Apr 2011 04:50:01 +0000 Subject: [PATCH] Added support for board cards in FTP run it twice (RIT) hands. Hands continues to store the standard board for the hand, while run it twice board cards dealt after the all in are stored in the new Boards table. Designed to allow for more than 2 boards if a hand history allows it. Can be enabled for other sites by adding regex to detect multiple FLOP|TURN|RIVER deals in hhc.markStreets, and by updating hhc.readCommunityCards to save RIT streets and set hand.runItTimes with the number of deals. The Hands table now includes a 'runIt' BOOL indicating if a hands was run multiple times and board inserts are done in the storeHand method. --- pyfpdb/Database.py | 19 +++++++++++-- pyfpdb/DerivedStats.py | 14 ++++++++++ pyfpdb/FulltiltToFpdb.py | 21 ++++++++++---- pyfpdb/Hand.py | 6 ++-- pyfpdb/SQL.py | 57 +++++++++++++++++++++++++++++++++++++- pyfpdb/TestHandsPlayers.py | 4 ++- 6 files changed, 108 insertions(+), 13 deletions(-) diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py index 0bf5bd5b..f017f601 100644 --- a/pyfpdb/Database.py +++ b/pyfpdb/Database.py @@ -73,7 +73,7 @@ except ImportError: use_numpy = False -DB_VERSION = 153 +DB_VERSION = 154 # Variance created as sqlite has a bunch of undefined aggregate functions. @@ -129,6 +129,7 @@ class Database: , {'tab':'HandsActions', 'col':'handId', 'drop':1} , {'tab':'HandsActions', 'col':'playerId', 'drop':1} , {'tab':'HandsActions', 'col':'actionId', 'drop':1} + , {'tab':'Boards', 'col':'handId', 'drop':1} , {'tab':'HandsPlayers', 'col':'handId', 'drop':1} , {'tab':'HandsPlayers', 'col':'playerId', 'drop':1} , {'tab':'HandsPlayers', 'col':'tourneysPlayersId', 'drop':0} @@ -153,6 +154,7 @@ class Database: , [ # indexes for sqlite (list index 4) {'tab':'Hands', 'col':'gametypeId', 'drop':0} , {'tab':'Hands', 'col':'fileId', 'drop':0} + , {'tab':'Boards', 'col':'handId', 'drop':0} , {'tab':'HandsPlayers', 'col':'handId', 'drop':0} , {'tab':'HandsPlayers', 'col':'playerId', 'drop':0} , {'tab':'HandsPlayers', 'col':'tourneysPlayersId', 'drop':0} @@ -182,6 +184,7 @@ class Database: , [ # foreign keys for mysql (index 2) {'fktab':'Hands', 'fkcol':'gametypeId', 'rtab':'Gametypes', 'rcol':'id', 'drop':1} , {'fktab':'Hands', 'fkcol':'fileId', 'rtab':'Files', 'rcol':'id', 'drop':1} + , {'fktab':'Boards', 'fkcol':'handId', 'rtab':'Hands', 'rcol':'id', 'drop':1} , {'fktab':'HandsPlayers', 'fkcol':'handId', 'rtab':'Hands', 'rcol':'id', 'drop':1} , {'fktab':'HandsPlayers', 'fkcol':'playerId', 'rtab':'Players', 'rcol':'id', 'drop':1} , {'fktab':'HandsPlayers', 'fkcol':'tourneysPlayersId','rtab':'TourneysPlayers','rcol':'id', 'drop':1} @@ -198,6 +201,7 @@ class Database: , [ # foreign keys for postgres (index 3) {'fktab':'Hands', 'fkcol':'gametypeId', 'rtab':'Gametypes', 'rcol':'id', 'drop':1} , {'fktab':'Hands', 'fkcol':'fileId', 'rtab':'Files', 'rcol':'id', 'drop':1} + , {'fktab':'Boards', 'fkcol':'handId', 'rtab':'Hands', 'rcol':'id', 'drop':1} , {'fktab':'HandsPlayers', 'fkcol':'handId', 'rtab':'Hands', 'rcol':'id', 'drop':1} , {'fktab':'HandsPlayers', 'fkcol':'playerId', 'rtab':'Players', 'rcol':'id', 'drop':1} , {'fktab':'HandsActions', 'fkcol':'handId', 'rtab':'Hands', 'rcol':'id', 'drop':1} @@ -336,7 +340,7 @@ class Database: tables=self.cursor.execute(self.sql.query['list_tables']) tables=self.cursor.fetchall() - for table in (u'Actions', u'Autorates', u'Backings', u'Gametypes', u'Hands', u'HandsActions', u'HandsPlayers', u'Files', u'HudCache', u'SessionsCache', u'Players', u'RawHands', u'RawTourneys', u'Settings', u'Sites', u'TourneyTypes', u'Tourneys', u'TourneysPlayers'): + for table in (u'Actions', u'Autorates', u'Backings', u'Gametypes', u'Hands', u'Boards', u'HandsActions', u'HandsPlayers', u'Files', u'HudCache', u'SessionsCache', u'Players', u'RawHands', u'RawTourneys', u'Settings', u'Sites', u'TourneyTypes', u'Tourneys', u'TourneysPlayers'): print "table:", table result+="###################\nTable "+table+"\n###################\n" rows=self.cursor.execute(self.sql.query['get'+table]) @@ -1265,6 +1269,7 @@ class Database: c.execute(self.sql.query['createPlayersTable']) c.execute(self.sql.query['createAutoratesTable']) c.execute(self.sql.query['createHandsTable']) + c.execute(self.sql.query['createBoardsTable']) c.execute(self.sql.query['createTourneyTypesTable']) c.execute(self.sql.query['createTourneysTable']) c.execute(self.sql.query['createTourneysPlayersTable']) @@ -1875,6 +1880,7 @@ class Database: hdata['boardcard3'], hdata['boardcard4'], hdata['boardcard5'], + hdata['runIt'], hdata['playersAtStreet1'], hdata['playersAtStreet2'], hdata['playersAtStreet3'], @@ -1890,19 +1896,28 @@ class Database: hdata['street3Pot'], hdata['street4Pot'], hdata['showdownPot'], + hdata['boards'], hdata['id'] ]) if doinsert: + bbulk = [] for h in hbulk: id = h.pop() if hdata['sc'] and hdata['gsc']: h[4] = hdata['sc'][id]['id'] h[5] = hdata['gsc'][id]['id'] + boards = h.pop() + for b in boards: + bbulk += [[id] + b] q = self.sql.query['store_hand'] q = q.replace('%s', self.sql.query['placeholder']) c = self.get_cursor() c.executemany(q, hbulk) + q = self.sql.query['store_boards'] + q = q.replace('%s', self.sql.query['placeholder']) + c = self.get_cursor() + c.executemany(q, bbulk) self.commit() return hbulk diff --git a/pyfpdb/DerivedStats.py b/pyfpdb/DerivedStats.py index d29a33ad..6cc97d09 100644 --- a/pyfpdb/DerivedStats.py +++ b/pyfpdb/DerivedStats.py @@ -143,6 +143,20 @@ class DerivedStats(): self.hands['boardcard3'] = cards[2] self.hands['boardcard4'] = cards[3] self.hands['boardcard5'] = cards[4] + + self.hands['boards'] = [] + self.hands['runIt'] = False + for i in range(hand.runItTimes): + self.hands['runIt'] = True + boardcards = [] + for street in hand.communityStreets: + boardId = i+1 + street_i = street + str(boardId) + if street_i in hand.board: + boardcards += hand.board[street_i] + boardcards = [u'0x', u'0x', u'0x', u'0x', u'0x'] + boardcards + cards = [Card.encodeCard(c) for c in boardcards[-5:]] + self.hands['boards'] += [[boardId] + cards] #print "DEBUG: self.getStreetTotals = (%s, %s, %s, %s, %s)" % hand.getStreetTotals() totals = hand.getStreetTotals() diff --git a/pyfpdb/FulltiltToFpdb.py b/pyfpdb/FulltiltToFpdb.py index 4e956650..31f889b0 100755 --- a/pyfpdb/FulltiltToFpdb.py +++ b/pyfpdb/FulltiltToFpdb.py @@ -381,10 +381,16 @@ class Fulltilt(HandHistoryConverter): def markStreets(self, hand): if hand.gametype['base'] == 'hold': - m = re.search(r"\*\*\* HOLE CARDS \*\*\*(?P.+(?=\*\*\* FLOP \*\*\*)|.+)" - r"(\*\*\* FLOP \*\*\*(?P \[\S\S \S\S \S\S\].+(?=\*\*\* TURN \*\*\*)|.+))?" - r"(\*\*\* TURN \*\*\* \[\S\S \S\S \S\S] (?P\[\S\S\].+(?=\*\*\* RIVER \*\*\*)|.+))?" - r"(\*\*\* RIVER \*\*\* \[\S\S \S\S \S\S \S\S] (?P\[\S\S\].+))?", hand.handText,re.DOTALL) + m = re.search(r"\*\*\* HOLE CARDS \*\*\*(?P.+(?=\*\*\* FLOP (1\s)?\*\*\*)|.+)" + r"(\*\*\* FLOP \*\*\*(?P \[\S\S \S\S \S\S\].+(?=\*\*\* TURN (1\s)?\*\*\*)|.+))?" + r"(\*\*\* TURN \*\*\* \[\S\S \S\S \S\S] (?P\[\S\S\].+(?=\*\*\* RIVER (1\s)?\*\*\*)|.+))?" + r"(\*\*\* RIVER \*\*\* \[\S\S \S\S \S\S \S\S] (?P\[\S\S\].+))?" + r"(\*\*\* FLOP 1 \*\*\*(?P \[\S\S \S\S \S\S\].+(?=\*\*\* TURN 1 \*\*\*)|.+))?" + r"(\*\*\* TURN 1 \*\*\* \[\S\S \S\S \S\S] (?P\[\S\S\].+(?=\*\*\* RIVER 1 \*\*\*)|.+))?" + r"(\*\*\* RIVER 1 \*\*\* \[\S\S \S\S \S\S \S\S] (?P\[\S\S\].))?" + r"(\*\*\* FLOP 2 \*\*\*(?P \[\S\S \S\S \S\S\].+(?=\*\*\* TURN 2 \*\*\*)|.+))?" + r"(\*\*\* TURN 2 \*\*\* \[\S\S \S\S \S\S] (?P\[\S\S\].+(?=\*\*\* RIVER 2 \*\*\*)|.+))?" + r"(\*\*\* RIVER 2 \*\*\* \[\S\S \S\S \S\S \S\S] (?P\[\S\S\].+))?", hand.handText,re.DOTALL) elif hand.gametype['base'] == "stud": m = re.search(r"(?P.+(?=\*\*\* 3RD STREET \*\*\*)|.+)" r"(\*\*\* 3RD STREET \*\*\*(?P.+(?=\*\*\* 4TH STREET \*\*\*)|.+))?" @@ -403,10 +409,13 @@ class Fulltilt(HandHistoryConverter): def readCommunityCards(self, hand, street): # street has been matched by markStreets, so exists in this hand if street in ('FLOP','TURN','RIVER'): # a list of streets which get dealt community cards (i.e. all but PREFLOP) - #print "DEBUG readCommunityCards:", street, hand.streets.group(street) + #print "DEBUG readCommunityCards:", street, hand.streets[street] m = self.re_Board.search(hand.streets[street]) hand.setCommunityCards(street, m.group('CARDS').split(' ')) - + if street in ('FLOP1', 'TURN1', 'RIVER1', 'FLOP2', 'TURN2', 'RIVER2'): + m = self.re_Board.search(hand.streets[street]) + hand.setCommunityCards(street, m.group('CARDS').split(' ')) + hand.runItTimes = 2 def readBlinds(self, hand): try: diff --git a/pyfpdb/Hand.py b/pyfpdb/Hand.py index 4e5249a3..ae53bafa 100644 --- a/pyfpdb/Hand.py +++ b/pyfpdb/Hand.py @@ -77,6 +77,7 @@ class Hand(object): self.maxseats = None self.counted_seats = 0 self.buttonpos = 0 + self.runItTimes = 0 #tourney stuff self.tourNo = None @@ -887,9 +888,8 @@ class HoldemOmahaHand(Hand): hhc.readHeroCards(self) hhc.readShowdownActions(self) # Read actions in street order - for street in self.communityStreets: - if self.streets[street]: - hhc.readCommunityCards(self, street) + for street, text in self.streets.iteritems(): + if text: hhc.readCommunityCards(self, street) for street in self.actionStreets: if self.streets[street]: hhc.readAction(self, street) diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py index cf054d7e..a7cc824e 100644 --- a/pyfpdb/SQL.py +++ b/pyfpdb/SQL.py @@ -372,6 +372,7 @@ class Sql: boardcard4 smallint, boardcard5 smallint, texture smallint, + runIt BOOLEAN, playersVpi SMALLINT NOT NULL, /* num of players vpi */ playersAtStreet1 SMALLINT NOT NULL, /* num of players seeing flop/street4 */ playersAtStreet2 SMALLINT NOT NULL, @@ -412,6 +413,7 @@ class Sql: boardcard4 smallint, boardcard5 smallint, texture smallint, + runIt BOOLEAN, playersVpi SMALLINT NOT NULL, /* num of players vpi */ playersAtStreet1 SMALLINT NOT NULL, /* num of players seeing flop/street4 */ playersAtStreet2 SMALLINT NOT NULL, @@ -451,6 +453,7 @@ class Sql: boardcard4 INT, boardcard5 INT, texture INT, + runIt BOOLEAN, playersVpi INT NOT NULL, /* num of players vpi */ playersAtStreet1 INT NOT NULL, /* num of players seeing flop/street4 */ playersAtStreet2 INT NOT NULL, @@ -469,6 +472,42 @@ class Sql: showdownPot INT, /* pot size at sd/street7 */ comment TEXT, commentTs REAL)""" + + ################################ + # Create Hands + ################################ + + if db_server == 'mysql': + self.query['createBoardsTable'] = """CREATE TABLE Boards ( + id BIGINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id), + handId BIGINT UNSIGNED NOT NULL, FOREIGN KEY (handId) REFERENCES Hands(id), + boardId smallint, + boardcard1 smallint, /* 0=none, 1-13=2-Ah 14-26=2-Ad 27-39=2-Ac 40-52=2-As */ + boardcard2 smallint, + boardcard3 smallint, + boardcard4 smallint, + boardcard5 smallint) + ENGINE=INNODB""" + elif db_server == 'postgresql': + self.query['createBoardsTable'] = """CREATE TABLE Boards ( + id BIGSERIAL, PRIMARY KEY (id), + handId BIGINT NOT NULL, FOREIGN KEY (handId) REFERENCES Hands(id), + boardId smallint, + boardcard1 smallint, /* 0=none, 1-13=2-Ah 14-26=2-Ad 27-39=2-Ac 40-52=2-As */ + boardcard2 smallint, + boardcard3 smallint, + boardcard4 smallint, + boardcard5 smallint)""" + elif db_server == 'sqlite': + self.query['createBoardsTable'] = """CREATE TABLE Boards ( + id INTEGER PRIMARY KEY, + handId INT NOT NULL, + boardId INT, + boardcard1 INT, /* 0=none, 1-13=2-Ah 14-26=2-Ad 27-39=2-Ac 40-52=2-As */ + boardcard2 INT, + boardcard3 INT, + boardcard4 INT, + boardcard5 INT)""" ################################ @@ -4865,6 +4904,7 @@ class Sql: boardcard3, boardcard4, boardcard5, + runIt, playersAtStreet1, playersAtStreet2, playersAtStreet3, @@ -4884,7 +4924,8 @@ class Sql: values (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, - %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""" + %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, + %s)""" self.query['store_hands_players'] = """insert into HandsPlayers ( @@ -5052,6 +5093,20 @@ class Sql: %s, %s, %s, %s, %s, %s, %s )""" + + self.query['store_boards'] = """insert into Boards ( + handId, + boardId, + boardcard1, + boardcard2, + boardcard3, + boardcard4, + boardcard5 + ) + values ( + %s, %s, %s, %s, %s, + %s, %s + )""" ################################ # queries for Files Table diff --git a/pyfpdb/TestHandsPlayers.py b/pyfpdb/TestHandsPlayers.py index 72f99bfe..b94344de 100755 --- a/pyfpdb/TestHandsPlayers.py +++ b/pyfpdb/TestHandsPlayers.py @@ -152,6 +152,7 @@ def compare_hands_file(filename, importer, errors): # Delete unused data from hash del ghash['gsc'] del ghash['sc'] + del ghash['boards'] for datum in ghash: #print "DEBUG: hand: '%s'" % datum try: @@ -164,7 +165,8 @@ def compare_hands_file(filename, importer, errors): or datum == 'sessionId' or datum == 'tourneyId' or datum == 'gameSessionId' - or datum == 'fileId'): + or datum == 'fileId' + or datum == 'runIt'): # Not an error. gametypeIds are dependent on the order added to the db. #print "DEBUG: Skipping mismatched gamtypeId" pass