Merge branch 'master' of git://git.assembla.com/fpdboz

This commit is contained in:
eblade 2008-12-12 09:56:13 -05:00
commit 0dbcf0079c
14 changed files with 710 additions and 502 deletions

View File

@ -152,6 +152,15 @@ class Database:
action[street].append(act) action[street].append(act)
return action return action
def get_winners_from_hand(self, hand):
"""Returns a hash of winners:amount won, given a hand number."""
winners = {}
c = self.connection.cursor()
c.execute(self.sql.query['get_winners_from_hand'], (hand))
for row in c.fetchall():
winners[row[0]] = row[1]
return winners
def get_stats_from_hand(self, hand, aggregate = False): def get_stats_from_hand(self, hand, aggregate = False):
c = self.connection.cursor() c = self.connection.cursor()

View File

@ -68,17 +68,18 @@ class Everleaf(HandHistoryConverter):
print "Initialising Everleaf converter class" print "Initialising Everleaf converter class"
HandHistoryConverter.__init__(self, config, file, sitename="Everleaf") # Call super class init. HandHistoryConverter.__init__(self, config, file, sitename="Everleaf") # Call super class init.
self.sitename = "Everleaf" self.sitename = "Everleaf"
self.setFileType("text") self.setFileType("text", "cp1252")
self.rexx.setGameInfoRegex('.*Blinds \$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+)') self.rexx.setGameInfoRegex('.*Blinds \$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+)')
self.rexx.setSplitHandRegex('\n\n\n\n') self.rexx.setSplitHandRegex('\n\n+')
self.rexx.setHandInfoRegex('.*#(?P<HID>[0-9]+)\n.*\nBlinds \$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+) (?P<GAMETYPE>.*) - (?P<YEAR>[0-9]+)/(?P<MON>[0-9]+)/(?P<DAY>[0-9]+) - (?P<HR>[0-9]+):(?P<MIN>[0-9]+):(?P<SEC>[0-9]+)\nTable (?P<TABLE>[ a-zA-Z]+)\nSeat (?P<BUTTON>[0-9]+)') self.rexx.setHandInfoRegex('.*#(?P<HID>[0-9]+)\n.*\nBlinds \$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+) (?P<GAMETYPE>.*) - (?P<YEAR>[0-9]+)/(?P<MON>[0-9]+)/(?P<DAY>[0-9]+) - (?P<HR>[0-9]+):(?P<MIN>[0-9]+):(?P<SEC>[0-9]+)\nTable (?P<TABLE>[ a-zA-Z]+)\nSeat (?P<BUTTON>[0-9]+)')
self.rexx.setPlayerInfoRegex('Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*) \( \$ (?P<CASH>[.0-9]+) USD \)') self.rexx.setPlayerInfoRegex('Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*) \(\s+(\$ (?P<CASH>[.0-9]+) USD|new player|All-in) \)')
self.rexx.setPostSbRegex('.*\n(?P<PNAME>.*): posts small blind \[\$? (?P<SB>[.0-9]+)') self.rexx.setPostSbRegex('.*\n(?P<PNAME>.*): posts small blind \[\$? (?P<SB>[.0-9]+)')
self.rexx.setPostBbRegex('.*\n(?P<PNAME>.*): posts big blind \[\$? (?P<BB>[.0-9]+)') self.rexx.setPostBbRegex('.*\n(?P<PNAME>.*): posts big blind \[\$? (?P<BB>[.0-9]+)')
# mct : what about posting small & big blinds simultaneously? # mct : what about posting small & big blinds simultaneously?
self.rexx.setHeroCardsRegex('.*\nDealt\sto\s(?P<PNAME>.*)\s\[ (?P<HOLE1>\S\S), (?P<HOLE2>\S\S) \]') self.rexx.setHeroCardsRegex('.*\nDealt\sto\s(?P<PNAME>.*)\s\[ (?P<HOLE1>\S\S), (?P<HOLE2>\S\S) \]')
self.rexx.setActionStepRegex('.*\n(?P<PNAME>.*) (?P<ATYPE>bets|checks|raises|calls|folds)(\s\[\$ (?P<BET>[.\d]+) USD\])?') self.rexx.setActionStepRegex('.*\n(?P<PNAME>.*)(?P<ATYPE>: bets| checks| raises| calls| folds)(\s\[\$ (?P<BET>[.\d]+) USD\])?')
self.rexx.setShowdownActionRegex('.*\n(?P<PNAME>.*) shows \[ (?P<CARDS>.*) \]') self.rexx.setShowdownActionRegex('.*\n(?P<PNAME>.*) shows \[ (?P<CARDS>.*) \]')
self.rexx.setCollectPotRegex('.*\n(?P<PNAME>.*) wins \$ (?P<POT>[.\d]+) USD(.*\[ (?P<HAND>.*) \])?')
self.rexx.compileRegexes() self.rexx.compileRegexes()
def readSupportedGames(self): def readSupportedGames(self):
@ -117,20 +118,23 @@ class Everleaf(HandHistoryConverter):
def readPlayerStacks(self, hand): def readPlayerStacks(self, hand):
m = self.rexx.player_info_re.finditer(hand.string) m = self.rexx.player_info_re.finditer(hand.string)
players = [] players = []
for a in m: for a in m:
hand.addPlayer(int(a.group('SEAT')), a.group('PNAME'), a.group('CASH')) hand.addPlayer(int(a.group('SEAT')), a.group('PNAME'), a.group('CASH'))
def markStreets(self, hand): def markStreets(self, hand):
# PREFLOP = ** Dealing down cards ** # PREFLOP = ** Dealing down cards **
m = re.search('(\*\* Dealing down cards \*\*\n)(?P<PREFLOP>.*?\n\*\*)?( Dealing Flop \*\* \[ (?P<FLOP1>\S\S), (?P<FLOP2>\S\S), (?P<FLOP3>\S\S) \])?(?P<FLOP>.*?\*\*)?( Dealing Turn \*\* \[ (?P<TURN1>\S\S) \])?(?P<TURN>.*?\*\*)?( Dealing River \*\* \[ (?P<RIVER1>\S\S) \])?(?P<RIVER>.*)', hand.string,re.DOTALL) # This re fails if, say, river is missing; then we don't get the ** that starts the river.
# for street in m.groupdict(): #m = re.search('(\*\* Dealing down cards \*\*\n)(?P<PREFLOP>.*?\n\*\*)?( Dealing Flop \*\* \[ (?P<FLOP1>\S\S), (?P<FLOP2>\S\S), (?P<FLOP3>\S\S) \])?(?P<FLOP>.*?\*\*)?( Dealing Turn \*\* \[ (?P<TURN1>\S\S) \])?(?P<TURN>.*?\*\*)?( Dealing River \*\* \[ (?P<RIVER1>\S\S) \])?(?P<RIVER>.*)', hand.string,re.DOTALL)
# print "DEBUG: Street: %s\tspan: %s" %(street, str(m.span(street)))
m = re.search(r"\*\* Dealing down cards \*\*(?P<PREFLOP>.+(?=\*\* Dealing Flop \*\*)|.+)"
r"(\*\* Dealing Flop \*\* \[ \S\S, \S\S, \S\S \](?P<FLOP>.+(?=\*\* Dealing Turn \*\*)|.+))?"
r"(\*\* Dealing Turn \*\* \[ \S\S \](?P<TURN>.+(?=\*\* Dealing River \*\*)|.+))?"
r"(\*\* Dealing River \*\* \[ \S\S \](?P<RIVER>.+))?", hand.string,re.DOTALL)
hand.streets = m hand.streets = m
def readCommunityCards(self, hand): def readCommunityCards(self, hand):
# currently regex in wrong place pls fix my brain's fried # currently regex in wrong place pls fix my brain's fried
# what a mess!
re_board = re.compile('\*\* Dealing (?P<STREET>.*) \*\* \[ (?P<CARDS>.*) \]') re_board = re.compile('\*\* Dealing (?P<STREET>.*) \*\* \[ (?P<CARDS>.*) \]')
m = re_board.finditer(hand.string) m = re_board.finditer(hand.string)
for street in m: for street in m:
@ -165,15 +169,19 @@ class Everleaf(HandHistoryConverter):
m = self.rexx.action_re.finditer(hand.streets.group(street)) m = self.rexx.action_re.finditer(hand.streets.group(street))
hand.actions[street] = [] hand.actions[street] = []
for action in m: for action in m:
if action.group('ATYPE') == 'raises': if action.group('ATYPE') == ' raises':
hand.addRaiseTo( street, action.group('PNAME'), action.group('BET') ) hand.addRaiseTo( street, action.group('PNAME'), action.group('BET') )
elif action.group('ATYPE') == 'calls': elif action.group('ATYPE') == ' calls':
hand.addCall( street, action.group('PNAME'), action.group('BET') ) hand.addCall( street, action.group('PNAME'), action.group('BET') )
elif action.group('ATYPE') == 'bets': elif action.group('ATYPE') == ': bets':
hand.addBet( street, action.group('PNAME'), action.group('BET') ) hand.addBet( street, action.group('PNAME'), action.group('BET') )
elif action.group('ATYPE') == ' folds':
hand.addFold( street, action.group('PNAME'))
elif action.group('ATYPE') == ' checks':
hand.addCheck( street, action.group('PNAME'))
else: else:
#print "DEBUG: unimplemented readAction: %s %s" %(action.group('PNAME'),action.group('ATYPE'),) print "DEBUG: unimplemented readAction: %s %s" %(action.group('PNAME'),action.group('ATYPE'),)
hand.actions[street] += [[action.group('PNAME'), action.group('ATYPE')]] #hand.actions[street] += [[action.group('PNAME'), action.group('ATYPE')]]
def readShowdownActions(self, hand): def readShowdownActions(self, hand):
@ -182,15 +190,25 @@ class Everleaf(HandHistoryConverter):
re_card = re.compile('(?P<CARD>[0-9tjqka][schd])') # copied from earlier re_card = re.compile('(?P<CARD>[0-9tjqka][schd])') # copied from earlier
cards = [card.group('CARD') for card in re_card.finditer(shows.group('CARDS'))] cards = [card.group('CARD') for card in re_card.finditer(shows.group('CARDS'))]
print cards print cards
hand.addHoleCards(cards, shows.group('PNAME')) hand.addShownCards(cards, shows.group('PNAME'))
def readCollectPot(self,hand):
m = self.rexx.collect_pot_re.search(hand.string)
if m is not None:
if m.group('HAND') is not None:
re_card = re.compile('(?P<CARD>[0-9tjqka][schd])') # copied from earlier
cards = set([hand.card(card.group('CARD')) for card in re_card.finditer(m.group('HAND'))])
hand.addShownCards(cards=None, player=m.group('PNAME'), holeandboard=cards)
hand.addCollectPot(player=m.group('PNAME'),pot=m.group('POT'))
else:
print "WARNING: Unusual, no one collected; can happen if it's folded to big blind with a dead small blind."
def getRake(self, hand): def getRake(self, hand):
hand.rake = hand.totalpot * Decimal('0.05') # probably not quite right hand.rake = hand.totalpot * Decimal('0.05') # probably not quite right
if __name__ == "__main__": if __name__ == "__main__":
c = Configuration.Config() c = Configuration.Config()
e = Everleaf(c, "Speed_Kuala.txt") e = Everleaf(c, "regression-test-files/everleaf/Speed_Kuala_full.txt")
e.processFile() e.processFile()
print str(e) print str(e)

View File

@ -73,13 +73,13 @@ class FpdbSQLQueries:
if(self.dbname == 'MySQL InnoDB'): if(self.dbname == 'MySQL InnoDB'):
self.query['createSitesTable'] = """CREATE TABLE Sites ( self.query['createSitesTable'] = """CREATE TABLE Sites (
id SMALLINT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id), id SMALLINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
name varchar(32) NOT NULL, name varchar(32) NOT NULL,
currency char(3) NOT NULL) currency char(3) NOT NULL)
ENGINE=INNODB""" ENGINE=INNODB"""
elif(self.dbname == 'PostgreSQL'): elif(self.dbname == 'PostgreSQL'):
self.query['createSitesTable'] = """CREATE TABLE Sites ( self.query['createSitesTable'] = """CREATE TABLE Sites (
id SERIAL UNIQUE, PRIMARY KEY (id), id SERIAL, PRIMARY KEY (id),
name varchar(32), name varchar(32),
currency char(3))""" currency char(3))"""
elif(self.dbname == 'SQLite'): elif(self.dbname == 'SQLite'):
@ -92,7 +92,7 @@ class FpdbSQLQueries:
if(self.dbname == 'MySQL InnoDB'): if(self.dbname == 'MySQL InnoDB'):
self.query['createGametypesTable'] = """CREATE TABLE Gametypes ( self.query['createGametypesTable'] = """CREATE TABLE Gametypes (
id SMALLINT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id), id SMALLINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
siteId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (siteId) REFERENCES Sites(id), siteId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (siteId) REFERENCES Sites(id),
type char(4) NOT NULL, type char(4) NOT NULL,
base char(4) NOT NULL, base char(4) NOT NULL,
@ -106,7 +106,7 @@ class FpdbSQLQueries:
ENGINE=INNODB""" ENGINE=INNODB"""
elif(self.dbname == 'PostgreSQL'): elif(self.dbname == 'PostgreSQL'):
self.query['createGametypesTable'] = """CREATE TABLE Gametypes ( self.query['createGametypesTable'] = """CREATE TABLE Gametypes (
id SERIAL UNIQUE, PRIMARY KEY (id), id SERIAL, PRIMARY KEY (id),
siteId INTEGER, FOREIGN KEY (siteId) REFERENCES Sites(id), siteId INTEGER, FOREIGN KEY (siteId) REFERENCES Sites(id),
type char(4), type char(4),
base char(4), base char(4),
@ -127,7 +127,7 @@ class FpdbSQLQueries:
if(self.dbname == 'MySQL InnoDB'): if(self.dbname == 'MySQL InnoDB'):
self.query['createPlayersTable'] = """CREATE TABLE Players ( self.query['createPlayersTable'] = """CREATE TABLE Players (
id INT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id), id INT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
name VARCHAR(32) CHARACTER SET utf8 NOT NULL, name VARCHAR(32) CHARACTER SET utf8 NOT NULL,
siteId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (siteId) REFERENCES Sites(id), siteId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (siteId) REFERENCES Sites(id),
comment text, comment text,
@ -135,7 +135,7 @@ class FpdbSQLQueries:
ENGINE=INNODB""" ENGINE=INNODB"""
elif(self.dbname == 'PostgreSQL'): elif(self.dbname == 'PostgreSQL'):
self.query['createPlayersTable'] = """CREATE TABLE Players ( self.query['createPlayersTable'] = """CREATE TABLE Players (
id SERIAL UNIQUE, PRIMARY KEY (id), id SERIAL, PRIMARY KEY (id),
name VARCHAR(32), name VARCHAR(32),
siteId INTEGER, FOREIGN KEY (siteId) REFERENCES Sites(id), siteId INTEGER, FOREIGN KEY (siteId) REFERENCES Sites(id),
comment text, comment text,
@ -150,7 +150,7 @@ class FpdbSQLQueries:
if(self.dbname == 'MySQL InnoDB'): if(self.dbname == 'MySQL InnoDB'):
self.query['createAutoratesTable'] = """CREATE TABLE Autorates ( self.query['createAutoratesTable'] = """CREATE TABLE Autorates (
id BIGINT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id), id BIGINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
playerId INT UNSIGNED NOT NULL, FOREIGN KEY (playerId) REFERENCES Players(id), playerId INT UNSIGNED NOT NULL, FOREIGN KEY (playerId) REFERENCES Players(id),
gametypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id), gametypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
description varchar(50) NOT NULL, description varchar(50) NOT NULL,
@ -160,7 +160,7 @@ class FpdbSQLQueries:
ENGINE=INNODB""" ENGINE=INNODB"""
elif(self.dbname == 'PostgreSQL'): elif(self.dbname == 'PostgreSQL'):
self.query['createAutoratesTable'] = """CREATE TABLE Autorates ( self.query['createAutoratesTable'] = """CREATE TABLE Autorates (
id BIGSERIAL UNIQUE, PRIMARY KEY (id), id BIGSERIAL, PRIMARY KEY (id),
playerId INT, FOREIGN KEY (playerId) REFERENCES Players(id), playerId INT, FOREIGN KEY (playerId) REFERENCES Players(id),
gametypeId INT, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id), gametypeId INT, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
description varchar(50), description varchar(50),
@ -177,7 +177,7 @@ class FpdbSQLQueries:
if(self.dbname == 'MySQL InnoDB'): if(self.dbname == 'MySQL InnoDB'):
self.query['createHandsTable'] = """CREATE TABLE Hands ( self.query['createHandsTable'] = """CREATE TABLE Hands (
id BIGINT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id), id BIGINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
tableName VARCHAR(20) NOT NULL, tableName VARCHAR(20) NOT NULL,
siteHandNo BIGINT NOT NULL, siteHandNo BIGINT NOT NULL,
gametypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id), gametypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
@ -190,7 +190,7 @@ class FpdbSQLQueries:
ENGINE=INNODB""" ENGINE=INNODB"""
elif(self.dbname == 'PostgreSQL'): elif(self.dbname == 'PostgreSQL'):
self.query['createHandsTable'] = """CREATE TABLE Hands ( self.query['createHandsTable'] = """CREATE TABLE Hands (
id BIGSERIAL UNIQUE, PRIMARY KEY (id), id BIGSERIAL, PRIMARY KEY (id),
tableName VARCHAR(20), tableName VARCHAR(20),
siteHandNo BIGINT, siteHandNo BIGINT,
gametypeId INT, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id), gametypeId INT, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
@ -210,7 +210,7 @@ class FpdbSQLQueries:
if(self.dbname == 'MySQL InnoDB'): if(self.dbname == 'MySQL InnoDB'):
self.query['createBoardCardsTable'] = """CREATE TABLE BoardCards ( self.query['createBoardCardsTable'] = """CREATE TABLE BoardCards (
id BIGINT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id), id BIGINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
handId BIGINT UNSIGNED NOT NULL, FOREIGN KEY (handId) REFERENCES Hands(id), handId BIGINT UNSIGNED NOT NULL, FOREIGN KEY (handId) REFERENCES Hands(id),
card1Value smallint NOT NULL, card1Value smallint NOT NULL,
card1Suit char(1) NOT NULL, card1Suit char(1) NOT NULL,
@ -225,7 +225,7 @@ class FpdbSQLQueries:
ENGINE=INNODB""" ENGINE=INNODB"""
elif(self.dbname == 'PostgreSQL'): elif(self.dbname == 'PostgreSQL'):
self.query['createBoardCardsTable'] = """CREATE TABLE BoardCards ( self.query['createBoardCardsTable'] = """CREATE TABLE BoardCards (
id BIGSERIAL UNIQUE, PRIMARY KEY (id), id BIGSERIAL, PRIMARY KEY (id),
handId BIGINT, FOREIGN KEY (handId) REFERENCES Hands(id), handId BIGINT, FOREIGN KEY (handId) REFERENCES Hands(id),
card1Value smallint, card1Value smallint,
card1Suit char(1), card1Suit char(1),
@ -247,7 +247,7 @@ class FpdbSQLQueries:
if(self.dbname == 'MySQL InnoDB'): if(self.dbname == 'MySQL InnoDB'):
self.query['createTourneyTypesTable'] = """CREATE TABLE TourneyTypes ( self.query['createTourneyTypesTable'] = """CREATE TABLE TourneyTypes (
id SMALLINT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id), id SMALLINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
siteId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (siteId) REFERENCES Sites(id), siteId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (siteId) REFERENCES Sites(id),
buyin INT NOT NULL, buyin INT NOT NULL,
fee INT NOT NULL, fee INT NOT NULL,
@ -272,7 +272,7 @@ class FpdbSQLQueries:
if(self.dbname == 'MySQL InnoDB'): if(self.dbname == 'MySQL InnoDB'):
self.query['createTourneysTable'] = """CREATE TABLE Tourneys ( self.query['createTourneysTable'] = """CREATE TABLE Tourneys (
id INT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id), id INT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
tourneyTypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (tourneyTypeId) REFERENCES TourneyTypes(id), tourneyTypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (tourneyTypeId) REFERENCES TourneyTypes(id),
siteTourneyNo BIGINT NOT NULL, siteTourneyNo BIGINT NOT NULL,
entries INT NOT NULL, entries INT NOT NULL,
@ -283,7 +283,7 @@ class FpdbSQLQueries:
ENGINE=INNODB""" ENGINE=INNODB"""
elif(self.dbname == 'PostgreSQL'): elif(self.dbname == 'PostgreSQL'):
self.query['createTourneysTable'] = """CREATE TABLE Tourneys ( self.query['createTourneysTable'] = """CREATE TABLE Tourneys (
id SERIAL UNIQUE, PRIMARY KEY (id), id SERIAL, PRIMARY KEY (id),
tourneyTypeId INT, FOREIGN KEY (tourneyTypeId) REFERENCES TourneyTypes(id), tourneyTypeId INT, FOREIGN KEY (tourneyTypeId) REFERENCES TourneyTypes(id),
siteTourneyNo BIGINT, siteTourneyNo BIGINT,
entries INT, entries INT,
@ -300,7 +300,7 @@ class FpdbSQLQueries:
if(self.dbname == 'MySQL InnoDB'): if(self.dbname == 'MySQL InnoDB'):
self.query['createHandsPlayersTable'] = """CREATE TABLE HandsPlayers ( self.query['createHandsPlayersTable'] = """CREATE TABLE HandsPlayers (
id BIGINT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id), id BIGINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
handId BIGINT UNSIGNED NOT NULL, FOREIGN KEY (handId) REFERENCES Hands(id), handId BIGINT UNSIGNED NOT NULL, FOREIGN KEY (handId) REFERENCES Hands(id),
playerId INT UNSIGNED NOT NULL, FOREIGN KEY (playerId) REFERENCES Players(id), playerId INT UNSIGNED NOT NULL, FOREIGN KEY (playerId) REFERENCES Players(id),
startCash INT NOT NULL, startCash INT NOT NULL,
@ -332,7 +332,7 @@ class FpdbSQLQueries:
ENGINE=INNODB""" ENGINE=INNODB"""
elif(self.dbname == 'PostgreSQL'): elif(self.dbname == 'PostgreSQL'):
self.query['createHandsPlayersTable'] = """CREATE TABLE HandsPlayers ( self.query['createHandsPlayersTable'] = """CREATE TABLE HandsPlayers (
id BIGSERIAL UNIQUE, PRIMARY KEY (id), id BIGSERIAL, PRIMARY KEY (id),
handId BIGINT, FOREIGN KEY (handId) REFERENCES Hands(id), handId BIGINT, FOREIGN KEY (handId) REFERENCES Hands(id),
playerId INT, FOREIGN KEY (playerId) REFERENCES Players(id), playerId INT, FOREIGN KEY (playerId) REFERENCES Players(id),
startCash INT, startCash INT,
@ -370,7 +370,7 @@ class FpdbSQLQueries:
if(self.dbname == 'MySQL InnoDB'): if(self.dbname == 'MySQL InnoDB'):
self.query['createTourneysPlayersTable'] = """CREATE TABLE TourneysPlayers ( self.query['createTourneysPlayersTable'] = """CREATE TABLE TourneysPlayers (
id BIGINT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id), id BIGINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
tourneyId INT UNSIGNED NOT NULL, FOREIGN KEY (tourneyId) REFERENCES Tourneys(id), tourneyId INT UNSIGNED NOT NULL, FOREIGN KEY (tourneyId) REFERENCES Tourneys(id),
playerId INT UNSIGNED NOT NULL, FOREIGN KEY (playerId) REFERENCES Players(id), playerId INT UNSIGNED NOT NULL, FOREIGN KEY (playerId) REFERENCES Players(id),
payinAmount INT NOT NULL, payinAmount INT NOT NULL,
@ -381,7 +381,7 @@ class FpdbSQLQueries:
ENGINE=INNODB""" ENGINE=INNODB"""
elif(self.dbname == 'PostgreSQL'): elif(self.dbname == 'PostgreSQL'):
self.query['createTourneysPlayersTable'] = """CREATE TABLE TourneysPlayers ( self.query['createTourneysPlayersTable'] = """CREATE TABLE TourneysPlayers (
id BIGSERIAL UNIQUE, PRIMARY KEY (id), id BIGSERIAL, PRIMARY KEY (id),
tourneyId INT, FOREIGN KEY (tourneyId) REFERENCES Tourneys(id), tourneyId INT, FOREIGN KEY (tourneyId) REFERENCES Tourneys(id),
playerId INT, FOREIGN KEY (playerId) REFERENCES Players(id), playerId INT, FOREIGN KEY (playerId) REFERENCES Players(id),
payinAmount INT, payinAmount INT,
@ -399,7 +399,7 @@ class FpdbSQLQueries:
if(self.dbname == 'MySQL InnoDB'): if(self.dbname == 'MySQL InnoDB'):
self.query['createHandsActionsTable'] = """CREATE TABLE HandsActions ( self.query['createHandsActionsTable'] = """CREATE TABLE HandsActions (
id BIGINT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id), id BIGINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
handPlayerId BIGINT UNSIGNED NOT NULL, FOREIGN KEY (handPlayerId) REFERENCES HandsPlayers(id), handPlayerId BIGINT UNSIGNED NOT NULL, FOREIGN KEY (handPlayerId) REFERENCES HandsPlayers(id),
street SMALLINT NOT NULL, street SMALLINT NOT NULL,
actionNo SMALLINT NOT NULL, actionNo SMALLINT NOT NULL,
@ -411,7 +411,7 @@ class FpdbSQLQueries:
ENGINE=INNODB""" ENGINE=INNODB"""
elif(self.dbname == 'PostgreSQL'): elif(self.dbname == 'PostgreSQL'):
self.query['createHandsActionsTable'] = """CREATE TABLE HandsActions ( self.query['createHandsActionsTable'] = """CREATE TABLE HandsActions (
id BIGSERIAL UNIQUE, PRIMARY KEY (id), id BIGSERIAL, PRIMARY KEY (id),
handPlayerId BIGINT, FOREIGN KEY (handPlayerId) REFERENCES HandsPlayers(id), handPlayerId BIGINT, FOREIGN KEY (handPlayerId) REFERENCES HandsPlayers(id),
street SMALLINT, street SMALLINT,
actionNo SMALLINT, actionNo SMALLINT,
@ -430,7 +430,7 @@ class FpdbSQLQueries:
if(self.dbname == 'MySQL InnoDB'): if(self.dbname == 'MySQL InnoDB'):
self.query['createHudCacheTable'] = """CREATE TABLE HudCache ( self.query['createHudCacheTable'] = """CREATE TABLE HudCache (
id BIGINT UNSIGNED UNIQUE AUTO_INCREMENT NOT NULL, PRIMARY KEY (id), id BIGINT UNSIGNED AUTO_INCREMENT NOT NULL, PRIMARY KEY (id),
gametypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id), gametypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
playerId INT UNSIGNED NOT NULL, FOREIGN KEY (playerId) REFERENCES Players(id), playerId INT UNSIGNED NOT NULL, FOREIGN KEY (playerId) REFERENCES Players(id),
activeSeats SMALLINT NOT NULL, activeSeats SMALLINT NOT NULL,
@ -503,7 +503,7 @@ class FpdbSQLQueries:
ENGINE=INNODB""" ENGINE=INNODB"""
elif(self.dbname == 'PostgreSQL'): elif(self.dbname == 'PostgreSQL'):
self.query['createHudCacheTable'] = """CREATE TABLE HudCache ( self.query['createHudCacheTable'] = """CREATE TABLE HudCache (
id BIGSERIAL UNIQUE, PRIMARY KEY (id), id BIGSERIAL, PRIMARY KEY (id),
gametypeId INT, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id), gametypeId INT, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
playerId INT, FOREIGN KEY (playerId) REFERENCES Players(id), playerId INT, FOREIGN KEY (playerId) REFERENCES Players(id),
activeSeats SMALLINT, activeSeats SMALLINT,
@ -726,7 +726,72 @@ class FpdbSQLQueries:
on hprof2.gameTypeId = stats.gameTypeId on hprof2.gameTypeId = stats.gameTypeId
order by stats.base, stats.limittype, stats.bigBlind""" order by stats.base, stats.limittype, stats.bigBlind"""
elif(self.dbname == 'PostgreSQL'): elif(self.dbname == 'PostgreSQL'):
self.query['playerStats'] = """ """ self.query['playerStats'] = """
SELECT stats.gametypeId
,stats.base
,stats.limitType
,stats.name
,(stats.bigBlind/100) as BigBlind
,stats.n
,stats.vpip
,stats.pfr
,stats.saw_f
,stats.sawsd
,stats.wtsdwsf
,stats.wmsd
,stats.FlAFq
,stats.TuAFq
,stats.RvAFq
,stats.PFAFq
,hprof2.sum_profit/100 as Net
,(hprof2.sum_profit/stats.bigBlind)/(stats.n/100) as BBlPer100
FROM
(select gt.base
,upper(gt.limitType) as limitType
,s.name
,gt.bigBlind
,hc.gametypeId
,sum(HDs) as n
,round(100*sum(street0VPI)/sum(HDs)) as vpip
,round(100*sum(street0Aggr)/sum(HDs)) as pfr
,round(100*sum(street1Seen)/sum(HDs)) AS saw_f
,round(100*sum(sawShowdown)/sum(HDs)) AS sawsd
,round(100*sum(sawShowdown)/sum(street1Seen)) AS wtsdwsf
,round(100*sum(wonAtSD)/sum(sawShowdown)) AS wmsd
,round(100*sum(street1Aggr)/sum(street1Seen)) AS FlAFq
,round(100*sum(street2Aggr)/sum(street2Seen)) AS TuAFq
,round(100*sum(street3Aggr)/sum(street3Seen)) AS RvAFq
,round(100*(sum(street1Aggr)+sum(street2Aggr)+sum(street3Aggr))
/(sum(street1Seen)+sum(street2Seen)+sum(street3Seen))) AS PFAFq
from Gametypes gt
inner join Sites s on s.Id = gt.siteId
inner join HudCache hc on hc.gameTypeId = gt.Id
where hc.playerId in <player_test>
group by gt.base
,upper(gt.limitType)
,s.name
,gt.bigBlind
,hc.gametypeId
) stats
inner join
( select hprof.gameTypeId, sum(hprof.profit) as sum_profit
from
(select hp.handId,
h.gameTypeId,
hp.winnings,
SUM(ha.amount) as costs,
hp.winnings - SUM(ha.amount) as profit
from HandsPlayers hp
inner join Hands h ON h.id = hp.handId
inner join HandsActions ha ON ha.handPlayerId = hp.id
where hp.playerId in <player_test>
and hp.tourneysPlayersId IS NULL
group by hp.handId, h.gameTypeId, hp.position, hp.winnings
) hprof
group by hprof.gameTypeId
) hprof2
on hprof2.gameTypeId = stats.gameTypeId
order by stats.base, stats.limittype, stats.bigBlind"""
elif(self.dbname == 'SQLite'): elif(self.dbname == 'SQLite'):
self.query['playerStats'] = """ """ self.query['playerStats'] = """ """

View File

@ -83,22 +83,24 @@ class GuiGraphViewer (threading.Thread):
self.ax.set_xlabel("Hands", fontsize = 12) self.ax.set_xlabel("Hands", fontsize = 12)
self.ax.set_ylabel("$", fontsize = 12) self.ax.set_ylabel("$", fontsize = 12)
self.ax.grid(color='g', linestyle=':', linewidth=0.2) self.ax.grid(color='g', linestyle=':', linewidth=0.2)
#This line will crash if no hands exist in the query. if(line == None):
text = "All Hands, " + sitename + str(name) + "\nProfit: $" + str(line[-1]) + "\nTotal Hands: " + str(len(line)) #TODO: Do something useful like alert user
print "No hands returned by graph query"
else:
text = "All Hands, " + sitename + str(name) + "\nProfit: $" + str(line[-1]) + "\nTotal Hands: " + str(len(line))
self.ax.annotate(text, self.ax.annotate(text,
xy=(10, -10), xy=(10, -10),
xycoords='axes points', xycoords='axes points',
horizontalalignment='left', verticalalignment='top', horizontalalignment='left', verticalalignment='top',
fontsize=10) fontsize=10)
#Draw plot
self.ax.plot(line,)
#Draw plot self.canvas = FigureCanvas(self.fig) # a gtk.DrawingArea
self.ax.plot(line,) self.graphBox.add(self.canvas)
self.canvas.show()
self.canvas = FigureCanvas(self.fig) # a gtk.DrawingArea
self.graphBox.add(self.canvas)
self.canvas.show()
#end of def showClicked #end of def showClicked
def getRingProfitGraph(self, name, site): def getRingProfitGraph(self, name, site):
@ -106,6 +108,9 @@ class GuiGraphViewer (threading.Thread):
#returns (HandId,Winnings,Costs,Profit) #returns (HandId,Winnings,Costs,Profit)
winnings = self.db.cursor.fetchall() winnings = self.db.cursor.fetchall()
if(winnings == ()):
return None
y=map(lambda x:float(x[3]), winnings) y=map(lambda x:float(x[3]), winnings)
line = cumsum(y) line = cumsum(y)
return line/100 return line/100

View File

@ -44,43 +44,44 @@ class GuiPlayerStats (threading.Thread):
# Get currently active site and grab playerid # Get currently active site and grab playerid
tmp = self.sql.query['playerStats'] tmp = self.sql.query['playerStats']
result = self.cursor.execute(self.sql.query['getPlayerId'], self.heroes[self.activesite]) result = self.cursor.execute(self.sql.query['getPlayerId'], (self.heroes[self.activesite],))
result = self.db.cursor.fetchall() result = self.db.cursor.fetchall()
pid = result[0][0] if not result == ():
tmp = tmp.replace("<player_test>", "(" + str(pid) + ")") pid = result[0][0]
self.cursor.execute(tmp) tmp = tmp.replace("<player_test>", "(" + str(pid) + ")")
result = self.db.cursor.fetchall() self.cursor.execute(tmp)
cols = 18 result = self.db.cursor.fetchall()
rows = len(result)+1 # +1 for title row cols = 18
self.stats_table = gtk.Table(rows, cols, False) rows = len(result)+1 # +1 for title row
self.stats_table.set_col_spacings(4) self.stats_table = gtk.Table(rows, cols, False)
self.stats_table.show() self.stats_table.set_col_spacings(4)
vbox.add(self.stats_table) self.stats_table.show()
vbox.add(self.stats_table)
# Create header row # Create header row
titles = ("GID", "base", "Style", "Site", "$BB", "Hands", "VPIP", "PFR", "saw_f", "sawsd", "wtsdwsf", "wmsd", "FlAFq", "TuAFq", "RvAFq", "PFAFq", "Net($)", "BB/100") titles = ("GID", "base", "Style", "Site", "$BB", "Hands", "VPIP", "PFR", "saw_f", "sawsd", "wtsdwsf", "wmsd", "FlAFq", "TuAFq", "RvAFq", "PFAFq", "Net($)", "BB/100")
col = 0 col = 0
row = 0 row = 0
for t in titles: for t in titles:
l = gtk.Label(titles[col]) l = gtk.Label(titles[col])
l.show() l.show()
self.stats_table.attach(l, col, col+1, row, row+1) self.stats_table.attach(l, col, col+1, row, row+1)
col +=1 col +=1
for row in range(rows-1): for row in range(rows-1):
for col in range(cols): for col in range(cols):
if(row%2 == 0): if(row%2 == 0):
bgcolor = "white" bgcolor = "white"
else: else:
bgcolor = "lightgrey" bgcolor = "lightgrey"
eb = gtk.EventBox() eb = gtk.EventBox()
eb.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(bgcolor)) eb.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(bgcolor))
l = gtk.Label(result[row-1][col]) l = gtk.Label(result[row-1][col])
eb.add(l) eb.add(l)
self.stats_table.attach(eb, col, col+1, row+1, row+2) self.stats_table.attach(eb, col, col+1, row+1, row+2)
l.show() l.show()
eb.show() eb.show()
def fillPlayerFrame(self, vbox): def fillPlayerFrame(self, vbox):

404
pyfpdb/Hand.py Normal file
View File

@ -0,0 +1,404 @@
#!/usr/bin/python
#Copyright 2008 Carl Gherardi
#This program is free software: you can redistribute it and/or modify
#it under the terms of the GNU Affero General Public License as published by
#the Free Software Foundation, version 3 of the License.
#
#This program is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
#GNU General Public License for more details.
#
#You should have received a copy of the GNU Affero General Public License
#along with this program. If not, see <http://www.gnu.org/licenses/>.
#In the "official" distribution you can find the license in
#agpl-3.0.txt in the docs folder of the package.
import Configuration
import FpdbRegex
import Hand
import re
import sys
import traceback
import os
import os.path
import xml.dom.minidom
import codecs
from decimal import Decimal
import operator
from time import time
class Hand:
# def __init__(self, sitename, gametype, sb, bb, string):
UPS = {'a':'A', 't':'T', 'j':'J', 'q':'Q', 'k':'K'}
def __init__(self, sitename, gametype, string):
self.sitename = sitename
self.gametype = gametype
self.string = string
self.streetList = ['BLINDS','PREFLOP','FLOP','TURN','RIVER'] # a list of the observed street names in order
self.handid = 0
self.sb = gametype[3]
self.bb = gametype[4]
self.tablename = "Slartibartfast"
self.hero = "Hiro"
self.maxseats = 10
self.counted_seats = 0
self.buttonpos = 0
self.seating = []
self.players = []
self.posted = []
self.involved = True
#
# Collections indexed by street names
#
# A MatchObject using a groupnames to identify streets.
# filled by markStreets()
self.streets = None
# dict from street names to lists of tuples, such as
# [['mct','bets','$10'],['mika','folds'],['carlg','raises','$20']]
# actually they're clearly lists but they probably should be tuples.
self.actions = {}
# dict from street names to community cards
self.board = {}
#
# Collections indexed by player names
#
# dict from player names to lists of hole cards
self.holecards = {}
# dict from player names to amounts collected
self.collected = {}
# Sets of players
self.shown = set()
self.folded = set()
self.action = []
self.totalpot = None
self.rake = None
self.bets = {}
self.lastBet = {}
for street in self.streetList:
self.bets[street] = {}
self.lastBet[street] = 0
def addPlayer(self, seat, name, chips):
"""\
Adds a player to the hand, and initialises data structures indexed by player.
seat (int) indicating the seat
name (string) player name
chips (string) the chips the player has at the start of the hand (can be None)
If a player has None chips he won't be added."""
if chips is not None:
self.players.append([seat, name, chips])
self.holecards[name] = []
for street in self.streetList:
self.bets[street][name] = []
def addHoleCards(self, cards, player):
"""\
Assigns observed holecards to a player.
cards list of card bigrams e.g. ['2h','jc']
player (string) name of player
hand
Note, will automatically uppercase the rank letter.
"""
try:
self.checkPlayerExists(player)
self.holecards[player] = set([self.card(c) for c in cards])
except FpdbParseError, e:
print "Tried to add holecards for unknown player: %s" % (player,)
def addShownCards(self, cards, player, holeandboard=None):
"""\
For when a player shows cards for any reason (for showdown or out of choice).
"""
if cards is not None:
self.shown.add(player)
self.addHoleCards(cards,player)
elif holeandboard is not None:
board = set([c for s in self.board.values() for c in s])
#print board
#print holeandboard
#print holeandboard.difference(board)
self.addHoleCards(holeandboard.difference(board),player)
def checkPlayerExists(self,player):
if player not in [p[1] for p in self.players]:
raise FpdbParseError
def discardHoleCards(self, cards, player):
try:
self.checkPlayerExists(player)
for card in cards:
self.holecards[player].remove(card)
except FpdbParseError, e:
pass
except ValueError:
print "tried to discard a card %s didn't have" % (player,)
def setCommunityCards(self, street, cards):
self.board[street] = [self.card(c) for c in cards]
def card(self,c):
"""upper case the ranks but not suits, 'atjqk' => 'ATJQK'"""
for k,v in self.UPS.items():
c = c.replace(k,v)
return c
def addBlind(self, player, amount):
# if player is None, it's a missing small blind.
if player is not None:
self.bets['PREFLOP'][player].append(Decimal(amount))
self.lastBet['PREFLOP'] = Decimal(amount)
self.posted += [player]
def addCall(self, street, player=None, amount=None):
# Potentially calculate the amount of the call if not supplied
# corner cases include if player would be all in
if amount is not None:
self.bets[street][player].append(Decimal(amount))
#self.lastBet[street] = Decimal(amount)
self.actions[street] += [[player, 'calls', amount]]
def addRaiseTo(self, street, player, amountTo):
"""\
Add a raise on [street] by [player] to [amountTo]
"""
#Given only the amount raised to, the amount of the raise can be calculated by
# working out how much this player has already in the pot
# (which is the sum of self.bets[street][player])
# and how much he needs to call to match the previous player
# (which is tracked by self.lastBet)
self.checkPlayerExists(player)
committedThisStreet = reduce(operator.add, self.bets[street][player], 0)
amountToCall = self.lastBet[street] - committedThisStreet
self.lastBet[street] = Decimal(amountTo)
amountBy = Decimal(amountTo) - amountToCall
self.bets[street][player].append(amountBy+amountToCall)
self.actions[street] += [[player, 'raises', amountBy, amountTo]]
def addBet(self, street, player, amount):
self.checkPlayerExists(player)
self.bets[street][player].append(Decimal(amount))
self.actions[street] += [[player, 'bets', amount]]
def addFold(self, street, player):
self.checkPlayerExists(player)
self.folded.add(player)
self.actions[street] += [[player, 'folds']]
def addCheck(self, street, player):
self.checkPlayerExists(player)
self.actions[street] += [[player, 'checks']]
def addCollectPot(self,player, pot):
self.checkPlayerExists(player)
if player not in self.collected:
self.collected[player] = pot
else:
# possibly lines like "p collected $ from pot" appear during the showdown
# but they are usually unique in the summary, so it's best to try to get them from there.
print "%s collected pot more than once; avoidable by reading winnings only from summary lines?"
def totalPot(self):
"""If all bets and blinds have been added, totals up the total pot size
Known bug: doesn't take into account side pots"""
if self.totalpot is None:
self.totalpot = 0
# player names:
# print [x[1] for x in self.players]
for player in [x[1] for x in self.players]:
for street in self.streetList:
#print street, self.bets[street][player]
self.totalpot += reduce(operator.add, self.bets[street][player], 0)
def getGameTypeAsString(self):
"""\
Map the tuple self.gametype onto the pokerstars string describing it
"""
# currently it appears to be something like ["ring", "hold", "nl", sb, bb]:
gs = {"hold" : "Hold'em",
"omahahi" : "FIXME",
"omahahilo" : "FIXME",
"razz" : "Razz",
"studhi" : "FIXME",
"studhilo" : "FIXME",
"fivedraw" : "5 Card Draw",
"27_1draw" : "FIXME",
"27_3draw" : "Triple Draw 2-7 Lowball",
"badugi" : "FIXME"
}
ls = {"nl" : "No Limit",
"pl" : "Pot Limit",
"fl" : "Limit",
"cn" : "Cap No Limit",
"cp" : "Cap Pot Limit"
}
string = "%s %s" %(gs[self.gametype[1]], ls[self.gametype[2]])
return string
def printHand(self):
# PokerStars format.
print "\n### Pseudo stars format ###"
print "%s Game #%s: %s ($%s/$%s) - %s" %(self.sitename, self.handid, self.getGameTypeAsString(), self.sb, self.bb, self.starttime)
print "Table '%s' %d-max Seat #%s is the button" %(self.tablename, self.maxseats, self.buttonpos)
for player in self.players:
print "Seat %s: %s ($%s)" %(player[0], player[1], player[2])
if(self.posted[0] is None):
print "No small blind posted"
else:
print "%s: posts small blind $%s" %(self.posted[0], self.sb)
#May be more than 1 bb posting
for a in self.posted[1:]:
print "%s: posts big blind $%s" %(self.posted[1], self.bb)
# What about big & small blinds?
print "*** HOLE CARDS ***"
if self.involved:
print "Dealt to %s [%s]" %(self.hero , " ".join(self.holecards[self.hero]))
if 'PREFLOP' in self.actions:
for act in self.actions['PREFLOP']:
self.printActionLine(act)
if 'FLOP' in self.actions:
print "*** FLOP *** [%s]" %( " ".join(self.board['Flop']))
for act in self.actions['FLOP']:
self.printActionLine(act)
if 'TURN' in self.actions:
print "*** TURN *** [%s] [%s]" %( " ".join(self.board['Flop']), " ".join(self.board['Turn']))
for act in self.actions['TURN']:
self.printActionLine(act)
if 'RIVER' in self.actions:
print "*** RIVER *** [%s] [%s]" %(" ".join(self.board['Flop']+self.board['Turn']), " ".join(self.board['River']) )
for act in self.actions['RIVER']:
self.printActionLine(act)
#Some sites don't have a showdown section so we have to figure out if there should be one
# The logic for a showdown is: at the end of river action there are at least two players in the hand
# we probably don't need a showdown section in pseudo stars format for our filtering purposes
if 'SHOWDOWN' in self.actions:
print "*** SHOW DOWN ***"
print "what do they show"
print "*** SUMMARY ***"
print "Total pot $%s | Rake $%.2f)" % (self.totalpot, self.rake) # TODO side pots
board = []
for s in self.board.values():
board += s
if board: # sometimes hand ends preflop without a board
print "Board [%s]" % (" ".join(board))
for player in self.players:
seatnum = player[0]
name = player[1]
if name in self.collected and self.holecards[name]:
print "Seat %d: %s showed [%s] and won ($%s)" % (seatnum, name, " ".join(self.holecards[name]), self.collected[name])
elif name in self.collected:
print "Seat %d: %s collected ($%s)" % (seatnum, name, self.collected[name])
elif player[1] in self.shown:
print "Seat %d: %s showed [%s]" % (seatnum, name, " ".join(self.holecards[name]))
elif player[1] in self.folded:
print "Seat %d: %s folded" % (seatnum, name)
else:
print "Seat %d: %s mucked" % (seatnum, name)
print
# TODO:
# logic for side pots
# logic for which players get to showdown
# I'm just not sure we need to do this so heavily.. and if we do, it's probably better to use pokerlib
#if self.holecards[player[1]]: # empty list default is false
#hole = self.holecards[player[1]]
##board = []
##for s in self.board.values():
##board += s
##playerhand = self.bestHand('hi', board+hole)
##print "Seat %d: %s showed %s and won/lost with %s" % (player[0], player[1], hole, playerhand)
#print "Seat %d: %s showed %s" % (player[0], player[1], hole)
#else:
#print "Seat %d: %s mucked or folded" % (player[0], player[1])
def printActionLine(self, act):
if act[1] == 'folds' or act[1] == 'checks':
print "%s: %s " %(act[0], act[1])
if act[1] == 'calls':
print "%s: %s $%s" %(act[0], act[1], act[2])
if act[1] == 'bets':
print "%s: %s $%s" %(act[0], act[1], act[2])
if act[1] == 'raises':
print "%s: %s $%s to $%s" %(act[0], act[1], act[2], act[3])
# going to use pokereval to figure out hands at some point.
# these functions are copied from pokergame.py
def bestHand(self, side, cards):
return HandHistoryConverter.eval.best('hi', cards, [])
# from pokergame.py
def bestHandValue(self, side, serial):
(value, cards) = self.bestHand(side, serial)
return value
# from pokergame.py
# got rid of the _ for internationalisation
def readableHandValueLong(self, side, value, cards):
if value == "NoPair":
if side == "low":
if cards[0][0] == '5':
return ("The wheel")
else:
return join(map(lambda card: card[0], cards), ", ")
else:
return ("High card %(card)s") % { 'card' : (letter2name[cards[0][0]]) }
elif value == "OnePair":
return ("A pair of %(card)s") % { 'card' : (letter2names[cards[0][0]]) } + (", %(card)s kicker") % { 'card' : (letter2name[cards[2][0]]) }
elif value == "TwoPair":
return ("Two pairs %(card1)s and %(card2)s") % { 'card1' : (letter2names[cards[0][0]]), 'card2' : _(letter2names[cards[2][0]]) } + (", %(card)s kicker") % { 'card' : (letter2name[cards[4][0]]) }
elif value == "Trips":
return ("Three of a kind %(card)s") % { 'card' : (letter2names[cards[0][0]]) } + (", %(card)s kicker") % { 'card' : (letter2name[cards[3][0]]) }
elif value == "Straight":
return ("Straight %(card1)s to %(card2)s") % { 'card1' : (letter2name[cards[0][0]]), 'card2' : (letter2name[cards[4][0]]) }
elif value == "Flush":
return ("Flush %(card)s high") % { 'card' : (letter2name[cards[0][0]]) }
elif value == "FlHouse":
return ("%(card1)ss full of %(card2)ss") % { 'card1' : (letter2name[cards[0][0]]), 'card2' : (letter2name[cards[3][0]]) }
elif value == "Quads":
return _("Four of a kind %(card)s") % { 'card' : (letter2names[cards[0][0]]) } + (", %(card)s kicker") % { 'card' : (letter2name[cards[4][0]]) }
elif value == "StFlush":
if letter2name[cards[0][0]] == 'Ace':
return ("Royal flush")
else:
return ("Straight flush %(card)s high") % { 'card' : (letter2name[cards[0][0]]) }
return value
class FpdbParseError(Exception): pass

View File

@ -17,26 +17,63 @@
import Configuration import Configuration
import FpdbRegex import FpdbRegex
import Hand
import re import re
import sys import sys
import traceback import traceback
import os import os
import os.path import os.path
import xml.dom.minidom import xml.dom.minidom
import codecs
from decimal import Decimal from decimal import Decimal
import operator import operator
from xml.dom.minidom import Node from xml.dom.minidom import Node
from pokereval import PokerEval from pokereval import PokerEval
from time import time
#from pokerengine.pokercards import *
# provides letter2name{}, letter2names{}, visible_card(), not_visible_card(), is_visible(), card_value(), class PokerCards
# but it's probably not installed so here are the ones we may want:
letter2name = {
'A': 'Ace',
'K': 'King',
'Q': 'Queen',
'J': 'Jack',
'T': 'Ten',
'9': 'Nine',
'8': 'Eight',
'7': 'Seven',
'6': 'Six',
'5': 'Five',
'4': 'Four',
'3': 'Trey',
'2': 'Deuce'
}
letter2names = {
'A': 'Aces',
'K': 'Kings',
'Q': 'Queens',
'J': 'Jacks',
'T': 'Tens',
'9': 'Nines',
'8': 'Eights',
'7': 'Sevens',
'6': 'Sixes',
'5': 'Fives',
'4': 'Fours',
'3': 'Treys',
'2': 'Deuces'
}
class HandHistoryConverter: class HandHistoryConverter:
eval = PokerEval() eval = PokerEval()
def __init__(self, config, file, sitename): def __init__(self, config, file, sitename):
print "HandHistory init called" print "HandHistory init called"
self.c = config self.c = config
self.sitename = sitename self.sitename = sitename
self.obs = "" # One big string self.obs = "" # One big string
self.filetype = "text" self.filetype = "text"
self.codepage = "utf8"
self.doc = None # For XML based HH files self.doc = None # For XML based HH files
self.file = file self.file = file
self.hhbase = self.c.get_import_parameters().get("hhArchiveBase") self.hhbase = self.c.get_import_parameters().get("hhArchiveBase")
@ -60,6 +97,7 @@ class HandHistoryConverter:
return tmp return tmp
def processFile(self): def processFile(self):
starttime = time()
if not self.sanityCheck(): if not self.sanityCheck():
print "Cowardly refusing to continue after failed sanity check" print "Cowardly refusing to continue after failed sanity check"
return return
@ -67,6 +105,7 @@ class HandHistoryConverter:
self.gametype = self.determineGameType() self.gametype = self.determineGameType()
self.hands = self.splitFileIntoHands() self.hands = self.splitFileIntoHands()
for hand in self.hands: for hand in self.hands:
print "\nInput:\n"+hand.string
self.readHandInfo(hand) self.readHandInfo(hand)
self.readPlayerStacks(hand) self.readPlayerStacks(hand)
self.markStreets(hand) self.markStreets(hand)
@ -76,17 +115,24 @@ class HandHistoryConverter:
self.readShowdownActions(hand) self.readShowdownActions(hand)
# Read action (Note: no guarantee this is in hand order. # Read action (Note: no guarantee this is in hand order.
for street in hand.streets.groupdict(): for street in hand.streets.groupdict():
self.readAction(hand, street) if hand.streets.group(street) is not None:
self.readAction(hand, street)
self.readCollectPot(hand)
# finalise it (total the pot) # finalise it (total the pot)
hand.totalPot() hand.totalPot()
self.getRake(hand) self.getRake(hand)
if(hand.involved == True): hand.printHand()
#if(hand.involved == True):
#self.writeHand("output file", hand) #self.writeHand("output file", hand)
hand.printHand() #hand.printHand()
else: #else:
pass #Don't write out observed hands #pass #Don't write out observed hands
endtime = time()
print "Processed %d hands in %d seconds" % (len(self.hands), endtime-starttime)
##### #####
# These functions are parse actions that may be overridden by the inheriting class # These functions are parse actions that may be overridden by the inheriting class
@ -124,6 +170,7 @@ class HandHistoryConverter:
def readBlinds(self, hand): abstract def readBlinds(self, hand): abstract
def readHeroCards(self, hand): abstract def readHeroCards(self, hand): abstract
def readAction(self, hand, street): abstract def readAction(self, hand, street): abstract
def readCollectPot(self, hand): abstract
# Some sites don't report the rake. This will be called at the end of the hand after the pot total has been calculated # Some sites don't report the rake. This will be called at the end of the hand after the pot total has been calculated
# so that an inheriting class can calculate it for the specific site if need be. # so that an inheriting class can calculate it for the specific site if need be.
@ -151,23 +198,25 @@ class HandHistoryConverter:
return sane return sane
# Functions not necessary to implement in sub class # Functions not necessary to implement in sub class
def setFileType(self, filetype = "text"): def setFileType(self, filetype = "text", codepage='utf8'):
self.filetype = filetype self.filetype = filetype
self.codepage = codepage
def splitFileIntoHands(self): def splitFileIntoHands(self):
hands = [] hands = []
self.obs.strip()
list = self.rexx.split_hand_re.split(self.obs) list = self.rexx.split_hand_re.split(self.obs)
list.pop() #Last entry is empty list.pop() #Last entry is empty
for l in list: for l in list:
# print "'" + l + "'" # print "'" + l + "'"
hands = hands + [Hand(self.sitename, self.gametype, l)] hands = hands + [Hand.Hand(self.sitename, self.gametype, l)]
return hands return hands
def readFile(self, filename): def readFile(self, filename):
"""Read file""" """Read file"""
print "Reading file: '%s'" %(filename) print "Reading file: '%s'" %(filename)
if(self.filetype == "text"): if(self.filetype == "text"):
infile=open(filename, "rU") infile=codecs.open(filename, "rU", self.codepage)
self.obs = infile.read() self.obs = infile.read()
infile.close() infile.close()
elif(self.filetype == "xml"): elif(self.filetype == "xml"):
@ -177,66 +226,6 @@ class HandHistoryConverter:
except: except:
traceback.print_exc(file=sys.stderr) traceback.print_exc(file=sys.stderr)
def writeHand(self, file, hand):
"""Write out parsed data"""
print "DEBUG: *************************"
print "DEBUG: Start of print hand"
print "DEBUG: *************************"
print "%s Game #%s: %s ($%s/$%s) - %s" %(hand.sitename, hand.handid, "XXXXhand.gametype", hand.sb, hand.bb, hand.starttime)
print "Table '%s' %d-max Seat #%s is the button" %(hand.tablename, hand.maxseats, hand.buttonpos)
for player in hand.players:
print "Seat %s: %s ($%s)" %(player[0], player[1], player[2])
if(hand.posted[0] == "FpdbNBP"):
print "No small blind posted"
else:
print "%s: posts small blind $%s" %(hand.posted[0], hand.sb)
#May be more than 1 bb posting
print "%s: posts big blind $%s" %(hand.posted[1], hand.bb)
if(len(hand.posted) > 2):
# Need to loop on all remaining big blinds - lazy
print "XXXXXXXXX FIXME XXXXXXXX"
print "*** HOLE CARDS ***"
print "Dealt to %s [%s %s]" %(hand.hero , hand.holecards[0], hand.holecards[1])
for act in hand.actions['PREFLOP']:
self.printActionLine(act, 0)
if 'PREFLOP' in hand.actions:
for act in hand.actions['PREFLOP']:
print "PF action"
if 'FLOP' in hand.actions:
print "*** FLOP *** [%s %s %s]" %(hand.streets.group("FLOP1"), hand.streets.group("FLOP2"), hand.streets.group("FLOP3"))
for act in hand.actions['FLOP']:
self.printActionLine(act, 0)
if 'TURN' in hand.actions:
print "*** TURN *** [%s %s %s] [%s]" %(hand.streets.group("FLOP1"), hand.streets.group("FLOP2"), hand.streets.group("FLOP3"), hand.streets.group("TURN1"))
for act in hand.actions['TURN']:
self.printActionLine(act, 0)
if 'RIVER' in hand.actions:
print "*** RIVER *** [%s %s %s %s] [%s]" %(hand.streets.group("FLOP1"), hand.streets.group("FLOP2"), hand.streets.group("FLOP3"), hand.streets.group("TURN1"), hand.streets.group("RIVER1"))
for act in hand.actions['RIVER']:
self.printActionLine(act, 0)
print "*** SUMMARY ***"
print "XXXXXXXXXXXX Need sumary info XXXXXXXXXXX"
def printActionLine(self, act, pot):
if act[1] == 'folds' or act[1] == 'checks':
print "%s: %s " %(act[0], act[1])
if act[1] == 'calls':
print "%s: %s $%s" %(act[0], act[1], act[2])
if act[1] == 'raises':
print "%s: %s $%s to XXXpottotalXXX" %(act[0], act[1], act[2])
#takes a poker float (including , for thousand seperator and converts it to an int #takes a poker float (including , for thousand seperator and converts it to an int
def float2int (self, string): def float2int (self, string):
@ -253,262 +242,3 @@ class HandHistoryConverter:
result*=100 result*=100
return result return result
#end def float2int #end def float2int
class Hand:
# def __init__(self, sitename, gametype, sb, bb, string):
UPS = {'a':'A', 't':'T', 'j':'J', 'q':'Q', 'k':'K'}
def __init__(self, sitename, gametype, string):
self.sitename = sitename
self.gametype = gametype
self.string = string
self.streets = None # A MatchObject using a groupnames to identify streets.
self.streetList = ['BLINDS','PREFLOP','FLOP','TURN','RIVER'] # a list of the observed street names in order
self.actions = {}
self.handid = 0
self.sb = gametype[3]
self.bb = gametype[4]
self.tablename = "Slartibartfast"
self.maxseats = 10
self.counted_seats = 0
self.buttonpos = 0
self.seating = []
self.players = []
self.posted = []
self.involved = True
self.hero = "Hiro"
self.holecards = {} # dict from player names to lists of hole cards
self.board = {} # dict from street names to community cards
self.action = []
self.totalpot = None
self.rake = None
self.bets = {}
self.lastBet = {}
for street in self.streetList:
self.bets[street] = {}
self.lastBet[street] = 0
def addPlayer(self, seat, name, chips):
"""seat, an int indicating the seat
name, the player name
chips, the chips the player has at the start of the hand"""
#self.players.append(name)
self.players.append([seat, name, chips])
self.holecards[name] = []
#self.startChips[name] = chips
#self.endChips[name] = chips
#self.winners[name] = 0
for street in self.streetList:
self.bets[street][name] = []
def addHoleCards(self, cards, player=None): # generalise to add hole cards for a specific seat or player
for c in cards:
self.holecards[player].append(self.card(c))
def discardHoleCards(self, cards, player=None):
if seat is None:
#raise something
pass
for card in cards:
try:
self.holecards[player].remove(card)
except ValueError:
print "tried to discard a card player apparently didn't have"
def setCommunityCards(self, street, cards):
self.board[street] = [self.card(c) for c in cards]
print self.board[street]
def card(self,c):
"""upper case the ranks but not suits, 'atjqk' => 'ATJQK'"""
# don't know how to make this 'static'
for k,v in self.UPS.items():
c = c.replace(k,v)
return c
def addBlind(self, player, amount):
# if player is None, it's a missing small blind.
if player is not None:
self.bets['PREFLOP'][player].append(Decimal(amount))
self.lastBet['PREFLOP'] = Decimal(amount)
self.posted += [player]
#def addFold(self, street, player=None):
## Called when a player folds.
#self.bets[street][player].append(None)
#def addCheck(self, street, player=None):
#self.bets[street][player].append(0)
def addCall(self, street, player=None, amount=None):
# Potentially calculate the amount of the call if not supplied
# corner cases include if player would be all in
if amount is not None:
self.bets[street][player].append(Decimal(amount))
#self.lastBet[street] = Decimal(amount)
self.actions[street] += [[player, 'calls', amount]]
def addRaiseTo(self, street, player, amountTo):
# Given only the amount raised to, the amount of the raise can be calculated by
# working out how much this player has already in the pot
# (which is the sum of self.bets[street][player])
# and how much he needs to call to match the previous player
# (which is tracked by self.lastBet)
committedThisStreet = reduce(operator.add, self.bets[street][player], 0)
amountToCall = self.lastBet[street] - committedThisStreet
self.lastBet[street] = Decimal(amountTo)
amountBy = Decimal(amountTo) - amountToCall
self.bets[street][player].append(amountBy+amountToCall)
self.actions[street] += [[player, 'raises', amountBy, amountTo]]
def addBet(self, street, player=None, amount=0):
self.bets[street][name].append(Decimal(amount))
self.orderedBets[street].append(Decimal(amount))
self.actions[street] += [[player, 'bets', amount]]
def totalPot(self):
"""If all bets and blinds have been added, totals up the total pot size
Known bug: doesn't take into account side pots"""
if self.totalpot is None:
self.totalpot = 0
# player names:
# print [x[1] for x in self.players]
for player in [x[1] for x in self.players]:
for street in self.streetList:
#print street, self.bets[street][player]
self.totalpot += reduce(operator.add, self.bets[street][player], 0)
def printHand(self):
# PokerStars format.
print "### DEBUG ###"
print "%s Game #%s: %s ($%s/$%s) - %s" %(self.sitename, self.handid, "XXXXhand.gametype", self.sb, self.bb, self.starttime)
print "Table '%s' %d-max Seat #%s is the button" %(self.tablename, self.maxseats, self.buttonpos)
for player in self.players:
print "Seat %s: %s ($%s)" %(player[0], player[1], player[2])
if(self.posted[0] is None):
print "No small blind posted"
else:
print "%s: posts small blind $%s" %(self.posted[0], self.sb)
#May be more than 1 bb posting
for a in self.posted[1:]:
print "%s: posts big blind $%s" %(self.posted[1], self.bb)
# What about big & small blinds?
print "*** HOLE CARDS ***"
print "Dealt to %s [%s %s]" %(self.hero , self.holecards[self.hero][0], self.holecards[self.hero][1])
if 'PREFLOP' in self.actions:
for act in self.actions['PREFLOP']:
self.printActionLine(act)
if 'FLOP' in self.actions:
print "*** FLOP *** [%s %s %s]" %(self.streets.group("FLOP1"), self.streets.group("FLOP2"), self.streets.group("FLOP3"))
for act in self.actions['FLOP']:
self.printActionLine(act)
if 'TURN' in self.actions:
print "*** TURN *** [%s %s %s] [%s]" %(self.streets.group("FLOP1"), self.streets.group("FLOP2"), self.streets.group("FLOP3"), self.streets.group("TURN1"))
for act in self.actions['TURN']:
self.printActionLine(act)
if 'RIVER' in self.actions:
print "*** RIVER *** [%s %s %s %s] [%s]" %(self.streets.group("FLOP1"), self.streets.group("FLOP2"), self.streets.group("FLOP3"), self.streets.group("TURN1"), self.streets.group("RIVER1"))
for act in self.actions['RIVER']:
self.printActionLine(act)
#Some sites don't have a showdown section so we have to figure out if there should be one
# The logic for a showdown is: at the end of river action there are at least two players in the hand
if 'SHOWDOWN' in self.actions:
print "*** SHOW DOWN ***"
print "what do they show"
print "*** SUMMARY ***"
print "Total pot $%s | Rake $%s)" % (self.totalpot, self.rake)
print "Board [%s %s %s %s %s]" % (self.streets.group("FLOP1"), self.streets.group("FLOP2"), self.streets.group("FLOP3"), self.streets.group("TURN1"), self.streets.group("RIVER1"))
#print self.board
for player in self.players:
if self.holecards[player[1]]: # empty list default is false
hole = self.holecards[player[1]]
#print self.board.values()
board = []
for s in self.board.values():
board += s
playerhand = self.bestHand('hi', board+hole)
print "Seat %d: %s showed %s and won/lost with %s" % (player[0], player[1], hole, playerhand)
else:
print "Seat %d: %s mucked or folded" % (player[0], player[1])
def printActionLine(self, act):
if act[1] == 'folds' or act[1] == 'checks':
print "%s: %s " %(act[0], act[1])
if act[1] == 'calls':
print "%s: %s $%s" %(act[0], act[1], act[2])
if act[1] == 'raises':
print "%s: %s $%s to $%s" %(act[0], act[1], act[2], act[3])
# going to use pokereval to figure out hands
# these functions are copied from pokergame.py
# im thinking perhaps its best to use all the functionality of pokergame instead
# of reinventing the wheel
def bestHand(self, side, cards):
#if self.variant == "omaha" or self.variant == "omaha8":
#hand = self.serial2player[serial].hand.tolist(True)
#board = self.board.tolist(True)
#else:
#hand = hand.tolist(True) + board.tolist(True)
#board = []
print cards
return HandHistoryConverter.eval.best('hi', cards, [])
def bestHandValue(self, side, serial):
(value, cards) = self.bestHand(side, serial)
return value
def readableHandValueLong(self, side, value, cards):
cards = self.eval.card2string(cards)
if value == "NoPair":
if side == "low":
if cards[0][0] == '5':
return _("The wheel")
else:
return join(map(lambda card: card[0], cards), ", ")
else:
return _("High card %(card)s") % { 'card' : _(letter2name[cards[0][0]]) }
elif value == "OnePair":
return _("A pair of %(card)s") % { 'card' : _(letter2names[cards[0][0]]) } + _(", %(card)s kicker") % { 'card' : _(letter2name[cards[2][0]]) }
elif value == "TwoPair":
return _("Two pairs %(card1)s and %(card2)s") % { 'card1' : _(letter2names[cards[0][0]]), 'card2' : _(letter2names[cards[2][0]]) } + _(", %(card)s kicker") % { 'card' : _(letter2name[cards[4][0]]) }
elif value == "Trips":
return _("Three of a kind %(card)s") % { 'card' : _(letter2names[cards[0][0]]) } + _(", %(card)s kicker") % { 'card' : _(letter2name[cards[3][0]]) }
elif value == "Straight":
return _("Straight %(card1)s to %(card2)s") % { 'card1' : _(letter2name[cards[0][0]]), 'card2' : _(letter2name[cards[4][0]]) }
elif value == "Flush":
return _("Flush %(card)s high") % { 'card' : _(letter2name[cards[0][0]]) }
elif value == "FlHouse":
return _("%(card1)ss full of %(card2)ss") % { 'card1' : _(letter2name[cards[0][0]]), 'card2' : _(letter2name[cards[3][0]]) }
elif value == "Quads":
return _("Four of a kind %(card)s") % { 'card' : _(letter2names[cards[0][0]]) } + _(", %(card)s kicker") % { 'card' : _(letter2name[cards[4][0]]) }
elif value == "StFlush":
if letter2name[cards[0][0]] == 'Ace':
return _("Royal flush")
else:
return _("Straight flush %(card)s high") % { 'card' : _(letter2name[cards[0][0]]) }
return value

View File

@ -55,6 +55,7 @@ class Hud:
self.db_name = db_name self.db_name = db_name
self.deleted = False self.deleted = False
self.stacked = True self.stacked = True
self.site = table.site
self.colors = config.get_default_colors(self.table.site) self.colors = config.get_default_colors(self.table.site)
self.stat_windows = {} self.stat_windows = {}

View File

@ -22,14 +22,9 @@ Mucked cards display for FreePokerTools HUD.
######################################################################## ########################################################################
# to do # to do
# problem with hand 30586
# Standard Library modules # Standard Library modules
import sys import sys
import os
import string
import xml.dom.minidom
from xml.dom.minidom import Node
# pyGTK modules # pyGTK modules
import pygtk import pygtk
@ -39,28 +34,30 @@ import gobject
# FreePokerTools modules # FreePokerTools modules
import Configuration import Configuration
import Database import Database
import Tables
import Hud
import HandHistory
class Aux_Window: class Aux_Window:
def __init__(self, parent, config, db_name): def __init__(self, parent, hud, config, db_name):
self.config = hud
self.config = config self.config = config
self.parent = parent #this is the parent of the mucked cards widget self.parent = parent
self.db_name = db_name self.db_name = db_name
self.vbox = gtk.VBox() self.vbox = gtk.VBox()
self.parent.add(self.vbox) self.parent.add(self.vbox)
def update(self): def update_data(self):
pass
def update_gui(self):
pass pass
class Stud_mucked(Aux_Window): class Stud_mucked(Aux_Window):
def __init__(self, parent, config, db_name): def __init__(self, parent, hud, config, db_name):
self.config = config self.hud = hud # hud object that this aux window supports
self.parent = parent #this is the parent of the mucked cards widget self.config = config # configuration object for this aux window to use
self.db_name = db_name self.parent = parent # parent container for this aux window widget
self.db_name = db_name # database for this aux window to use
self.vbox = gtk.VBox() self.vbox = gtk.VBox()
self.parent.add(self.vbox) self.parent.add(self.vbox)
@ -71,9 +68,11 @@ class Stud_mucked(Aux_Window):
self.parent.show_all() self.parent.show_all()
def update_data(self, new_hand_id): def update_data(self, new_hand_id):
self.mucked_cards.update_data(new_hand_id)
self.mucked_list.update_data(new_hand_id) self.mucked_list.update_data(new_hand_id)
def update_gui(self, new_hand_id): def update_gui(self, new_hand_id):
self.mucked_cards.update_gui(new_hand_id)
self.mucked_list.update_gui(new_hand_id) self.mucked_list.update_gui(new_hand_id)
class Stud_list: class Stud_list:
@ -89,16 +88,18 @@ class Stud_list:
self.parent.add(self.scrolled_window) self.parent.add(self.scrolled_window)
# create a ListStore to use as the model # create a ListStore to use as the model
self.liststore = gtk.ListStore(str, str, str) self.liststore = gtk.ListStore(str, str, str, str)
self.treeview = gtk.TreeView(self.liststore) self.treeview = gtk.TreeView(self.liststore)
self.tvcolumn0 = gtk.TreeViewColumn('HandID') self.tvcolumn0 = gtk.TreeViewColumn('HandID')
self.tvcolumn1 = gtk.TreeViewColumn('Cards') self.tvcolumn1 = gtk.TreeViewColumn('Cards')
self.tvcolumn2 = gtk.TreeViewColumn('Net') self.tvcolumn2 = gtk.TreeViewColumn('Net')
self.tvcolumn3 = gtk.TreeViewColumn('Winner')
# add tvcolumn to treeview # add tvcolumn to treeview
self.treeview.append_column(self.tvcolumn0) self.treeview.append_column(self.tvcolumn0)
self.treeview.append_column(self.tvcolumn1) self.treeview.append_column(self.tvcolumn1)
self.treeview.append_column(self.tvcolumn2) self.treeview.append_column(self.tvcolumn2)
self.treeview.append_column(self.tvcolumn3)
# create a CellRendererText to render the data # create a CellRendererText to render the data
self.cell = gtk.CellRendererText() self.cell = gtk.CellRendererText()
@ -107,11 +108,16 @@ class Stud_list:
self.tvcolumn0.pack_start(self.cell, True) self.tvcolumn0.pack_start(self.cell, True)
self.tvcolumn1.pack_start(self.cell, True) self.tvcolumn1.pack_start(self.cell, True)
self.tvcolumn2.pack_start(self.cell, True) self.tvcolumn2.pack_start(self.cell, True)
self.tvcolumn3.pack_start(self.cell, True)
self.tvcolumn0.add_attribute(self.cell, 'text', 0) self.tvcolumn0.add_attribute(self.cell, 'text', 0)
self.tvcolumn1.add_attribute(self.cell, 'text', 1) self.tvcolumn1.add_attribute(self.cell, 'text', 1)
self.tvcolumn2.add_attribute(self.cell, 'text', 2) self.tvcolumn2.add_attribute(self.cell, 'text', 2)
self.tvcolumn3.add_attribute(self.cell, 'text', 3)
# resize the cols if nec # resize the cols if nec
self.tvcolumn0.set_resizable(True) self.tvcolumn0.set_resizable(True)
self.tvcolumn1.set_resizable(True)
self.tvcolumn2.set_resizable(True)
self.tvcolumn3.set_resizable(True)
self.treeview.connect("row-activated", self.activated_event) self.treeview.connect("row-activated", self.activated_event)
self.scrolled_window.add_with_viewport(self.treeview) self.scrolled_window.add_with_viewport(self.treeview)
@ -119,22 +125,25 @@ class Stud_list:
def activated_event(self, path, column, data=None): def activated_event(self, path, column, data=None):
sel = self.treeview.get_selection() sel = self.treeview.get_selection()
(model, iter) = sel.get_selected() (model, iter) = sel.get_selected()
self.mucked_cards.update(model.get_value(iter, 0)) self.mucked_cards.update_data(model.get_value(iter, 0))
self.mucked_cards.update_gui(model.get_value(iter, 0))
def update(self, new_hand_id):
# info_row = self.db_connection.get_hand_info(new_hand_id)
info_row = ((new_hand_id, "xxxx", 0), )
iter = self.liststore.append(info_row[0])
sel = self.treeview.get_selection()
sel.select_iter(iter)
vadj = self.scrolled_window.get_vadjustment()
vadj.set_value(vadj.upper)
self.mucked_cards.update(new_hand_id)
def update_data(self, new_hand_id): def update_data(self, new_hand_id):
self.info_row = ((new_hand_id, "xxxx", 0), ) """Updates the data needed for the list box."""
self.mucked_cards.update_data(new_hand_id)
db_connection = Database.Database(self.config, 'fpdb', '')
self.winners = db_connection.get_winners_from_hand(new_hand_id)
db_connection.close_connection()
pot = 0
winners = ''
for player in self.winners.keys():
pot = pot + int(self.winners[player])
if not winners == '':
winners = winners + ", "
winners = winners + player
pot_dec = "%.2f" % (float(pot)/100)
self.info_row = ((new_hand_id, "xxxx", pot_dec, winners), )
def update_gui(self, new_hand_id): def update_gui(self, new_hand_id):
iter = self.liststore.append(self.info_row[0]) iter = self.liststore.append(self.info_row[0])
@ -143,7 +152,6 @@ class Stud_list:
vadj = self.scrolled_window.get_vadjustment() vadj = self.scrolled_window.get_vadjustment()
vadj.set_value(vadj.upper) vadj.set_value(vadj.upper)
self.mucked_cards.update_gui(new_hand_id)
class Stud_cards: class Stud_cards:
def __init__(self, parent, config, db_name = 'fpdb'): def __init__(self, parent, config, db_name = 'fpdb'):
@ -204,46 +212,6 @@ class Stud_cards:
old_cards[c][key] = ranks[old_cards[c][rank]] + old_cards[c][suit] old_cards[c][key] = ranks[old_cards[c][rank]] + old_cards[c][suit]
return old_cards return old_cards
def update(self, new_hand_id):
db_connection = Database.Database(self.config, 'fpdb', '')
cards = db_connection.get_cards(new_hand_id)
self.clear()
cards = self.translate_cards(cards)
for c in cards.keys():
self.grid_contents[(1, cards[c]['seat_number'] - 1)].set_text(cards[c]['screen_name'])
for i in ((0, 'hole_card_1'), (1, 'hole_card_2'), (2, 'hole_card_3'), (3, 'hole_card_4'),
(4, 'hole_card_5'), (5, 'hole_card_6'), (6, 'hole_card_7')):
if not cards[c][i[1]] == "xx":
self.seen_cards[(i[0], cards[c]['seat_number'] - 1)]. \
set_from_pixbuf(self.card_images[self.split_cards(cards[c][i[1]])])
tips = []
action = db_connection.get_action_from_hand(new_hand_id)
for street in action:
temp = ''
for act in street:
temp = temp + act[0] + " " + act[1] + "s "
if act[2] > 0:
if act[2]%100 > 0:
temp = temp + "%4.2f\n" % (float(act[2])/100)
else:
temp = temp + "%d\n" % (act[2]/100)
else:
temp = temp + "\n"
tips.append(temp)
## action in tool tips for 3rd street cards
for c in (0, 1, 2):
for r in range(0, self.rows):
self.eb[(c, r)].set_tooltip_text(tips[0])
# action in tools tips for later streets
round_to_col = (0, 3, 4, 5, 6)
for round in range(1, len(tips)):
for r in range(0, self.rows):
self.eb[(round_to_col[round], r)].set_tooltip_text(tips[round])
db_connection.close_connection()
def update_data(self, new_hand_id): def update_data(self, new_hand_id):
db_connection = Database.Database(self.config, 'fpdb', '') db_connection = Database.Database(self.config, 'fpdb', '')
cards = db_connection.get_cards(new_hand_id) cards = db_connection.get_cards(new_hand_id)
@ -330,7 +298,7 @@ if __name__== "__main__":
main_window.connect("destroy", destroy) main_window.connect("destroy", destroy)
aux_to_call = "Stud_mucked" aux_to_call = "Stud_mucked"
m = eval("%s(main_window, config, 'fpdb')" % aux_to_call) m = eval("%s(main_window, None, config, 'fpdb')" % aux_to_call)
main_window.show_all() main_window.show_all()
s_id = gobject.io_add_watch(sys.stdin, gobject.IO_IN, process_new_hand) s_id = gobject.io_add_watch(sys.stdin, gobject.IO_IN, process_new_hand)

View File

@ -30,6 +30,7 @@ import fpdb_db
import fpdb_import import fpdb_import
import fpdb_simple import fpdb_simple
import FpdbSQLQueries import FpdbSQLQueries
import Tables
import unittest import unittest
@ -89,6 +90,11 @@ class TestSequenceFunctions(unittest.TestCase):
self.failUnless(result==datetime.datetime(2008,8,17,6,14,43), self.failUnless(result==datetime.datetime(2008,8,17,6,14,43),
"Date incorrect, expected: 2008-08-17 01:14:43 got: " + str(result)) "Date incorrect, expected: 2008-08-17 01:14:43 got: " + str(result))
def testTableDetection(self):
result = Tables.clean_title("French (deep)")
self.failUnless(result == "French", "French (deep) parsed incorrectly. Expected 'French' got: " + str(result))
# result = ("French (deep) - $0.25/$0.50 - No Limit Hold'em - Logged In As xxxx")
def testImportHandHistoryFiles(self): def testImportHandHistoryFiles(self):
"""Test import of single HH file""" """Test import of single HH file"""
self.mysqlimporter.addImportFile("regression-test-files/hand-histories/ps-lhe-ring-3hands.txt") self.mysqlimporter.addImportFile("regression-test-files/hand-histories/ps-lhe-ring-3hands.txt")

View File

@ -319,6 +319,14 @@ class Sql:
""" """
# WHERE handId = %s AND Players.id LIKE %s # WHERE handId = %s AND Players.id LIKE %s
self.query['get_winners_from_hand'] = """
SELECT name, winnings
FROM HandsPlayers, Players
WHERE winnings > 0
AND Players.id = HandsPlayers.playerId
AND handId = %s;
"""
self.query['get_table_name'] = """ self.query['get_table_name'] = """
select tableName, maxSeats, category select tableName, maxSeats, category
from Hands,Gametypes from Hands,Gametypes

View File

@ -291,17 +291,6 @@ def get_site_from_exe(c, exe):
return params['site_name'] return params['site_name']
return None return None
def clean_title(name):
"""Clean the little info strings from the table name."""
# these strings could go in a config file
for pattern in [' \(6 max\)', ' \(heads up\)', ' \(deep\)',
' \(deep hu\)', ' \(deep 6\)', ' \(2\)',
' \(edu\)', ' \(edu, 6 max\)', ' \(6\)',
' no all-in', ' fast', ',', ' 50BB min', '\s+$']:
name = re.sub(pattern, '', name)
name = name.rstrip()
return name
def pokerstars_decode_table(tw): def pokerstars_decode_table(tw):
# Extract poker information from the window title. This is not needed for # Extract poker information from the window title. This is not needed for
# fpdb, since all that information is available in the db via new_hand_number. # fpdb, since all that information is available in the db via new_hand_number.
@ -431,8 +420,8 @@ def discover_mac_by_name(c, tablename):
if __name__=="__main__": if __name__=="__main__":
c = Configuration.Config() c = Configuration.Config()
print discover_table_by_name(c, "Howard Lederer") print discover_table_by_name(c, "Daddy Nasty")
print discover_tournament_table(c, "118942908", "3") # print discover_tournament_table(c, "118942908", "3")
tables = discover(c) tables = discover(c)
for t in tables.keys(): for t in tables.keys():

View File

@ -59,7 +59,11 @@ class Importer:
#Set defaults #Set defaults
self.callHud = self.config.get_import_parameters().get("callFpdbHud") self.callHud = self.config.get_import_parameters().get("callFpdbHud")
if not self.settings.has_key('minPrint'): if not self.settings.has_key('minPrint'):
#TODO: Is this value in the xml file?
self.settings['minPrint'] = 30 self.settings['minPrint'] = 30
if not self.settings.has_key('handCount'):
#TODO: Is this value in the xml file?
self.settings['handCount'] = 0
self.dbConnect() self.dbConnect()
# XXX: Why is this here, when fpdb_db.connect() already does the # XXX: Why is this here, when fpdb_db.connect() already does the

BIN
pyfpdb/psnlheparser-mct.tgz Normal file

Binary file not shown.