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.

This commit is contained in:
Chaz Littlejohn 2011-04-04 04:50:01 +00:00
parent f55cd5569d
commit d98a28ea0a
6 changed files with 108 additions and 13 deletions

View File

@ -73,7 +73,7 @@ except ImportError:
use_numpy = False use_numpy = False
DB_VERSION = 153 DB_VERSION = 154
# Variance created as sqlite has a bunch of undefined aggregate functions. # 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':'handId', 'drop':1}
, {'tab':'HandsActions', 'col':'playerId', 'drop':1} , {'tab':'HandsActions', 'col':'playerId', 'drop':1}
, {'tab':'HandsActions', 'col':'actionId', 'drop':1} , {'tab':'HandsActions', 'col':'actionId', 'drop':1}
, {'tab':'Boards', 'col':'handId', 'drop':1}
, {'tab':'HandsPlayers', 'col':'handId', 'drop':1} , {'tab':'HandsPlayers', 'col':'handId', 'drop':1}
, {'tab':'HandsPlayers', 'col':'playerId', 'drop':1} , {'tab':'HandsPlayers', 'col':'playerId', 'drop':1}
, {'tab':'HandsPlayers', 'col':'tourneysPlayersId', 'drop':0} , {'tab':'HandsPlayers', 'col':'tourneysPlayersId', 'drop':0}
@ -153,6 +154,7 @@ class Database:
, [ # indexes for sqlite (list index 4) , [ # indexes for sqlite (list index 4)
{'tab':'Hands', 'col':'gametypeId', 'drop':0} {'tab':'Hands', 'col':'gametypeId', 'drop':0}
, {'tab':'Hands', 'col':'fileId', 'drop':0} , {'tab':'Hands', 'col':'fileId', 'drop':0}
, {'tab':'Boards', 'col':'handId', 'drop':0}
, {'tab':'HandsPlayers', 'col':'handId', 'drop':0} , {'tab':'HandsPlayers', 'col':'handId', 'drop':0}
, {'tab':'HandsPlayers', 'col':'playerId', 'drop':0} , {'tab':'HandsPlayers', 'col':'playerId', 'drop':0}
, {'tab':'HandsPlayers', 'col':'tourneysPlayersId', 'drop':0} , {'tab':'HandsPlayers', 'col':'tourneysPlayersId', 'drop':0}
@ -182,6 +184,7 @@ class Database:
, [ # foreign keys for mysql (index 2) , [ # foreign keys for mysql (index 2)
{'fktab':'Hands', 'fkcol':'gametypeId', 'rtab':'Gametypes', 'rcol':'id', 'drop':1} {'fktab':'Hands', 'fkcol':'gametypeId', 'rtab':'Gametypes', 'rcol':'id', 'drop':1}
, {'fktab':'Hands', 'fkcol':'fileId', 'rtab':'Files', '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':'handId', 'rtab':'Hands', 'rcol':'id', 'drop':1}
, {'fktab':'HandsPlayers', 'fkcol':'playerId', 'rtab':'Players', 'rcol':'id', 'drop':1} , {'fktab':'HandsPlayers', 'fkcol':'playerId', 'rtab':'Players', 'rcol':'id', 'drop':1}
, {'fktab':'HandsPlayers', 'fkcol':'tourneysPlayersId','rtab':'TourneysPlayers','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) , [ # foreign keys for postgres (index 3)
{'fktab':'Hands', 'fkcol':'gametypeId', 'rtab':'Gametypes', 'rcol':'id', 'drop':1} {'fktab':'Hands', 'fkcol':'gametypeId', 'rtab':'Gametypes', 'rcol':'id', 'drop':1}
, {'fktab':'Hands', 'fkcol':'fileId', 'rtab':'Files', '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':'handId', 'rtab':'Hands', 'rcol':'id', 'drop':1}
, {'fktab':'HandsPlayers', 'fkcol':'playerId', 'rtab':'Players', 'rcol':'id', 'drop':1} , {'fktab':'HandsPlayers', 'fkcol':'playerId', 'rtab':'Players', 'rcol':'id', 'drop':1}
, {'fktab':'HandsActions', 'fkcol':'handId', 'rtab':'Hands', '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.execute(self.sql.query['list_tables'])
tables=self.cursor.fetchall() 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 print "table:", table
result+="###################\nTable "+table+"\n###################\n" result+="###################\nTable "+table+"\n###################\n"
rows=self.cursor.execute(self.sql.query['get'+table]) 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['createPlayersTable'])
c.execute(self.sql.query['createAutoratesTable']) c.execute(self.sql.query['createAutoratesTable'])
c.execute(self.sql.query['createHandsTable']) c.execute(self.sql.query['createHandsTable'])
c.execute(self.sql.query['createBoardsTable'])
c.execute(self.sql.query['createTourneyTypesTable']) c.execute(self.sql.query['createTourneyTypesTable'])
c.execute(self.sql.query['createTourneysTable']) c.execute(self.sql.query['createTourneysTable'])
c.execute(self.sql.query['createTourneysPlayersTable']) c.execute(self.sql.query['createTourneysPlayersTable'])
@ -1875,6 +1880,7 @@ class Database:
hdata['boardcard3'], hdata['boardcard3'],
hdata['boardcard4'], hdata['boardcard4'],
hdata['boardcard5'], hdata['boardcard5'],
hdata['runIt'],
hdata['playersAtStreet1'], hdata['playersAtStreet1'],
hdata['playersAtStreet2'], hdata['playersAtStreet2'],
hdata['playersAtStreet3'], hdata['playersAtStreet3'],
@ -1890,19 +1896,28 @@ class Database:
hdata['street3Pot'], hdata['street3Pot'],
hdata['street4Pot'], hdata['street4Pot'],
hdata['showdownPot'], hdata['showdownPot'],
hdata['boards'],
hdata['id'] hdata['id']
]) ])
if doinsert: if doinsert:
bbulk = []
for h in hbulk: for h in hbulk:
id = h.pop() id = h.pop()
if hdata['sc'] and hdata['gsc']: if hdata['sc'] and hdata['gsc']:
h[4] = hdata['sc'][id]['id'] h[4] = hdata['sc'][id]['id']
h[5] = hdata['gsc'][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 = self.sql.query['store_hand']
q = q.replace('%s', self.sql.query['placeholder']) q = q.replace('%s', self.sql.query['placeholder'])
c = self.get_cursor() c = self.get_cursor()
c.executemany(q, hbulk) 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() self.commit()
return hbulk return hbulk

View File

@ -143,6 +143,20 @@ class DerivedStats():
self.hands['boardcard3'] = cards[2] self.hands['boardcard3'] = cards[2]
self.hands['boardcard4'] = cards[3] self.hands['boardcard4'] = cards[3]
self.hands['boardcard5'] = cards[4] 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() #print "DEBUG: self.getStreetTotals = (%s, %s, %s, %s, %s)" % hand.getStreetTotals()
totals = hand.getStreetTotals() totals = hand.getStreetTotals()

View File

@ -381,10 +381,16 @@ class Fulltilt(HandHistoryConverter):
def markStreets(self, hand): def markStreets(self, hand):
if hand.gametype['base'] == 'hold': if hand.gametype['base'] == 'hold':
m = re.search(r"\*\*\* HOLE CARDS \*\*\*(?P<PREFLOP>.+(?=\*\*\* FLOP \*\*\*)|.+)" m = re.search(r"\*\*\* HOLE CARDS \*\*\*(?P<PREFLOP>.+(?=\*\*\* FLOP (1\s)?\*\*\*)|.+)"
r"(\*\*\* FLOP \*\*\*(?P<FLOP> \[\S\S \S\S \S\S\].+(?=\*\*\* TURN \*\*\*)|.+))?" r"(\*\*\* FLOP \*\*\*(?P<FLOP> \[\S\S \S\S \S\S\].+(?=\*\*\* TURN (1\s)?\*\*\*)|.+))?"
r"(\*\*\* TURN \*\*\* \[\S\S \S\S \S\S] (?P<TURN>\[\S\S\].+(?=\*\*\* RIVER \*\*\*)|.+))?" r"(\*\*\* TURN \*\*\* \[\S\S \S\S \S\S] (?P<TURN>\[\S\S\].+(?=\*\*\* RIVER (1\s)?\*\*\*)|.+))?"
r"(\*\*\* RIVER \*\*\* \[\S\S \S\S \S\S \S\S] (?P<RIVER>\[\S\S\].+))?", hand.handText,re.DOTALL) r"(\*\*\* RIVER \*\*\* \[\S\S \S\S \S\S \S\S] (?P<RIVER>\[\S\S\].+))?"
r"(\*\*\* FLOP 1 \*\*\*(?P<FLOP1> \[\S\S \S\S \S\S\].+(?=\*\*\* TURN 1 \*\*\*)|.+))?"
r"(\*\*\* TURN 1 \*\*\* \[\S\S \S\S \S\S] (?P<TURN1>\[\S\S\].+(?=\*\*\* RIVER 1 \*\*\*)|.+))?"
r"(\*\*\* RIVER 1 \*\*\* \[\S\S \S\S \S\S \S\S] (?P<RIVER1>\[\S\S\].))?"
r"(\*\*\* FLOP 2 \*\*\*(?P<FLOP2> \[\S\S \S\S \S\S\].+(?=\*\*\* TURN 2 \*\*\*)|.+))?"
r"(\*\*\* TURN 2 \*\*\* \[\S\S \S\S \S\S] (?P<TURN2>\[\S\S\].+(?=\*\*\* RIVER 2 \*\*\*)|.+))?"
r"(\*\*\* RIVER 2 \*\*\* \[\S\S \S\S \S\S \S\S] (?P<RIVER2>\[\S\S\].+))?", hand.handText,re.DOTALL)
elif hand.gametype['base'] == "stud": elif hand.gametype['base'] == "stud":
m = re.search(r"(?P<ANTES>.+(?=\*\*\* 3RD STREET \*\*\*)|.+)" m = re.search(r"(?P<ANTES>.+(?=\*\*\* 3RD STREET \*\*\*)|.+)"
r"(\*\*\* 3RD STREET \*\*\*(?P<THIRD>.+(?=\*\*\* 4TH STREET \*\*\*)|.+))?" r"(\*\*\* 3RD STREET \*\*\*(?P<THIRD>.+(?=\*\*\* 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 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) 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]) m = self.re_Board.search(hand.streets[street])
hand.setCommunityCards(street, m.group('CARDS').split(' ')) 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): def readBlinds(self, hand):
try: try:

View File

@ -77,6 +77,7 @@ class Hand(object):
self.maxseats = None self.maxseats = None
self.counted_seats = 0 self.counted_seats = 0
self.buttonpos = 0 self.buttonpos = 0
self.runItTimes = 0
#tourney stuff #tourney stuff
self.tourNo = None self.tourNo = None
@ -887,9 +888,8 @@ class HoldemOmahaHand(Hand):
hhc.readHeroCards(self) hhc.readHeroCards(self)
hhc.readShowdownActions(self) hhc.readShowdownActions(self)
# Read actions in street order # Read actions in street order
for street in self.communityStreets: for street, text in self.streets.iteritems():
if self.streets[street]: if text: hhc.readCommunityCards(self, street)
hhc.readCommunityCards(self, street)
for street in self.actionStreets: for street in self.actionStreets:
if self.streets[street]: if self.streets[street]:
hhc.readAction(self, street) hhc.readAction(self, street)

View File

@ -372,6 +372,7 @@ class Sql:
boardcard4 smallint, boardcard4 smallint,
boardcard5 smallint, boardcard5 smallint,
texture smallint, texture smallint,
runIt BOOLEAN,
playersVpi SMALLINT NOT NULL, /* num of players vpi */ playersVpi SMALLINT NOT NULL, /* num of players vpi */
playersAtStreet1 SMALLINT NOT NULL, /* num of players seeing flop/street4 */ playersAtStreet1 SMALLINT NOT NULL, /* num of players seeing flop/street4 */
playersAtStreet2 SMALLINT NOT NULL, playersAtStreet2 SMALLINT NOT NULL,
@ -412,6 +413,7 @@ class Sql:
boardcard4 smallint, boardcard4 smallint,
boardcard5 smallint, boardcard5 smallint,
texture smallint, texture smallint,
runIt BOOLEAN,
playersVpi SMALLINT NOT NULL, /* num of players vpi */ playersVpi SMALLINT NOT NULL, /* num of players vpi */
playersAtStreet1 SMALLINT NOT NULL, /* num of players seeing flop/street4 */ playersAtStreet1 SMALLINT NOT NULL, /* num of players seeing flop/street4 */
playersAtStreet2 SMALLINT NOT NULL, playersAtStreet2 SMALLINT NOT NULL,
@ -451,6 +453,7 @@ class Sql:
boardcard4 INT, boardcard4 INT,
boardcard5 INT, boardcard5 INT,
texture INT, texture INT,
runIt BOOLEAN,
playersVpi INT NOT NULL, /* num of players vpi */ playersVpi INT NOT NULL, /* num of players vpi */
playersAtStreet1 INT NOT NULL, /* num of players seeing flop/street4 */ playersAtStreet1 INT NOT NULL, /* num of players seeing flop/street4 */
playersAtStreet2 INT NOT NULL, playersAtStreet2 INT NOT NULL,
@ -469,6 +472,42 @@ class Sql:
showdownPot INT, /* pot size at sd/street7 */ showdownPot INT, /* pot size at sd/street7 */
comment TEXT, comment TEXT,
commentTs REAL)""" 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, boardcard3,
boardcard4, boardcard4,
boardcard5, boardcard5,
runIt,
playersAtStreet1, playersAtStreet1,
playersAtStreet2, playersAtStreet2,
playersAtStreet3, playersAtStreet3,
@ -4884,7 +4924,8 @@ class Sql:
values 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, %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 ( self.query['store_hands_players'] = """insert into HandsPlayers (
@ -5052,6 +5093,20 @@ class Sql:
%s, %s, %s, %s, %s, %s, %s, %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 # queries for Files Table

View File

@ -152,6 +152,7 @@ def compare_hands_file(filename, importer, errors):
# Delete unused data from hash # Delete unused data from hash
del ghash['gsc'] del ghash['gsc']
del ghash['sc'] del ghash['sc']
del ghash['boards']
for datum in ghash: for datum in ghash:
#print "DEBUG: hand: '%s'" % datum #print "DEBUG: hand: '%s'" % datum
try: try:
@ -164,7 +165,8 @@ def compare_hands_file(filename, importer, errors):
or datum == 'sessionId' or datum == 'sessionId'
or datum == 'tourneyId' or datum == 'tourneyId'
or datum == 'gameSessionId' 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. # Not an error. gametypeIds are dependent on the order added to the db.
#print "DEBUG: Skipping mismatched gamtypeId" #print "DEBUG: Skipping mismatched gamtypeId"
pass pass