Merge branch 'master' of git://git.assembla.com/fpdboz
This commit is contained in:
commit
0dbcf0079c
|
@ -152,6 +152,15 @@ class Database:
|
|||
action[street].append(act)
|
||||
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):
|
||||
c = self.connection.cursor()
|
||||
|
||||
|
|
|
@ -68,17 +68,18 @@ class Everleaf(HandHistoryConverter):
|
|||
print "Initialising Everleaf converter class"
|
||||
HandHistoryConverter.__init__(self, config, file, sitename="Everleaf") # Call super class init.
|
||||
self.sitename = "Everleaf"
|
||||
self.setFileType("text")
|
||||
self.setFileType("text", "cp1252")
|
||||
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.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.setPostBbRegex('.*\n(?P<PNAME>.*): posts big blind \[\$? (?P<BB>[.0-9]+)')
|
||||
# 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.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.setCollectPotRegex('.*\n(?P<PNAME>.*) wins \$ (?P<POT>[.\d]+) USD(.*\[ (?P<HAND>.*) \])?')
|
||||
self.rexx.compileRegexes()
|
||||
|
||||
def readSupportedGames(self):
|
||||
|
@ -117,20 +118,23 @@ class Everleaf(HandHistoryConverter):
|
|||
def readPlayerStacks(self, hand):
|
||||
m = self.rexx.player_info_re.finditer(hand.string)
|
||||
players = []
|
||||
|
||||
for a in m:
|
||||
hand.addPlayer(int(a.group('SEAT')), a.group('PNAME'), a.group('CASH'))
|
||||
|
||||
def markStreets(self, hand):
|
||||
# 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)
|
||||
# for street in m.groupdict():
|
||||
# print "DEBUG: Street: %s\tspan: %s" %(street, str(m.span(street)))
|
||||
# This re fails if, say, river is missing; then we don't get the ** that starts the river.
|
||||
#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)
|
||||
|
||||
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
|
||||
|
||||
def readCommunityCards(self, hand):
|
||||
# currently regex in wrong place pls fix my brain's fried
|
||||
# what a mess!
|
||||
re_board = re.compile('\*\* Dealing (?P<STREET>.*) \*\* \[ (?P<CARDS>.*) \]')
|
||||
m = re_board.finditer(hand.string)
|
||||
for street in m:
|
||||
|
@ -165,15 +169,19 @@ class Everleaf(HandHistoryConverter):
|
|||
m = self.rexx.action_re.finditer(hand.streets.group(street))
|
||||
hand.actions[street] = []
|
||||
for action in m:
|
||||
if action.group('ATYPE') == 'raises':
|
||||
if action.group('ATYPE') == ' raises':
|
||||
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') )
|
||||
elif action.group('ATYPE') == 'bets':
|
||||
elif action.group('ATYPE') == ': bets':
|
||||
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:
|
||||
#print "DEBUG: unimplemented readAction: %s %s" %(action.group('PNAME'),action.group('ATYPE'),)
|
||||
hand.actions[street] += [[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')]]
|
||||
|
||||
|
||||
def readShowdownActions(self, hand):
|
||||
|
@ -182,15 +190,25 @@ class Everleaf(HandHistoryConverter):
|
|||
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'))]
|
||||
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):
|
||||
hand.rake = hand.totalpot * Decimal('0.05') # probably not quite right
|
||||
|
||||
if __name__ == "__main__":
|
||||
c = Configuration.Config()
|
||||
e = Everleaf(c, "Speed_Kuala.txt")
|
||||
e = Everleaf(c, "regression-test-files/everleaf/Speed_Kuala_full.txt")
|
||||
e.processFile()
|
||||
print str(e)
|
||||
|
||||
|
|
|
@ -73,13 +73,13 @@ class FpdbSQLQueries:
|
|||
|
||||
if(self.dbname == 'MySQL InnoDB'):
|
||||
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,
|
||||
currency char(3) NOT NULL)
|
||||
ENGINE=INNODB"""
|
||||
elif(self.dbname == 'PostgreSQL'):
|
||||
self.query['createSitesTable'] = """CREATE TABLE Sites (
|
||||
id SERIAL UNIQUE, PRIMARY KEY (id),
|
||||
id SERIAL, PRIMARY KEY (id),
|
||||
name varchar(32),
|
||||
currency char(3))"""
|
||||
elif(self.dbname == 'SQLite'):
|
||||
|
@ -92,7 +92,7 @@ class FpdbSQLQueries:
|
|||
|
||||
if(self.dbname == 'MySQL InnoDB'):
|
||||
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),
|
||||
type char(4) NOT NULL,
|
||||
base char(4) NOT NULL,
|
||||
|
@ -106,7 +106,7 @@ class FpdbSQLQueries:
|
|||
ENGINE=INNODB"""
|
||||
elif(self.dbname == 'PostgreSQL'):
|
||||
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),
|
||||
type char(4),
|
||||
base char(4),
|
||||
|
@ -127,7 +127,7 @@ class FpdbSQLQueries:
|
|||
|
||||
if(self.dbname == 'MySQL InnoDB'):
|
||||
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,
|
||||
siteId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (siteId) REFERENCES Sites(id),
|
||||
comment text,
|
||||
|
@ -135,7 +135,7 @@ class FpdbSQLQueries:
|
|||
ENGINE=INNODB"""
|
||||
elif(self.dbname == 'PostgreSQL'):
|
||||
self.query['createPlayersTable'] = """CREATE TABLE Players (
|
||||
id SERIAL UNIQUE, PRIMARY KEY (id),
|
||||
id SERIAL, PRIMARY KEY (id),
|
||||
name VARCHAR(32),
|
||||
siteId INTEGER, FOREIGN KEY (siteId) REFERENCES Sites(id),
|
||||
comment text,
|
||||
|
@ -150,7 +150,7 @@ class FpdbSQLQueries:
|
|||
|
||||
if(self.dbname == 'MySQL InnoDB'):
|
||||
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),
|
||||
gametypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
|
||||
description varchar(50) NOT NULL,
|
||||
|
@ -160,7 +160,7 @@ class FpdbSQLQueries:
|
|||
ENGINE=INNODB"""
|
||||
elif(self.dbname == 'PostgreSQL'):
|
||||
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),
|
||||
gametypeId INT, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
|
||||
description varchar(50),
|
||||
|
@ -177,7 +177,7 @@ class FpdbSQLQueries:
|
|||
|
||||
if(self.dbname == 'MySQL InnoDB'):
|
||||
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,
|
||||
siteHandNo BIGINT NOT NULL,
|
||||
gametypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
|
||||
|
@ -190,7 +190,7 @@ class FpdbSQLQueries:
|
|||
ENGINE=INNODB"""
|
||||
elif(self.dbname == 'PostgreSQL'):
|
||||
self.query['createHandsTable'] = """CREATE TABLE Hands (
|
||||
id BIGSERIAL UNIQUE, PRIMARY KEY (id),
|
||||
id BIGSERIAL, PRIMARY KEY (id),
|
||||
tableName VARCHAR(20),
|
||||
siteHandNo BIGINT,
|
||||
gametypeId INT, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
|
||||
|
@ -210,7 +210,7 @@ class FpdbSQLQueries:
|
|||
|
||||
if(self.dbname == 'MySQL InnoDB'):
|
||||
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),
|
||||
card1Value smallint NOT NULL,
|
||||
card1Suit char(1) NOT NULL,
|
||||
|
@ -225,7 +225,7 @@ class FpdbSQLQueries:
|
|||
ENGINE=INNODB"""
|
||||
elif(self.dbname == 'PostgreSQL'):
|
||||
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),
|
||||
card1Value smallint,
|
||||
card1Suit char(1),
|
||||
|
@ -247,7 +247,7 @@ class FpdbSQLQueries:
|
|||
|
||||
if(self.dbname == 'MySQL InnoDB'):
|
||||
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),
|
||||
buyin INT NOT NULL,
|
||||
fee INT NOT NULL,
|
||||
|
@ -272,7 +272,7 @@ class FpdbSQLQueries:
|
|||
|
||||
if(self.dbname == 'MySQL InnoDB'):
|
||||
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),
|
||||
siteTourneyNo BIGINT NOT NULL,
|
||||
entries INT NOT NULL,
|
||||
|
@ -283,7 +283,7 @@ class FpdbSQLQueries:
|
|||
ENGINE=INNODB"""
|
||||
elif(self.dbname == 'PostgreSQL'):
|
||||
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),
|
||||
siteTourneyNo BIGINT,
|
||||
entries INT,
|
||||
|
@ -300,7 +300,7 @@ class FpdbSQLQueries:
|
|||
|
||||
if(self.dbname == 'MySQL InnoDB'):
|
||||
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),
|
||||
playerId INT UNSIGNED NOT NULL, FOREIGN KEY (playerId) REFERENCES Players(id),
|
||||
startCash INT NOT NULL,
|
||||
|
@ -332,7 +332,7 @@ class FpdbSQLQueries:
|
|||
ENGINE=INNODB"""
|
||||
elif(self.dbname == 'PostgreSQL'):
|
||||
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),
|
||||
playerId INT, FOREIGN KEY (playerId) REFERENCES Players(id),
|
||||
startCash INT,
|
||||
|
@ -370,7 +370,7 @@ class FpdbSQLQueries:
|
|||
|
||||
if(self.dbname == 'MySQL InnoDB'):
|
||||
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),
|
||||
playerId INT UNSIGNED NOT NULL, FOREIGN KEY (playerId) REFERENCES Players(id),
|
||||
payinAmount INT NOT NULL,
|
||||
|
@ -381,7 +381,7 @@ class FpdbSQLQueries:
|
|||
ENGINE=INNODB"""
|
||||
elif(self.dbname == 'PostgreSQL'):
|
||||
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),
|
||||
playerId INT, FOREIGN KEY (playerId) REFERENCES Players(id),
|
||||
payinAmount INT,
|
||||
|
@ -399,7 +399,7 @@ class FpdbSQLQueries:
|
|||
|
||||
if(self.dbname == 'MySQL InnoDB'):
|
||||
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),
|
||||
street SMALLINT NOT NULL,
|
||||
actionNo SMALLINT NOT NULL,
|
||||
|
@ -411,7 +411,7 @@ class FpdbSQLQueries:
|
|||
ENGINE=INNODB"""
|
||||
elif(self.dbname == 'PostgreSQL'):
|
||||
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),
|
||||
street SMALLINT,
|
||||
actionNo SMALLINT,
|
||||
|
@ -430,7 +430,7 @@ class FpdbSQLQueries:
|
|||
|
||||
if(self.dbname == 'MySQL InnoDB'):
|
||||
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),
|
||||
playerId INT UNSIGNED NOT NULL, FOREIGN KEY (playerId) REFERENCES Players(id),
|
||||
activeSeats SMALLINT NOT NULL,
|
||||
|
@ -503,7 +503,7 @@ class FpdbSQLQueries:
|
|||
ENGINE=INNODB"""
|
||||
elif(self.dbname == 'PostgreSQL'):
|
||||
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),
|
||||
playerId INT, FOREIGN KEY (playerId) REFERENCES Players(id),
|
||||
activeSeats SMALLINT,
|
||||
|
@ -726,7 +726,72 @@ class FpdbSQLQueries:
|
|||
on hprof2.gameTypeId = stats.gameTypeId
|
||||
order by stats.base, stats.limittype, stats.bigBlind"""
|
||||
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'):
|
||||
self.query['playerStats'] = """ """
|
||||
|
||||
|
|
|
@ -83,22 +83,24 @@ class GuiGraphViewer (threading.Thread):
|
|||
self.ax.set_xlabel("Hands", fontsize = 12)
|
||||
self.ax.set_ylabel("$", fontsize = 12)
|
||||
self.ax.grid(color='g', linestyle=':', linewidth=0.2)
|
||||
#This line will crash if no hands exist in the query.
|
||||
text = "All Hands, " + sitename + str(name) + "\nProfit: $" + str(line[-1]) + "\nTotal Hands: " + str(len(line))
|
||||
if(line == None):
|
||||
#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,
|
||||
xy=(10, -10),
|
||||
xycoords='axes points',
|
||||
horizontalalignment='left', verticalalignment='top',
|
||||
fontsize=10)
|
||||
self.ax.annotate(text,
|
||||
xy=(10, -10),
|
||||
xycoords='axes points',
|
||||
horizontalalignment='left', verticalalignment='top',
|
||||
fontsize=10)
|
||||
|
||||
#Draw plot
|
||||
self.ax.plot(line,)
|
||||
|
||||
#Draw plot
|
||||
self.ax.plot(line,)
|
||||
|
||||
self.canvas = FigureCanvas(self.fig) # a gtk.DrawingArea
|
||||
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
|
||||
|
||||
def getRingProfitGraph(self, name, site):
|
||||
|
@ -106,6 +108,9 @@ class GuiGraphViewer (threading.Thread):
|
|||
#returns (HandId,Winnings,Costs,Profit)
|
||||
winnings = self.db.cursor.fetchall()
|
||||
|
||||
if(winnings == ()):
|
||||
return None
|
||||
|
||||
y=map(lambda x:float(x[3]), winnings)
|
||||
line = cumsum(y)
|
||||
return line/100
|
||||
|
|
|
@ -44,43 +44,44 @@ class GuiPlayerStats (threading.Thread):
|
|||
# Get currently active site and grab playerid
|
||||
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()
|
||||
pid = result[0][0]
|
||||
tmp = tmp.replace("<player_test>", "(" + str(pid) + ")")
|
||||
self.cursor.execute(tmp)
|
||||
result = self.db.cursor.fetchall()
|
||||
cols = 18
|
||||
rows = len(result)+1 # +1 for title row
|
||||
self.stats_table = gtk.Table(rows, cols, False)
|
||||
self.stats_table.set_col_spacings(4)
|
||||
self.stats_table.show()
|
||||
vbox.add(self.stats_table)
|
||||
if not result == ():
|
||||
pid = result[0][0]
|
||||
tmp = tmp.replace("<player_test>", "(" + str(pid) + ")")
|
||||
self.cursor.execute(tmp)
|
||||
result = self.db.cursor.fetchall()
|
||||
cols = 18
|
||||
rows = len(result)+1 # +1 for title row
|
||||
self.stats_table = gtk.Table(rows, cols, False)
|
||||
self.stats_table.set_col_spacings(4)
|
||||
self.stats_table.show()
|
||||
vbox.add(self.stats_table)
|
||||
|
||||
# Create header row
|
||||
titles = ("GID", "base", "Style", "Site", "$BB", "Hands", "VPIP", "PFR", "saw_f", "sawsd", "wtsdwsf", "wmsd", "FlAFq", "TuAFq", "RvAFq", "PFAFq", "Net($)", "BB/100")
|
||||
# Create header row
|
||||
titles = ("GID", "base", "Style", "Site", "$BB", "Hands", "VPIP", "PFR", "saw_f", "sawsd", "wtsdwsf", "wmsd", "FlAFq", "TuAFq", "RvAFq", "PFAFq", "Net($)", "BB/100")
|
||||
|
||||
col = 0
|
||||
row = 0
|
||||
for t in titles:
|
||||
l = gtk.Label(titles[col])
|
||||
l.show()
|
||||
self.stats_table.attach(l, col, col+1, row, row+1)
|
||||
col +=1
|
||||
col = 0
|
||||
row = 0
|
||||
for t in titles:
|
||||
l = gtk.Label(titles[col])
|
||||
l.show()
|
||||
self.stats_table.attach(l, col, col+1, row, row+1)
|
||||
col +=1
|
||||
|
||||
for row in range(rows-1):
|
||||
for col in range(cols):
|
||||
if(row%2 == 0):
|
||||
bgcolor = "white"
|
||||
else:
|
||||
bgcolor = "lightgrey"
|
||||
eb = gtk.EventBox()
|
||||
eb.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(bgcolor))
|
||||
l = gtk.Label(result[row-1][col])
|
||||
eb.add(l)
|
||||
self.stats_table.attach(eb, col, col+1, row+1, row+2)
|
||||
l.show()
|
||||
eb.show()
|
||||
for row in range(rows-1):
|
||||
for col in range(cols):
|
||||
if(row%2 == 0):
|
||||
bgcolor = "white"
|
||||
else:
|
||||
bgcolor = "lightgrey"
|
||||
eb = gtk.EventBox()
|
||||
eb.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(bgcolor))
|
||||
l = gtk.Label(result[row-1][col])
|
||||
eb.add(l)
|
||||
self.stats_table.attach(eb, col, col+1, row+1, row+2)
|
||||
l.show()
|
||||
eb.show()
|
||||
|
||||
|
||||
def fillPlayerFrame(self, vbox):
|
||||
|
|
404
pyfpdb/Hand.py
Normal file
404
pyfpdb/Hand.py
Normal 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
|
|
@ -17,26 +17,63 @@
|
|||
|
||||
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 xml.dom.minidom import Node
|
||||
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:
|
||||
eval = PokerEval()
|
||||
def __init__(self, config, file, sitename):
|
||||
print "HandHistory init called"
|
||||
|
||||
self.c = config
|
||||
self.sitename = sitename
|
||||
self.obs = "" # One big string
|
||||
self.filetype = "text"
|
||||
self.codepage = "utf8"
|
||||
self.doc = None # For XML based HH files
|
||||
self.file = file
|
||||
self.hhbase = self.c.get_import_parameters().get("hhArchiveBase")
|
||||
|
@ -60,6 +97,7 @@ class HandHistoryConverter:
|
|||
return tmp
|
||||
|
||||
def processFile(self):
|
||||
starttime = time()
|
||||
if not self.sanityCheck():
|
||||
print "Cowardly refusing to continue after failed sanity check"
|
||||
return
|
||||
|
@ -67,6 +105,7 @@ class HandHistoryConverter:
|
|||
self.gametype = self.determineGameType()
|
||||
self.hands = self.splitFileIntoHands()
|
||||
for hand in self.hands:
|
||||
print "\nInput:\n"+hand.string
|
||||
self.readHandInfo(hand)
|
||||
self.readPlayerStacks(hand)
|
||||
self.markStreets(hand)
|
||||
|
@ -76,17 +115,24 @@ class HandHistoryConverter:
|
|||
self.readShowdownActions(hand)
|
||||
# Read action (Note: no guarantee this is in hand order.
|
||||
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)
|
||||
hand.totalPot()
|
||||
self.getRake(hand)
|
||||
|
||||
if(hand.involved == True):
|
||||
hand.printHand()
|
||||
#if(hand.involved == True):
|
||||
#self.writeHand("output file", hand)
|
||||
hand.printHand()
|
||||
else:
|
||||
pass #Don't write out observed hands
|
||||
#hand.printHand()
|
||||
#else:
|
||||
#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
|
||||
|
@ -124,6 +170,7 @@ class HandHistoryConverter:
|
|||
def readBlinds(self, hand): abstract
|
||||
def readHeroCards(self, hand): 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
|
||||
# so that an inheriting class can calculate it for the specific site if need be.
|
||||
|
@ -151,23 +198,25 @@ class HandHistoryConverter:
|
|||
return sane
|
||||
|
||||
# Functions not necessary to implement in sub class
|
||||
def setFileType(self, filetype = "text"):
|
||||
def setFileType(self, filetype = "text", codepage='utf8'):
|
||||
self.filetype = filetype
|
||||
self.codepage = codepage
|
||||
|
||||
def splitFileIntoHands(self):
|
||||
hands = []
|
||||
self.obs.strip()
|
||||
list = self.rexx.split_hand_re.split(self.obs)
|
||||
list.pop() #Last entry is empty
|
||||
for l in list:
|
||||
# print "'" + l + "'"
|
||||
hands = hands + [Hand(self.sitename, self.gametype, l)]
|
||||
hands = hands + [Hand.Hand(self.sitename, self.gametype, l)]
|
||||
return hands
|
||||
|
||||
def readFile(self, filename):
|
||||
"""Read file"""
|
||||
print "Reading file: '%s'" %(filename)
|
||||
if(self.filetype == "text"):
|
||||
infile=open(filename, "rU")
|
||||
infile=codecs.open(filename, "rU", self.codepage)
|
||||
self.obs = infile.read()
|
||||
infile.close()
|
||||
elif(self.filetype == "xml"):
|
||||
|
@ -177,66 +226,6 @@ class HandHistoryConverter:
|
|||
except:
|
||||
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
|
||||
def float2int (self, string):
|
||||
|
@ -253,262 +242,3 @@ class HandHistoryConverter:
|
|||
result*=100
|
||||
return result
|
||||
#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
|
|
@ -55,6 +55,7 @@ class Hud:
|
|||
self.db_name = db_name
|
||||
self.deleted = False
|
||||
self.stacked = True
|
||||
self.site = table.site
|
||||
self.colors = config.get_default_colors(self.table.site)
|
||||
|
||||
self.stat_windows = {}
|
||||
|
|
112
pyfpdb/Mucked.py
112
pyfpdb/Mucked.py
|
@ -22,14 +22,9 @@ Mucked cards display for FreePokerTools HUD.
|
|||
########################################################################
|
||||
|
||||
# to do
|
||||
# problem with hand 30586
|
||||
|
||||
# Standard Library modules
|
||||
import sys
|
||||
import os
|
||||
import string
|
||||
import xml.dom.minidom
|
||||
from xml.dom.minidom import Node
|
||||
|
||||
# pyGTK modules
|
||||
import pygtk
|
||||
|
@ -39,28 +34,30 @@ import gobject
|
|||
# FreePokerTools modules
|
||||
import Configuration
|
||||
import Database
|
||||
import Tables
|
||||
import Hud
|
||||
import HandHistory
|
||||
|
||||
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.parent = parent #this is the parent of the mucked cards widget
|
||||
self.parent = parent
|
||||
self.db_name = db_name
|
||||
|
||||
self.vbox = gtk.VBox()
|
||||
self.parent.add(self.vbox)
|
||||
|
||||
def update(self):
|
||||
def update_data(self):
|
||||
pass
|
||||
|
||||
def update_gui(self):
|
||||
pass
|
||||
|
||||
class Stud_mucked(Aux_Window):
|
||||
def __init__(self, parent, config, db_name):
|
||||
def __init__(self, parent, hud, config, db_name):
|
||||
|
||||
self.config = config
|
||||
self.parent = parent #this is the parent of the mucked cards widget
|
||||
self.db_name = db_name
|
||||
self.hud = hud # hud object that this aux window supports
|
||||
self.config = config # configuration object for this aux window to use
|
||||
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.parent.add(self.vbox)
|
||||
|
@ -71,9 +68,11 @@ class Stud_mucked(Aux_Window):
|
|||
self.parent.show_all()
|
||||
|
||||
def update_data(self, new_hand_id):
|
||||
self.mucked_cards.update_data(new_hand_id)
|
||||
self.mucked_list.update_data(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)
|
||||
|
||||
class Stud_list:
|
||||
|
@ -89,16 +88,18 @@ class Stud_list:
|
|||
self.parent.add(self.scrolled_window)
|
||||
|
||||
# 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.tvcolumn0 = gtk.TreeViewColumn('HandID')
|
||||
self.tvcolumn1 = gtk.TreeViewColumn('Cards')
|
||||
self.tvcolumn2 = gtk.TreeViewColumn('Net')
|
||||
self.tvcolumn3 = gtk.TreeViewColumn('Winner')
|
||||
|
||||
# add tvcolumn to treeview
|
||||
self.treeview.append_column(self.tvcolumn0)
|
||||
self.treeview.append_column(self.tvcolumn1)
|
||||
self.treeview.append_column(self.tvcolumn2)
|
||||
self.treeview.append_column(self.tvcolumn3)
|
||||
|
||||
# create a CellRendererText to render the data
|
||||
self.cell = gtk.CellRendererText()
|
||||
|
@ -107,11 +108,16 @@ class Stud_list:
|
|||
self.tvcolumn0.pack_start(self.cell, True)
|
||||
self.tvcolumn1.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.tvcolumn1.add_attribute(self.cell, 'text', 1)
|
||||
self.tvcolumn2.add_attribute(self.cell, 'text', 2)
|
||||
self.tvcolumn3.add_attribute(self.cell, 'text', 3)
|
||||
# resize the cols if nec
|
||||
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.scrolled_window.add_with_viewport(self.treeview)
|
||||
|
@ -119,22 +125,25 @@ class Stud_list:
|
|||
def activated_event(self, path, column, data=None):
|
||||
sel = self.treeview.get_selection()
|
||||
(model, iter) = sel.get_selected()
|
||||
self.mucked_cards.update(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)
|
||||
self.mucked_cards.update_data(model.get_value(iter, 0))
|
||||
self.mucked_cards.update_gui(model.get_value(iter, 0))
|
||||
|
||||
def update_data(self, new_hand_id):
|
||||
self.info_row = ((new_hand_id, "xxxx", 0), )
|
||||
self.mucked_cards.update_data(new_hand_id)
|
||||
"""Updates the data needed for the list box."""
|
||||
|
||||
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):
|
||||
iter = self.liststore.append(self.info_row[0])
|
||||
|
@ -143,7 +152,6 @@ class Stud_list:
|
|||
|
||||
vadj = self.scrolled_window.get_vadjustment()
|
||||
vadj.set_value(vadj.upper)
|
||||
self.mucked_cards.update_gui(new_hand_id)
|
||||
|
||||
class Stud_cards:
|
||||
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]
|
||||
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):
|
||||
db_connection = Database.Database(self.config, 'fpdb', '')
|
||||
cards = db_connection.get_cards(new_hand_id)
|
||||
|
@ -330,7 +298,7 @@ if __name__== "__main__":
|
|||
main_window.connect("destroy", destroy)
|
||||
|
||||
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()
|
||||
|
||||
s_id = gobject.io_add_watch(sys.stdin, gobject.IO_IN, process_new_hand)
|
||||
|
|
|
@ -30,6 +30,7 @@ import fpdb_db
|
|||
import fpdb_import
|
||||
import fpdb_simple
|
||||
import FpdbSQLQueries
|
||||
import Tables
|
||||
|
||||
import unittest
|
||||
|
||||
|
@ -89,6 +90,11 @@ class TestSequenceFunctions(unittest.TestCase):
|
|||
self.failUnless(result==datetime.datetime(2008,8,17,6,14,43),
|
||||
"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):
|
||||
"""Test import of single HH file"""
|
||||
self.mysqlimporter.addImportFile("regression-test-files/hand-histories/ps-lhe-ring-3hands.txt")
|
||||
|
|
|
@ -319,6 +319,14 @@ class Sql:
|
|||
"""
|
||||
# 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'] = """
|
||||
select tableName, maxSeats, category
|
||||
from Hands,Gametypes
|
||||
|
|
|
@ -291,17 +291,6 @@ def get_site_from_exe(c, exe):
|
|||
return params['site_name']
|
||||
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):
|
||||
# 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.
|
||||
|
@ -431,8 +420,8 @@ def discover_mac_by_name(c, tablename):
|
|||
if __name__=="__main__":
|
||||
c = Configuration.Config()
|
||||
|
||||
print discover_table_by_name(c, "Howard Lederer")
|
||||
print discover_tournament_table(c, "118942908", "3")
|
||||
print discover_table_by_name(c, "Daddy Nasty")
|
||||
# print discover_tournament_table(c, "118942908", "3")
|
||||
|
||||
tables = discover(c)
|
||||
for t in tables.keys():
|
||||
|
|
|
@ -59,7 +59,11 @@ class Importer:
|
|||
#Set defaults
|
||||
self.callHud = self.config.get_import_parameters().get("callFpdbHud")
|
||||
if not self.settings.has_key('minPrint'):
|
||||
#TODO: Is this value in the xml file?
|
||||
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()
|
||||
|
||||
# XXX: Why is this here, when fpdb_db.connect() already does the
|
||||
|
|
BIN
pyfpdb/psnlheparser-mct.tgz
Normal file
BIN
pyfpdb/psnlheparser-mct.tgz
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user