From cbac52ccbbdb68a1bd10993bcf615360a3ded0b6 Mon Sep 17 00:00:00 2001 From: Matt Turnbull Date: Sat, 13 Dec 2008 00:52:31 +0000 Subject: [PATCH 01/43] Autoimport toggle button --- pyfpdb/GuiAutoImport.py | 67 +++++++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 29 deletions(-) diff --git a/pyfpdb/GuiAutoImport.py b/pyfpdb/GuiAutoImport.py index bb5e0386..e74c3888 100644 --- a/pyfpdb/GuiAutoImport.py +++ b/pyfpdb/GuiAutoImport.py @@ -71,7 +71,8 @@ class GuiAutoImport (threading.Thread): self.addSites(self.mainVBox) - self.startButton=gtk.Button("Start Autoimport") + self.doAutoImportBool = False + self.startButton=gtk.ToggleButton("Start Autoimport") self.startButton.connect("clicked", self.startClicked, "start clicked") self.mainVBox.add(self.startButton) self.startButton.show() @@ -103,7 +104,7 @@ class GuiAutoImport (threading.Thread): """Callback for timer to do an import iteration.""" self.importer.runUpdated() print "GuiAutoImport.import_dir done" - return True + return self.doAutoImportBool def startClicked(self, widget, data): """runs when user clicks start on auto import tab""" @@ -117,34 +118,42 @@ class GuiAutoImport (threading.Thread): # That is not correct. It should open another dir for importing while piping the # results to the same pipe. This means that self.path should be a a list of dirs # to watch. - try: #uhhh, I don't this this is the best way to check for the existence of an attr - getattr(self, "pipe_to_hud") - except AttributeError: - if os.name == 'nt': - command = "python HUD_main.py" + " %s" % (self.database) - bs = 0 # windows is not happy with line buffing here - self.pipe_to_hud = subprocess.Popen(command, bufsize = bs, stdin = subprocess.PIPE, - universal_newlines=True) - else: - cwd = os.getcwd() - command = os.path.join(cwd, 'HUD_main.py') - bs = 1 - self.pipe_to_hud = subprocess.Popen((command, self.database), bufsize = bs, stdin = subprocess.PIPE, - universal_newlines=True) -# self.pipe_to_hud = subprocess.Popen((command, self.database), bufsize = bs, stdin = subprocess.PIPE, -# universal_newlines=True) -# command = command + " %s" % (self.database) -# print "command = ", command -# self.pipe_to_hud = os.popen(command, 'w') + if widget.get_active(): # toggled on + self.doAutoImportBool = True + widget.set_label(u'Stop Autoimport') + try: #uhhh, I don't this this is the best way to check for the existence of an attr + getattr(self, "pipe_to_hud") + except AttributeError: + if os.name == 'nt': + command = "python HUD_main.py" + " %s" % (self.database) + bs = 0 # windows is not happy with line buffing here + self.pipe_to_hud = subprocess.Popen(command, bufsize = bs, stdin = subprocess.PIPE, + universal_newlines=True) + else: + cwd = os.getcwd() + command = os.path.join(cwd, 'HUD_main.py') + bs = 1 + self.pipe_to_hud = subprocess.Popen((command, self.database), bufsize = bs, stdin = subprocess.PIPE, + universal_newlines=True) + # self.pipe_to_hud = subprocess.Popen((command, self.database), bufsize = bs, stdin = subprocess.PIPE, + # universal_newlines=True) + # command = command + " %s" % (self.database) + # print "command = ", command + # self.pipe_to_hud = os.popen(command, 'w') -# Add directories to importer object. - for site in self.input_settings: - self.importer.addImportDirectory(self.input_settings[site][0], True, site, self.input_settings[site][1]) - print "Adding import directories - Site: " + site + " dir: "+ str(self.input_settings[site][0]) - self.do_import() - - interval=int(self.intervalEntry.get_text()) - gobject.timeout_add(interval*1000, self.do_import) + # Add directories to importer object. + for site in self.input_settings: + self.importer.addImportDirectory(self.input_settings[site][0], True, site, self.input_settings[site][1]) + print "Adding import directories - Site: " + site + " dir: "+ str(self.input_settings[site][0]) + self.do_import() + + interval=int(self.intervalEntry.get_text()) + gobject.timeout_add(interval*1000, self.do_import) + else: # toggled off + self.doAutoImportBool = False # do_import will return this and stop the gobject callback timer + #TODO: other clean up, such as killing HUD + print "Stopping autoimport" + widget.set_label(u'Start Autoimport') #end def GuiAutoImport.startClicked def get_vbox(self): From 24805700dad5f460d53b0c828dc7db3e79a20431 Mon Sep 17 00:00:00 2001 From: Matt Turnbull Date: Sat, 13 Dec 2008 01:06:26 +0000 Subject: [PATCH 02/43] StopAutoImport kills HUD --- pyfpdb/GuiAutoImport.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pyfpdb/GuiAutoImport.py b/pyfpdb/GuiAutoImport.py index e74c3888..ba09ab1e 100644 --- a/pyfpdb/GuiAutoImport.py +++ b/pyfpdb/GuiAutoImport.py @@ -40,6 +40,8 @@ class GuiAutoImport (threading.Thread): self.input_settings = {} + self.pipe_to_hud = None + self.importer = fpdb_import.Importer(self,self.settings, self.config) self.importer.setCallHud(True) self.importer.setMinPrint(30) @@ -121,9 +123,7 @@ class GuiAutoImport (threading.Thread): if widget.get_active(): # toggled on self.doAutoImportBool = True widget.set_label(u'Stop Autoimport') - try: #uhhh, I don't this this is the best way to check for the existence of an attr - getattr(self, "pipe_to_hud") - except AttributeError: + if self.pipe_to_hud is None: if os.name == 'nt': command = "python HUD_main.py" + " %s" % (self.database) bs = 0 # windows is not happy with line buffing here @@ -153,6 +153,8 @@ class GuiAutoImport (threading.Thread): self.doAutoImportBool = False # do_import will return this and stop the gobject callback timer #TODO: other clean up, such as killing HUD print "Stopping autoimport" + self.pipe_to_hud.communicate('\n') # waits for process to terminate + self.pipe_to_hud = None widget.set_label(u'Start Autoimport') #end def GuiAutoImport.startClicked From 84dfb0e967eeccbff385b5f49952faaa4b2d2cb5 Mon Sep 17 00:00:00 2001 From: eblade Date: Sat, 13 Dec 2008 12:52:25 -0500 Subject: [PATCH 03/43] looks like a whitespace churn, don't know why --- pyfpdb/fpdb_simple.py | 4434 ++++++++++++++++++++--------------------- 1 file changed, 2217 insertions(+), 2217 deletions(-) diff --git a/pyfpdb/fpdb_simple.py b/pyfpdb/fpdb_simple.py index 592e2c25..0938e00b 100755 --- a/pyfpdb/fpdb_simple.py +++ b/pyfpdb/fpdb_simple.py @@ -1,2217 +1,2217 @@ -#!/usr/bin/python - -#Copyright 2008 Steffen Jobbagy-Felso -#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 . -#In the "official" distribution you can find the license in -#agpl-3.0.txt in the docs folder of the package. - -#This file contains simple functions for fpdb - -import datetime -import re - -PS=1 -FTP=2 - -MYSQL_INNODB=2 -PGSQL=3 -SQLITE=4 - - -class DuplicateError(Exception): - def __init__(self, value): - self.value = value - def __str__(self): - return repr(self.value) - -class FpdbError(Exception): - def __init__(self, value): - self.value = value - def __str__(self): - return repr(self.value) - -# gets value for last auto-increment key generated -# returns -1 if a problem occurs -def getLastInsertId(backend, conn, cursor): - if backend == MYSQL_INNODB: - ret = conn.insert_id() - if ret < 1 or ret > 999999999: - print "getLastInsertId(): problem fetching insert_id? ret=", ret - ret = -1 - elif backend == PGSQL: - # some options: - # currval(hands_id_seq) - use name of implicit seq here - # lastval() - still needs sequences set up? - # insert ... returning is useful syntax (but postgres specific?) - # see rules (fancy trigger type things) - cursor.execute ("SELECT lastval()") - row = cursor.fetchone() - if not row: - print "getLastInsertId(%s): problem fetching lastval? row=" % seq, row - ret = -1 - else: - ret = row[0] - elif backend == SQLITE: - # don't know how to do this in sqlite - print "getLastInsertId(): not coded for sqlite yet" - ret = -1 - else: - print "getLastInsertId(): unknown backend ", backend - ret = -1 - return ret -#end def getLastInsertId - -#returns an array of the total money paid. intending to add rebuys/addons here -def calcPayin(count, buyin, fee): - result=[] - for i in range(count): - result.append (buyin+fee) - return result -#end def calcPayin - -def checkPositions(positions): - """verifies that these positions are valid""" - for i in range (len(positions)): - pos=positions[i] - try:#todo: use type recognition instead of error - if (len(pos)!=1): - raise FpdbError("invalid position found in checkPositions. i: "+str(i)+" position: "+pos) #dont need to str() here - except TypeError:#->not string->is int->fine - pass - - ### RHH modified to allow for "position 9" here (pos==9 is when you're a dead hand before the BB - if (pos!="B" and pos!="S" and pos!=0 and pos!=1 and pos!=2 and pos!=3 and pos!=4 and pos!=5 and pos!=6 and pos!=7 and pos!=9): - raise FpdbError("invalid position found in checkPositions. i: "+str(i)+" position: "+str(pos)) -#end def fpdb_simple.checkPositions - -#classifies each line for further processing in later code. Manipulates the passed arrays. -def classifyLines(hand, category, lineTypes, lineStreets): - currentStreet="predeal" - done=False #set this to true once we reach the last relevant line (the summary, except rake, is all repeats) - for i in range (len(hand)): - if (done): - if (hand[i].find("[")==-1 or hand[i].find("mucked [")==-1): - lineTypes.append("ignore") - else: #it's storing a mucked card - lineTypes.append("cards") - elif (hand[i].startswith("Dealt to")): - lineTypes.append("cards") - elif (i==0): - lineTypes.append("header") - elif (hand[i].startswith("Seat ") and ((hand[i].find("in chips")!=-1) or (hand[i].find("($")!=-1))): - lineTypes.append("name") - elif (isActionLine(hand[i])): - lineTypes.append("action") - if (hand[i].find(" posts ")!=-1 or hand[i].find(" posts the ")!=-1):#need to set this here so the "action" of posting blinds is registered properly - currentStreet="preflop" - elif (isWinLine(hand[i])): - lineTypes.append("win") - elif (hand[i].startswith("Total pot ") and hand[i].find("Rake")!=-1): - lineTypes.append("rake") - done=True - elif (hand[i]=="*** SHOW DOWN ***" or hand[i]=="*** SUMMARY ***"): - lineTypes.append("ignore") - #print "in classifyLine, showdown or summary" - elif (hand[i].find(" antes ")!=-1 or hand[i].find(" posts the ante ")!=-1): - lineTypes.append("ante") - elif (hand[i].startswith("*** FLOP *** [")): - lineTypes.append("cards") - currentStreet="flop" - elif (hand[i].startswith("*** TURN *** [")): - lineTypes.append("cards") - currentStreet="turn" - elif (hand[i].startswith("*** RIVER *** [")): - lineTypes.append("cards") - currentStreet="river" - elif (hand[i].startswith("*** 3")): - lineTypes.append("ignore") - currentStreet=0 - elif (hand[i].startswith("*** 4")): - lineTypes.append("ignore") - currentStreet=1 - elif (hand[i].startswith("*** 5")): - lineTypes.append("ignore") - currentStreet=2 - elif (hand[i].startswith("*** 6")): - lineTypes.append("ignore") - currentStreet=3 - elif (hand[i].startswith("*** 7") or hand[i]=="*** RIVER ***"): - lineTypes.append("ignore") - currentStreet=4 - elif (hand[i].find(" shows [")!=-1): - lineTypes.append("cards") - elif (hand[i].startswith("Table '")): - lineTypes.append("table") - else: - raise FpdbError("unrecognised linetype in:"+hand[i]) - lineStreets.append(currentStreet) -#end def classifyLines - -def convert3B4B(site, category, limit_type, actionTypes, actionAmounts): - """calculates the actual bet amounts in the given amount array and changes it accordingly.""" - for i in range (len(actionTypes)): - for j in range (len(actionTypes[i])): - bets=[] - for k in range (len(actionTypes[i][j])): - if (actionTypes[i][j][k]=="bet"): - bets.append((i,j,k)) - if (len(bets)==2): - #print "len(bets) 2 or higher, need to correct it. bets:",bets,"len:",len(bets) - amount2=actionAmounts[bets[1][0]][bets[1][1]][bets[1][2]] - amount1=actionAmounts[bets[0][0]][bets[0][1]][bets[0][2]] - actionAmounts[bets[1][0]][bets[1][1]][bets[1][2]]=amount2-amount1 - elif (len(bets)>2): - fail=True - #todo: run correction for below - if (site=="ps" and category=="holdem" and limit_type=="nl" and len(bets)==3): - fail=False - if (site=="ftp" and category=="omahahi" and limit_type=="pl" and len(bets)==3): - fail=False - - if fail: - print "len(bets)>2 in convert3B4B, i didnt think this is possible. i:",i,"j:",j,"k:",k - print "actionTypes:",actionTypes - raise FpdbError ("too many bets in convert3B4B") - #print "actionAmounts postConvert",actionAmounts -#end def convert3B4B(actionTypes, actionAmounts) - -#Corrects the bet amount if the player had to pay blinds -def convertBlindBet(actionTypes, actionAmounts): - i=0#setting street to pre-flop - for j in range (len(actionTypes[i])):#playerloop - blinds=[] - bets=[] - for k in range (len(actionTypes[i][j])): - if (actionTypes[i][j][k]=="blind"): - blinds.append((i,j,k)) - - if (len(blinds)>0 and actionTypes[i][j][k]=="bet"): - bets.append((i,j,k)) - if (len(bets)==1): - blind_amount=actionAmounts[blinds[0][0]][blinds[0][1]][blinds[0][2]] - bet_amount=actionAmounts[bets[0][0]][bets[0][1]][bets[0][2]] - actionAmounts[bets[0][0]][bets[0][1]][bets[0][2]]=bet_amount-blind_amount -#end def convertBlindBet - -#converts the strings in the given array to ints (changes the passed array, no returning). see table design for conversion details -#todo: make this use convertCardValuesBoard -def convertCardValues(arr): - for i in range (len(arr)): - for j in range (len(arr[i])): - if (arr[i][j]=="A"): - arr[i][j]=14 - elif (arr[i][j]=="K"): - arr[i][j]=13 - elif (arr[i][j]=="Q"): - arr[i][j]=12 - elif (arr[i][j]=="J"): - arr[i][j]=11 - elif (arr[i][j]=="T"): - arr[i][j]=10 - else: - arr[i][j]=int(arr[i][j]) -#end def convertCardValues - -#converts the strings in the given array to ints (changes the passed array, no returning). see table design for conversion details -def convertCardValuesBoard(arr): - for i in range (len(arr)): - if (arr[i]=="A"): - arr[i]=14 - elif (arr[i]=="K"): - arr[i]=13 - elif (arr[i]=="Q"): - arr[i]=12 - elif (arr[i]=="J"): - arr[i]=11 - elif (arr[i]=="T"): - arr[i]=10 - else: - arr[i]=int(arr[i]) -#end def convertCardValuesBoard - -#this creates the 2D/3D arrays. manipulates the passed arrays instead of returning. -def createArrays(category, seats, card_values, card_suits, antes, winnings, rakes, action_types, allIns, action_amounts, actionNos, actionTypeByNo): - for i in range(seats):#create second dimension arrays - tmp=[] - card_values.append(tmp) - tmp=[] - card_suits.append(tmp) - antes.append(0) - winnings.append(0) - rakes.append(0) - - if (category=="holdem" or category=="omahahi" or category=="omahahilo"): - streetCount=4 - else: - streetCount=5 - - for i in range(streetCount): #build the first dimension array, for streets - tmp=[] - action_types.append(tmp) - tmp=[] - allIns.append(tmp) - tmp=[] - action_amounts.append(tmp) - tmp=[] - actionNos.append(tmp) - tmp=[] - actionTypeByNo.append(tmp) - for j in range (seats): #second dimension arrays: players - tmp=[] - action_types[i].append(tmp) - tmp=[] - allIns[i].append(tmp) - tmp=[] - action_amounts[i].append(tmp) - tmp=[] - actionNos[i].append(tmp) - if (category=="holdem" or category=="omahahi" or category=="omahahilo"): - pass - elif (category=="razz" or category=="studhi" or category=="studhilo"):#need to fill card arrays. - for i in range(seats): - for j in range (7): - card_values[i].append(0) - card_suits[i].append("x") - else: - raise FpdbError("invalid category") -#end def createArrays - -def fill_board_cards(board_values, board_suits): -#fill up the two board card arrays - while (len(board_values)<5): - board_values.append(0) - board_suits.append("x") -#end def fill_board_cards - -def fillCardArrays(player_count, base, category, card_values, card_suits): - """fills up the two card arrays""" - if (category=="holdem"): - cardCount=2 - elif (category=="omahahi" or category=="omahahilo"): - cardCount=4 - elif base=="stud": - cardCount=7 - else: - raise fpdb_simple.FpdbError ("invalid category:", category) - - for i in range (player_count): - while (len(card_values[i])=1): - raise DuplicateError ("dupl") -#end isAlreadyInDB - -def isRebuyOrAddon(topline): - """isRebuyOrAddon not implemented yet""" - return False -#end def isRebuyOrAddon - -#returns whether the passed topline indicates a tournament or not -def isTourney(topline): - if (topline.find("Tournament")!=-1): - return True - else: - return False -#end def isTourney - -#returns boolean whether the passed line is a win line -def isWinLine(line): - if (line.find("wins the pot")!=-1): - return True - elif (line.find("ties for the high pot")!=-1): - return True - elif (line.find("ties for the high main pot")!=-1): - return True - elif (line.find("ties for the high side pot")!=-1): - return True - elif (line.find("ties for the low pot")!=-1): - return True - elif (line.find("ties for the low main pot")!=-1): - return True - elif (line.find("ties for the low side pot")!=-1): - return True - elif (line.find("ties for the main pot")!=-1): #for ftp tied main pot of split pot - return True - elif (line.find("ties for the pot")!=-1): #for ftp tie - return True - elif (line.find("ties for the side pot")!=-1): #for ftp tied split pots - return True - elif (line.find("wins side pot #")!=-1): #for ftp multi split pots - return True - elif (line.find("wins the low main pot")!=-1): - return True - elif (line.find("wins the low pot")!=-1): - return True - elif (line.find("wins the low side pot")!=-1): - return True - elif (line.find("wins the high main pot")!=-1): - return True - elif (line.find("wins the high pot")!=-1): - return True - elif (line.find("wins the high side pot")!=-1): - return True - elif (line.find("wins the main pot")!=-1): - return True - elif (line.find("wins the side pot")!=-1): #for ftp split pots - return True - elif (line.find("collected")!=-1): - return True - else: - return False #not raising error here, any unknown line wouldve been detected in isActionLine already -#end def isWinLine - -#returns the amount of cash/chips put into the put in the given action line -def parseActionAmount(line, atype, site, isTourney): - #if (line.endswith(" and is all-in")): - # line=line[:-14] - #elif (line.endswith(", and is all in")): - # line=line[:-15] - - if line.endswith(", and is capped"):#ideally we should recognise this as an all-in if category is capXl - line=line[:-15] - if line.endswith(" and is capped"): - line=line[:-14] - - - if (atype=="fold"): - amount=0 - elif (atype=="check"): - amount=0 - elif (atype=="unbet" and site=="ftp"): - pos1=line.find("$")+1 - pos2=line.find(" returned to") - amount=float2int(line[pos1:pos2]) - elif (atype=="unbet" and site=="ps"): - #print "ps unbet, line:",line - pos1=line.find("$")+1 - if pos1==0: - pos1=line.find("(")+1 - pos2=line.find(")") - amount=float2int(line[pos1:pos2]) - elif (atype=="bet" and site=="ps" and line.find(": raises $")!=-1 and line.find("to $")!=-1): - pos=line.find("to $")+4 - amount=float2int(line[pos:]) - else: - if not isTourney: - pos=line.rfind("$")+1 - #print "parseActionAmount, line:", line, "line[pos:]:", line[pos:] - amount=float2int(line[pos:]) - else: - #print "line:"+line+"EOL" - pos=line.rfind(" ")+1 - #print "pos:",pos - #print "pos of 20:", line.find("20") - amount=int(line[pos:]) - - if atype=="unbet": - amount*=-1 - return amount -#end def parseActionAmount - -#doesnt return anything, simply changes the passed arrays action_types and -# action_amounts. For stud this expects numeric streets (3-7), for -# holdem/omaha it expects predeal, preflop, flop, turn or river -def parseActionLine(site, base, isTourney, line, street, playerIDs, names, action_types, allIns, action_amounts, actionNos, actionTypeByNo): - if (street=="predeal" or street=="preflop"): - street=0 - elif (street=="flop"): - street=1 - elif (street=="turn"): - street=2 - elif (street=="river"): - street=3 - - nextActionNo=0 - for player in range(len(actionNos[street])): - for count in range(len(actionNos[street][player])): - if actionNos[street][player][count]>=nextActionNo: - nextActionNo=actionNos[street][player][count]+1 - - line, allIn=goesAllInOnThisLine(line) - atype=parseActionType(line) - playerno=recognisePlayerNo(line, names, atype) - amount=parseActionAmount(line, atype, site, isTourney) - - action_types[street][playerno].append(atype) - allIns[street][playerno].append(allIn) - action_amounts[street][playerno].append(amount) - actionNos[street][playerno].append(nextActionNo) - tmp=(playerIDs[playerno], atype) - actionTypeByNo[street].append(tmp) -#end def parseActionLine - -def goesAllInOnThisLine(line): - """returns whether the player went all-in on this line and removes the all-in text from the line.""" - isAllIn=False - if (line.endswith(" and is all-in")): - line=line[:-14] - isAllIn=True - elif (line.endswith(", and is all in")): - line=line[:-15] - isAllIn=True - return (line, isAllIn) -#end def goesAllInOnThisLine - -#returns the action type code (see table design) of the given action line -def parseActionType(line): - if (line.startswith("Uncalled bet")): - return "unbet" - elif (line.endswith("folds")): - return "fold" - elif (line.endswith("checks")): - return "check" - elif (line.find("calls")!=-1): - return "call" - elif (line.find("brings in for")!=-1): - return "blind" - elif (line.find("completes it to")!=-1): - return "bet" - #todo: what if someone completes instead of bringing in? - elif (line.find(" posts $")!=-1): - return "blind" - elif (line.find(" posts a dead ")!=-1): - return "blind" - elif (line.find(": posts small blind ")!=-1): - return "blind" - elif (line.find(" posts the small blind of $")!=-1): - return "blind" - elif (line.find(": posts big blind ")!=-1): - return "blind" - elif (line.find(" posts the big blind of $")!=-1): - return "blind" - elif (line.find(": posts small & big blinds $")!=-1): - return "blind" - #todo: seperately record voluntary blind payments made to join table out of turn - elif (line.find("bets")!=-1): - return "bet" - elif (line.find("raises")!=-1): - return "bet" - else: - raise FpdbError ("failed to recognise actiontype in parseActionLine in: "+line) -#end def parseActionType - -#parses the ante out of the given line and checks which player paid it, updates antes accordingly. -def parseAnteLine(line, site, isTourney, names, antes): - for i in range(len(names)): - if (line.startswith(names[i].encode("latin-1"))): #found the ante'er - pos=line.rfind("$")+1 - if not isTourney: - antes[i]+=float2int(line[pos:]) - else: - if line.find("all-in")==-1: - pos=line.rfind(" ")+1 - antes[i]+=int(line[pos:]) - else: - pos1=line.rfind("ante")+5 - pos2=line.find(" ",pos1) - antes[i]+=int(line[pos1:pos2]) - #print "parseAnteLine line: ", line, "antes[i]", antes[i], "antes", antes -#end def parseAntes - -#returns the buyin of a tourney in cents -def parseBuyin(topline): - pos1=topline.find("$")+1 - pos2=topline.find("+") - return float2int(topline[pos1:pos2]) -#end def parseBuyin - -#parses a card line and changes the passed arrays accordingly -#todo: reorganise this messy method -def parseCardLine(site, category, street, line, names, cardValues, cardSuits, boardValues, boardSuits): - if (line.startswith("Dealt to ") or line.find(" shows [")!=-1 or line.find("mucked [")!=-1): - playerNo=recognisePlayerNo(line, names, "card") #anything but unbet will be ok for that string - - pos=line.rfind("[")+1 - if (category=="holdem"): - for i in (pos, pos+3): - cardValues[playerNo].append(line[i:i+1]) - cardSuits[playerNo].append(line[i+1:i+2]) - if (len(cardValues[playerNo])!=2): - if cardValues[playerNo][0]==cardValues[playerNo][2] and cardSuits[playerNo][1]==cardSuits[playerNo][3]: #two tests will do - cardValues[playerNo]=cardValues[playerNo][0:2] - cardSuits[playerNo]=cardSuits[playerNo][0:2] - else: - print "line:",line,"cardValues[playerNo]:",cardValues[playerNo] - raise FpdbError("read too many/too few holecards in parseCardLine") - elif (category=="omahahi" or category=="omahahilo"): - for i in (pos, pos+3, pos+6, pos+9): - cardValues[playerNo].append(line[i:i+1]) - cardSuits[playerNo].append(line[i+1:i+2]) - if (len(cardValues[playerNo])!=4): - if cardValues[playerNo][0]==cardValues[playerNo][4] and cardSuits[playerNo][3]==cardSuits[playerNo][7]: #two tests will do - cardValues[playerNo]=cardValues[playerNo][0:4] - cardSuits[playerNo]=cardSuits[playerNo][0:4] - else: - print "line:",line,"cardValues[playerNo]:",cardValues[playerNo] - raise FpdbError("read too many/too few holecards in parseCardLine") - elif (category=="razz" or category=="studhi" or category=="studhilo"): - if (line.find("shows")==-1 and line.find("mucked")==-1): - #print "parseCardLine(in stud if), street:", street - if line[pos+2]=="]": #-> not (hero and 3rd street) - cardValues[playerNo][street+2]=line[pos:pos+1] - cardSuits[playerNo][street+2]=line[pos+1:pos+2] - else: - #print "hero card1:", line[pos:pos+2], "hero card2:", line[pos+3:pos+5], "hero card3:", line[pos+6:pos+8], - cardValues[playerNo][street]=line[pos:pos+1] - cardSuits[playerNo][street]=line[pos+1:pos+2] - cardValues[playerNo][street+1]=line[pos+3:pos+4] - cardSuits[playerNo][street+1]=line[pos+4:pos+5] - cardValues[playerNo][street+2]=line[pos+6:pos+7] - cardSuits[playerNo][street+2]=line[pos+7:pos+8] - else: - #print "parseCardLine(in stud else), street:", street - cardValues[playerNo][0]=line[pos:pos+1] - cardSuits[playerNo][0]=line[pos+1:pos+2] - pos+=3 - cardValues[playerNo][1]=line[pos:pos+1] - cardSuits[playerNo][1]=line[pos+1:pos+2] - if street==4: - pos=pos=line.rfind("]")-2 - cardValues[playerNo][6]=line[pos:pos+1] - cardSuits[playerNo][6]=line[pos+1:pos+2] - #print "cardValues:", cardValues - #print "cardSuits:", cardSuits - else: - print "line:",line,"street:",street - raise FpdbError("invalid category") - #print "end of parseCardLine/playercards, cardValues:",cardValues - elif (line.startswith("*** FLOP ***")): - pos=line.find("[")+1 - for i in (pos, pos+3, pos+6): - boardValues.append(line[i:i+1]) - boardSuits.append(line[i+1:i+2]) - #print boardValues - elif (line.startswith("*** TURN ***") or line.startswith("*** RIVER ***")): - pos=line.find("[")+1 - pos=line.find("[", pos+1)+1 - boardValues.append(line[pos:pos+1]) - boardSuits.append(line[pos+1:pos+2]) - #print boardValues - else: - raise FpdbError ("unrecognised line:"+line) -#end def parseCardLine - -def parseCashesAndSeatNos(lines, site): - """parses the startCashes and seatNos of each player out of the given lines and returns them as a dictionary of two arrays""" - cashes = [] - seatNos = [] - for i in range (len(lines)): - pos2=lines[i].find(":") - seatNos.append(int(lines[i][5:pos2])) - - pos1=lines[i].rfind("($")+2 - if pos1==1: #for tourneys - it's 1 instead of -1 due to adding 2 above - pos1=lines[i].rfind("(")+1 - if (site=="ftp"): - pos2=lines[i].rfind(")") - elif (site=="ps"): - pos2=lines[i].find(" in chips") - cashes.append(float2int(lines[i][pos1:pos2])) - return {'startCashes':cashes, 'seatNos':seatNos} -#end def parseCashesAndSeatNos - -#returns the buyin of a tourney in cents -def parseFee(topline): - pos1=topline.find("$")+1 - pos1=topline.find("$",pos1)+1 - pos2=topline.find(" ", pos1) - return float2int(topline[pos1:pos2]) -#end def parsefee - -#returns a datetime object with the starttime indicated in the given topline -def parseHandStartTime(topline, site): - #convert x:13:35 to 0x:13:35 - counter=0 - while (True): - pos=topline.find(" "+str(counter)+":") - if (pos!=-1): - topline=topline[0:pos+1]+"0"+topline[pos+1:] - counter+=1 - if counter==10: break - - isUTC=False - if site=="ftp": - pos = topline.find(" ", len(topline)-26)+1 - tmp = topline[pos:] - #print "year:", tmp[14:18], "month", tmp[19:21], "day", tmp[22:24], "hour", tmp[0:2], "minute", tmp[3:5], "second", tmp[6:8] - result = datetime.datetime(int(tmp[14:18]), int(tmp[19:21]), int(tmp[22:24]), int(tmp[0:2]), int(tmp[3:5]), int(tmp[6:8])) - elif site=="ps": - if topline.find("UTC")!=-1: - pos1 = topline.find("-")+2 - pos2 = topline.find("UTC") - tmp=topline[pos1:pos2] - isUTC=True - else: - tmp=topline[-30:] - #print "parsehandStartTime, tmp:", tmp - pos = tmp.find("-")+2 - tmp = tmp[pos:] - #Need to match either - # 2008/09/07 06:23:14 ET or - # 2008/08/17 - 01:14:43 (ET) - m = re.match('(?P[0-9]{4})\/(?P[0-9]{2})\/(?P[0-9]{2})[\- ]+(?P
[0-9]{2}):(?P[0-9]{2}):(?P[0-9]{2})',tmp) - #print "year:", int(m.group('YEAR')), "month", int(m.group('MON')), "day", int(m.group('DAY')), "hour", int(m.group('HR')), "minute", int(m.group('MIN')), "second", int(m.group('SEC')) - result = datetime.datetime(int(m.group('YEAR')), int(m.group('MON')), int(m.group('DAY')), int(m.group('HR')), int(m.group('MIN')), int(m.group('SEC'))) - else: - raise FpdbError("invalid site in parseHandStartTime") - - if (site=="ftp" or site=="ps") and not isUTC: #these use US ET - result+=datetime.timedelta(hours=5) - - return result -#end def parseHandStartTime - -#parses the names out of the given lines and returns them as an array -def parseNames(lines): - result = [] - for i in range (len(lines)): - pos1=lines[i].find(":")+2 - pos2=lines[i].rfind("(")-1 - tmp=lines[i][pos1:pos2] - #print "parseNames, tmp original:",tmp - tmp=unicode(tmp,"latin-1") - #print "parseNames, tmp after unicode latin-1 conversion:",tmp - result.append(tmp) - return result -#end def parseNames - -#returns an array with the positions of the respective players -def parsePositions (hand, names): - #prep array - positions=[] - for i in range(len(names)): - positions.append(-1) - - #find blinds - sb,bb=-1,-1 - for i in range (len(hand)): - if (sb==-1 and hand[i].find("small blind")!=-1 and hand[i].find("dead small blind")==-1): - sb=hand[i] - #print "sb:",sb - if (bb==-1 and hand[i].find("big blind")!=-1 and hand[i].find("dead big blind")==-1): - bb=hand[i] - #print "bb:",bb - - #identify blinds - #print "parsePositions before recognising sb/bb. names:",names - sbExists=True - if (sb!=-1): - sb=recognisePlayerNo(sb, names, "bet") - else: - sbExists=False - if (bb!=-1): - bb=recognisePlayerNo(bb, names, "bet") - - #write blinds into array - if (sbExists): - positions[sb]="S" - positions[bb]="B" - - #fill up rest of array - if (sbExists): - arraypos=sb-1 - else: - arraypos=bb-1 - distFromBtn=0 - while (arraypos>=0 and arraypos != bb): - #print "parsePositions first while, arraypos:",arraypos,"positions:",positions - positions[arraypos]=distFromBtn - arraypos-=1 - distFromBtn+=1 - - ### RHH - Changed to set the null seats before BB to "9" - i=bb-1 - while positions[i] < 0: - positions[i]=9 - i-=1 - - arraypos=len(names)-1 - if (bb!=0 or (bb==0 and sbExists==False)): - while (arraypos>bb): - positions[arraypos]=distFromBtn - arraypos-=1 - distFromBtn+=1 - - for i in range (len(names)): - if positions[i]==-1: - print "parsePositions names:",names - print "result:",positions - raise FpdbError ("failed to read positions") - return positions -#end def parsePositions - -#simply parses the rake amount and returns it as an int -def parseRake(line): - pos=line.find("Rake")+6 - rake=float2int(line[pos:]) - return rake -#end def parseRake - -def parseSiteHandNo(topline): - """returns the hand no assigned by the poker site""" - pos1=topline.find("#")+1 - pos2=topline.find(":") - return topline[pos1:pos2] -#end def parseSiteHandNo - -def parseTableLine(site, base, line): - """returns a dictionary with maxSeats and tableName""" - if site=="ps": - pos1=line.find('\'')+1 - pos2=line.find('\'', pos1) - #print "table:",line[pos1:pos2] - pos3=pos2+2 - pos4=line.find("-max") - #print "seats:",line[pos3:pos4] - return {'maxSeats':int(line[pos3:pos4]), 'tableName':line[pos1:pos2]} - elif site=="ftp": - pos1=line.find("Table ")+6 - pos2=line.find("-")-1 - if base=="hold": - maxSeats=9 - elif base=="stud": - maxSeats=8 - - if line.find("6 max")!=-1: - maxSeats=6 - elif line.find("4 max")!=-1: - maxSeats=4 - elif line.find("heads up")!=-1: - maxSeats=2 - - return {'maxSeats':maxSeats, 'tableName':line[pos1:pos2]} - else: - raise FpdbError("invalid site ID") -#end def parseTableLine - -#returns the hand no assigned by the poker site -def parseTourneyNo(topline): - pos1=topline.find("Tournament #")+12 - pos2=topline.find(",", pos1) - #print "parseTourneyNo pos1:",pos1," pos2:",pos2, " result:",topline[pos1:pos2] - return topline[pos1:pos2] -#end def parseTourneyNo - -#parses a win/collect line. manipulates the passed array winnings, no explicit return -def parseWinLine(line, site, names, winnings, isTourney): - #print "parseWinLine: line:",line - for i in range(len(names)): - if (line.startswith(names[i].encode("latin-1"))): #found a winner - if isTourney: - pos1=line.rfind("collected ")+10 - if (site=="ftp"): - pos2=line.find(")", pos1) - elif (site=="ps"): - pos2=line.find(" ", pos1) - winnings[i]+=int(line[pos1:pos2]) - else: - pos1=line.rfind("$")+1 - if (site=="ftp"): - pos2=line.find(")", pos1) - elif (site=="ps"): - pos2=line.find(" ", pos1) - winnings[i]+=float2int(line[pos1:pos2]) -#end def parseWinLine - -#returns the category (as per database) string for the given line -def recogniseCategory(line): - if (line.find("Razz")!=-1): - return "razz" - elif (line.find("Hold'em")!=-1): - return "holdem" - elif (line.find("Omaha")!=-1 and line.find("Hi/Lo")==-1 and line.find("H/L")==-1): - return "omahahi" - elif (line.find("Omaha")!=-1 and (line.find("Hi/Lo")!=-1 or line.find("H/L")!=-1)): - return "omahahilo" - elif (line.find("Stud")!=-1 and line.find("Hi/Lo")==-1 and line.find("H/L")==-1): - return "studhi" - elif (line.find("Stud")!=-1 and (line.find("Hi/Lo")!=-1 or line.find("H/L")!=-1)): - return "studhilo" - else: - raise FpdbError("failed to recognise category, line:"+line) -#end def recogniseCategory - -#returns the int for the gametype_id for the given line -def recogniseGametypeID(backend, db, cursor, topline, smallBlindLine, site_id, category, isTourney):#todo: this method is messy - #if (topline.find("HORSE")!=-1): - # raise FpdbError("recogniseGametypeID: HORSE is not yet supported.") - - #note: the below variable names small_bet and big_bet are misleading, in NL/PL they mean small/big blind - if isTourney: - type="tour" - pos1=topline.find("(")+1 - if (topline[pos1]=="H" or topline[pos1]=="O" or topline[pos1]=="R" or topline[pos1]=="S" or topline[pos1+2]=="C"): - pos1=topline.find("(", pos1)+1 - pos2=topline.find("/", pos1) - small_bet=int(topline[pos1:pos2]) - else: - type="ring" - pos1=topline.find("$")+1 - pos2=topline.find("/$") - small_bet=float2int(topline[pos1:pos2]) - - pos1=pos2+2 - if isTourney: - pos1-=1 - if (site_id==1): #ftp - pos2=topline.find(" ", pos1) - elif (site_id==2): #ps - pos2=topline.find(")") - - if pos2<=pos1: - pos2=topline.find(")", pos1) - - if isTourney: - big_bet=int(topline[pos1:pos2]) - else: - big_bet=float2int(topline[pos1:pos2]) - - if (topline.find("No Limit")!=-1): - limit_type="nl" - if (topline.find("Cap No")!=-1): - limit_type="cn" - elif (topline.find("Pot Limit")!=-1): - limit_type="pl" - if (topline.find("Cap Pot")!=-1): - limit_type="cp" - else: - limit_type="fl" - - #print "recogniseGametypeID small_bet/blind:",small_bet,"big bet/blind:", big_bet,"limit type:",limit_type - if (limit_type=="fl"): - cursor.execute ("SELECT id FROM Gametypes WHERE siteId=%s AND type=%s AND category=%s AND limitType=%s AND smallBet=%s AND bigBet=%s", (site_id, type, category, limit_type, small_bet, big_bet)) - else: - cursor.execute ("SELECT id FROM Gametypes WHERE siteId=%s AND type=%s AND category=%s AND limitType=%s AND smallBlind=%s AND bigBlind=%s", (site_id, type, category, limit_type, small_bet, big_bet)) - result=cursor.fetchone() - #print "recgt1 result=",result - #ret=result[0] - #print "recgt1 ret=",ret - #print "tried SELECTing gametypes.id, result:",result - - try: - len(result) - except TypeError: - if category=="holdem" or category=="omahahi" or category=="omahahilo": - base="hold" - else: - base="stud" - - if category=="holdem" or category=="omahahi" or category=="studhi": - hiLo='h' - elif category=="razz": - hiLo='l' - else: - hiLo='s' - - if (limit_type=="fl"): - big_blind=small_bet - if base=="hold": - if smallBlindLine==topline: - raise FpdbError("invalid small blind line") - elif isTourney: - pos=smallBlindLine.rfind(" ")+1 - small_blind=int(smallBlindLine[pos:]) - else: - pos=smallBlindLine.rfind("$")+1 - small_blind=float2int(smallBlindLine[pos:]) - else: - small_blind=0 - cursor.execute( """INSERT INTO Gametypes(siteId, type, base, category, limitType - ,hiLo, smallBlind, bigBlind, smallBet, bigBet) - VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""" - , (site_id, type, base, category, limit_type, hiLo - ,small_blind, big_blind, small_bet, big_bet) ) - #cursor.execute ("SELECT id FROM Gametypes WHERE siteId=%s AND type=%s AND category=%s - #AND limitType=%s AND smallBet=%s AND bigBet=%s", (site_id, type, category, limit_type, small_bet, big_bet)) - else: - cursor.execute( """INSERT INTO Gametypes(siteId, type, base, category, limitType - ,hiLo, smallBlind, bigBlind, smallBet, bigBet) - VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""" - , (site_id, type, base, category, limit_type - ,hiLo, small_bet, big_bet, 0, 0))#remember, for these bet means blind - #cursor.execute ("SELECT id FROM Gametypes WHERE siteId=%s AND type=%s AND category=%s - #AND limitType=%s AND smallBlind=%s AND bigBlind=%s", (site_id, type, category, limit_type, small_bet, big_bet)) - - #result=(db.insert_id(),) - result=(getLastInsertId(backend,db,cursor),) - - return result[0] -#end def recogniseGametypeID - -def recogniseTourneyTypeId(cursor, siteId, buyin, fee, knockout, rebuyOrAddon): - cursor.execute ("SELECT id FROM TourneyTypes WHERE siteId=%s AND buyin=%s AND fee=%s AND knockout=%s AND rebuyOrAddon=%s", (siteId, buyin, fee, knockout, rebuyOrAddon)) - result=cursor.fetchone() - #print "tried SELECTing gametypes.id, result:",result - - try: - len(result) - except TypeError:#this means we need to create a new entry - cursor.execute("""INSERT INTO TourneyTypes (siteId, buyin, fee, knockout, rebuyOrAddon) VALUES (%s, %s, %s, %s, %s)""", (siteId, buyin, fee, knockout, rebuyOrAddon)) - cursor.execute("SELECT id FROM TourneyTypes WHERE siteId=%s AND buyin=%s AND fee=%s AND knockout=%s AND rebuyOrAddon=%s", (siteId, buyin, fee, knockout, rebuyOrAddon)) - result=cursor.fetchone() - return result[0] -#end def recogniseTourneyTypeId - -#returns the SQL ids of the names given in an array -def recognisePlayerIDs(cursor, names, site_id): - result = [] - for i in range (len(names)): - cursor.execute ("SELECT id FROM Players WHERE name=%s", (names[i],)) - tmp=cursor.fetchall() - if (len(tmp)==0): #new player - cursor.execute ("INSERT INTO Players (name, siteId) VALUES (%s, %s)", (names[i], site_id)) - #print "Number of players rows inserted: %d" % cursor.rowcount - cursor.execute ("SELECT id FROM Players WHERE name=%s", (names[i],)) - tmp=cursor.fetchall() - #print "recognisePlayerIDs, names[i]:",names[i],"tmp:",tmp - result.append(tmp[0][0]) - return result -#end def recognisePlayerIDs - -#recognises the name in the given line and returns its array position in the given array -def recognisePlayerNo(line, names, atype): - #print "recogniseplayerno, names:",names - for i in range (len(names)): - if (atype=="unbet"): - if (line.endswith(names[i].encode("latin-1"))): - return (i) - elif (line.startswith("Dealt to ")): - #print "recognisePlayerNo, card precut, line:",line - tmp=line[9:] - #print "recognisePlayerNo, card postcut, tmp:",tmp - if (tmp.startswith(names[i].encode("latin-1"))): - return (i) - elif (line.startswith("Seat ")): - if (line.startswith("Seat 10")): - tmp=line[9:] - else: - tmp=line[8:] - - if (tmp.startswith(names[i].encode("latin-1"))): - return (i) - else: - if (line.startswith(names[i].encode("latin-1"))): - return (i) - #if we're here we mustve failed - raise FpdbError ("failed to recognise player in: "+line+" atype:"+atype) -#end def recognisePlayerNo - -#returns the site abbreviation for the given site -def recogniseSite(line): - if (line.startswith("Full Tilt Poker")): - return "ftp" - elif (line.startswith("PokerStars")): - return "ps" - else: - raise FpdbError("failed to recognise site, line:"+line) -#end def recogniseSite - -#returns the ID of the given site -def recogniseSiteID(cursor, site): - if (site=="ftp"): - return 1 - #cursor.execute("SELECT id FROM Sites WHERE name = ('Full Tilt Poker')") - elif (site=="ps"): - return 2 - #cursor.execute("SELECT id FROM Sites WHERE name = ('PokerStars')") - else: - raise FpdbError("invalid site in recogniseSiteID: "+site) - return cursor.fetchall()[0][0] -#end def recogniseSiteID - -#removes trailing \n from the given array -def removeTrailingEOL(arr): - for i in range(len(arr)): - if (arr[i].endswith("\n")): - #print "arr[i] before removetrailingEOL:", arr[i] - arr[i]=arr[i][:-1] - #print "arr[i] after removetrailingEOL:", arr[i] - return arr -#end def removeTrailingEOL - -#splits the rake according to the proportion of pot won. manipulates the second passed array. -def splitRake(winnings, rakes, totalRake): - winnercnt=0 - totalWin=0 - for i in range(len(winnings)): - if winnings[i]!=0: - winnercnt+=1 - totalWin+=winnings[i] - firstWinner=i - if winnercnt==1: - rakes[firstWinner]=totalRake - else: - totalWin=float(totalWin) - for i in range(len(winnings)): - if winnings[i]!=0: - winPortion=winnings[i]/totalWin - rakes[i]=totalRake*winPortion -#end def splitRake - -def storeActions(cursor, handsPlayersIds, actionTypes, allIns, actionAmounts, actionNos): -#stores into table hands_actions - #print "start of storeActions, actionNos:",actionNos - #print " action_amounts:",action_amounts - for i in range (len(actionTypes)): #iterate through streets - for j in range (len(actionTypes[i])): #iterate through names - for k in range (len(actionTypes[i][j])): #iterate through individual actions of that player on that street - cursor.execute ("INSERT INTO HandsActions (handPlayerId, street, actionNo, action, allIn, amount) VALUES (%s, %s, %s, %s, %s, %s)" - , (handsPlayersIds[j], i, actionNos[i][j][k], actionTypes[i][j][k], allIns[i][j][k], actionAmounts[i][j][k])) -#end def storeActions - -def store_board_cards(cursor, hands_id, board_values, board_suits): -#stores into table board_cards - cursor.execute ("""INSERT INTO BoardCards (handId, card1Value, card1Suit, -card2Value, card2Suit, card3Value, card3Suit, card4Value, card4Suit, -card5Value, card5Suit) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""", - (hands_id, board_values[0], board_suits[0], board_values[1], board_suits[1], - board_values[2], board_suits[2], board_values[3], board_suits[3], - board_values[4], board_suits[4])) -#end def store_board_cards - -def storeHands(backend, conn, cursor, site_hand_no, gametype_id - ,hand_start_time, names, tableName, maxSeats): -#stores into table hands - cursor.execute ("INSERT INTO Hands (siteHandNo, gametypeId, handStart, seats, tableName, importTime, maxSeats) VALUES (%s, %s, %s, %s, %s, %s, %s)", (site_hand_no, gametype_id, hand_start_time, len(names), tableName, datetime.datetime.today(), maxSeats)) - #todo: find a better way of doing this... - #cursor.execute("SELECT id FROM Hands WHERE siteHandNo=%s AND gametypeId=%s", (site_hand_no, gametype_id)) - #return cursor.fetchall()[0][0] - return getLastInsertId(backend, conn, cursor) - #return db.insert_id() # mysql only -#end def storeHands - -def store_hands_players_holdem_omaha(backend, conn, cursor, category, hands_id, player_ids, start_cashes - ,positions, card_values, card_suits, winnings, rakes, seatNos): - result=[] - if (category=="holdem"): - for i in range (len(player_ids)): - cursor.execute (""" -INSERT INTO HandsPlayers -(handId, playerId, startCash, position, -card1Value, card1Suit, card2Value, card2Suit, winnings, rake, seatNo) -VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""", - (hands_id, player_ids[i], start_cashes[i], positions[i], - card_values[i][0], card_suits[i][0], card_values[i][1], card_suits[i][1], - winnings[i], rakes[i], seatNos[i])) - #cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId=%s", (hands_id, player_ids[i])) - #result.append(cursor.fetchall()[0][0]) - result.append( getLastInsertId(backend, conn, cursor) ) # mysql only - elif (category=="omahahi" or category=="omahahilo"): - for i in range (len(player_ids)): - cursor.execute ("""INSERT INTO HandsPlayers -(handId, playerId, startCash, position, -card1Value, card1Suit, card2Value, card2Suit, -card3Value, card3Suit, card4Value, card4Suit, winnings, rake, seatNo) -VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""", - (hands_id, player_ids[i], start_cashes[i], positions[i], - card_values[i][0], card_suits[i][0], card_values[i][1], card_suits[i][1], - card_values[i][2], card_suits[i][2], card_values[i][3], card_suits[i][3], - winnings[i], rakes[i], seatNos[i])) - #cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[i])) - #result.append(cursor.fetchall()[0][0]) - result.append( getLastInsertId(backend, conn, cursor) ) # mysql only - else: - raise FpdbError("invalid category") - return result -#end def store_hands_players_holdem_omaha - -def store_hands_players_stud(backend, conn, cursor, hands_id, player_ids, start_cashes, antes, - card_values, card_suits, winnings, rakes, seatNos): -#stores hands_players rows for stud/razz games. returns an array of the resulting IDs - result=[] - #print "before inserts in store_hands_players_stud, antes:", antes - for i in range (len(player_ids)): - cursor.execute ("""INSERT INTO HandsPlayers -(handId, playerId, startCash, ante, -card1Value, card1Suit, card2Value, card2Suit, -card3Value, card3Suit, card4Value, card4Suit, -card5Value, card5Suit, card6Value, card6Suit, -card7Value, card7Suit, winnings, rake, seatNo) -VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, -%s, %s, %s, %s)""", - (hands_id, player_ids[i], start_cashes[i], antes[i], - card_values[i][0], card_suits[i][0], card_values[i][1], card_suits[i][1], - card_values[i][2], card_suits[i][2], card_values[i][3], card_suits[i][3], - card_values[i][4], card_suits[i][4], card_values[i][5], card_suits[i][5], - card_values[i][6], card_suits[i][6], winnings[i], rakes[i], seatNos[i])) - #cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[i])) - #result.append(cursor.fetchall()[0][0]) - result.append( getLastInsertId(backend, conn, cursor) ) # mysql only - return result -#end def store_hands_players_stud - -def store_hands_players_holdem_omaha_tourney(backend, conn, cursor, category, hands_id, player_ids - ,start_cashes, positions, card_values, card_suits - , winnings, rakes, seatNos, tourneys_players_ids): - #stores hands_players for tourney holdem/omaha hands - result=[] - for i in range (len(player_ids)): - if len(card_values[0])==2: - cursor.execute ("""INSERT INTO HandsPlayers -(handId, playerId, startCash, position, -card1Value, card1Suit, card2Value, card2Suit, -winnings, rake, tourneysPlayersId, seatNo) -VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""", - (hands_id, player_ids[i], start_cashes[i], positions[i], - card_values[i][0], card_suits[i][0], card_values[i][1], card_suits[i][1], - winnings[i], rakes[i], tourneys_players_ids[i], seatNos[i])) - elif len(card_values[0])==4: - cursor.execute ("""INSERT INTO HandsPlayers -(handId, playerId, startCash, position, -card1Value, card1Suit, card2Value, card2Suit, -card3Value, card3Suit, card4Value, card4Suit, -winnings, rake, tourneysPlayersId, seatNo) -VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""", - (hands_id, player_ids[i], start_cashes[i], positions[i], - card_values[i][0], card_suits[i][0], card_values[i][1], card_suits[i][1], - card_values[i][2], card_suits[i][2], card_values[i][3], card_suits[i][3], - winnings[i], rakes[i], tourneys_players_ids[i], seatNos[i])) - else: - raise FpdbError ("invalid card_values length:"+str(len(card_values[0]))) - #cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[i])) - #result.append(cursor.fetchall()[0][0]) - result.append( getLastInsertId(backend, conn, cursor) ) # mysql only - - return result -#end def store_hands_players_holdem_omaha_tourney - -def store_hands_players_stud_tourney(backend, conn, cursor, hands_id, player_ids, start_cashes, - antes, card_values, card_suits, winnings, rakes, seatNos, tourneys_players_ids): -#stores hands_players for tourney stud/razz hands - result=[] - for i in range (len(player_ids)): - cursor.execute ("""INSERT INTO HandsPlayers -(handId, playerId, startCash, ante, -card1Value, card1Suit, card2Value, card2Suit, -card3Value, card3Suit, card4Value, card4Suit, -card5Value, card5Suit, card6Value, card6Suit, -card7Value, card7Suit, winnings, rake, tourneysPlayersId, seatNo) -VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, -%s, %s, %s, %s, %s, %s)""", - (hands_id, player_ids[i], start_cashes[i], antes[i], - card_values[i][0], card_suits[i][0], card_values[i][1], card_suits[i][1], - card_values[i][2], card_suits[i][2], card_values[i][3], card_suits[i][3], - card_values[i][4], card_suits[i][4], card_values[i][5], card_suits[i][5], - card_values[i][6], card_suits[i][6], winnings[i], rakes[i], tourneys_players_ids[i], seatNos[i])) - #cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[i])) - #result.append(cursor.fetchall()[0][0]) - result.append( getLastInsertId(backend, conn, cursor) ) # mysql only - return result -#end def store_hands_players_stud_tourney - -def generateHudCacheData(player_ids, base, category, action_types, allIns, actionTypeByNo - ,winnings, totalWinnings, positions, actionTypes, actionAmounts): - """calculates data for the HUD during import. IMPORTANT: if you change this method make -sure to also change the following storage method and table_viewer.prepare_data if necessary -""" - #print "generateHudCacheData, len(player_ids)=", len(player_ids) - #setup subarrays of the result dictionary. - street0VPI=[] - street0Aggr=[] - street0_3B4BChance=[] - street0_3B4BDone=[] - street1Seen=[] - street2Seen=[] - street3Seen=[] - street4Seen=[] - sawShowdown=[] - street1Aggr=[] - street2Aggr=[] - street3Aggr=[] - street4Aggr=[] - otherRaisedStreet1=[] - otherRaisedStreet2=[] - otherRaisedStreet3=[] - otherRaisedStreet4=[] - foldToOtherRaisedStreet1=[] - foldToOtherRaisedStreet2=[] - foldToOtherRaisedStreet3=[] - foldToOtherRaisedStreet4=[] - wonWhenSeenStreet1=[] - - wonAtSD=[] - stealAttemptChance=[] - stealAttempted=[] - hudDataPositions=[] - - firstPfRaiseByNo=-1 - firstPfRaiserId=-1 - firstPfRaiserNo=-1 - firstPfCallByNo=-1 - firstPfCallerId=-1 - for i in range(len(actionTypeByNo[0])): - if actionTypeByNo[0][i][1]=="bet": - firstPfRaiseByNo=i - firstPfRaiserId=actionTypeByNo[0][i][0] - for j in range(len(player_ids)): - if player_ids[j]==firstPfRaiserId: - firstPfRaiserNo=j - break - break - for i in range(len(actionTypeByNo[0])): - if actionTypeByNo[0][i][1]=="call": - firstPfCallByNo=i - firstPfCallerId=actionTypeByNo[0][i][0] - break - - cutoffId=-1 - buttonId=-1 - sbId=-1 - bbId=-1 - if base=="hold": - for player in range(len(positions)): - if positions==1: - cutoffId=player_ids[player] - if positions==0: - buttonId=player_ids[player] - if positions=='S': - sbId=player_ids[player] - if positions=='B': - bbId=player_ids[player] - - someoneStole=False - - #run a loop for each player preparing the actual values that will be commited to SQL - for player in range (len(player_ids)): - #set default values - myStreet0VPI=False - myStreet0Aggr=False - myStreet0_3B4BChance=False - myStreet0_3B4BDone=False - myStreet1Seen=False - myStreet2Seen=False - myStreet3Seen=False - myStreet4Seen=False - mySawShowdown=False - myStreet1Aggr=False - myStreet2Aggr=False - myStreet3Aggr=False - myStreet4Aggr=False - myOtherRaisedStreet1=False - myOtherRaisedStreet2=False - myOtherRaisedStreet3=False - myOtherRaisedStreet4=False - myFoldToOtherRaisedStreet1=False - myFoldToOtherRaisedStreet2=False - myFoldToOtherRaisedStreet3=False - myFoldToOtherRaisedStreet4=False - myWonWhenSeenStreet1=0.0 - myWonAtSD=0.0 - myStealAttemptChance=False - myStealAttempted=False - - #calculate VPIP and PFR - street=0 - heroPfRaiseCount=0 - for count in range (len(action_types[street][player])):#finally individual actions - currentAction=action_types[street][player][count] - if currentAction=="bet": - myStreet0Aggr=True - if (currentAction=="bet" or currentAction=="call"): - myStreet0VPI=True - - #PF3B4BChance and PF3B4B - pfFold=-1 - pfRaise=-1 - if firstPfRaiseByNo!=-1: - for i in range(len(actionTypeByNo[0])): - if actionTypeByNo[0][i][0]==player_ids[player]: - if actionTypeByNo[0][i][1]=="bet" and pfRaise==-1 and i>firstPfRaiseByNo: - pfRaise=i - if actionTypeByNo[0][i][1]=="fold" and pfFold==-1: - pfFold=i - if pfFold==-1 or pfFold>firstPfRaiseByNo: - myStreet0_3B4BChance=True - if pfRaise>firstPfRaiseByNo: - myStreet0_3B4BDone=True - - #steal calculations - if base=="hold": - if len(player_ids)>=5: #no point otherwise - if positions[player]==1: - if firstPfRaiserId==player_ids[player]: - myStealAttemptChance=True - myStealAttempted=True - elif firstPfRaiserId==buttonId or firstPfRaiserId==sbId or firstPfRaiserId==bbId or firstPfRaiserId==-1: - myStealAttemptChance=True - if positions[player]==0: - if firstPfRaiserId==player_ids[player]: - myStealAttemptChance=True - myStealAttempted=True - elif firstPfRaiserId==sbId or firstPfRaiserId==bbId or firstPfRaiserId==-1: - myStealAttemptChance=True - if positions[player]=='S': - if firstPfRaiserId==player_ids[player]: - myStealAttemptChance=True - myStealAttempted=True - elif firstPfRaiserId==bbId or firstPfRaiserId==-1: - myStealAttemptChance=True - if positions[player]=='B': - pass - - if myStealAttempted: - someoneStole=True - - - #calculate saw* values - isAllIn=False - for i in range(len(allIns[0][player])): - if allIns[0][player][i]: - isAllIn=True - if (len(action_types[1][player])>0 or isAllIn): - myStreet1Seen=True - - for i in range(len(allIns[1][player])): - if allIns[1][player][i]: - isAllIn=True - if (len(action_types[2][player])>0 or isAllIn): - myStreet2Seen=True - - for i in range(len(allIns[2][player])): - if allIns[2][player][i]: - isAllIn=True - if (len(action_types[3][player])>0 or isAllIn): - myStreet3Seen=True - - #print "base:", base - if base=="hold": - mySawShowdown=True - for count in range (len(action_types[3][player])): - if action_types[3][player][count]=="fold": - mySawShowdown=False - else: - #print "in else" - for i in range(len(allIns[3][player])): - if allIns[3][player][i]: - isAllIn=True - if (len(action_types[4][player])>0 or isAllIn): - #print "in if" - myStreet4Seen=True - - mySawShowdown=True - for count in range (len(action_types[4][player])): - if action_types[4][player][count]=="fold": - mySawShowdown=False - - - #flop stuff - street=1 - if myStreet1Seen: - for count in range(len(action_types[street][player])): - if action_types[street][player][count]=="bet": - myStreet1Aggr=True - - for otherPlayer in range (len(player_ids)): - if player==otherPlayer: - pass - else: - for countOther in range (len(action_types[street][otherPlayer])): - if action_types[street][otherPlayer][countOther]=="bet": - myOtherRaisedStreet1=True - for countOtherFold in range (len(action_types[street][player])): - if action_types[street][player][countOtherFold]=="fold": - myFoldToOtherRaisedStreet1=True - - #turn stuff - copy of flop with different vars - street=2 - if myStreet2Seen: - for count in range(len(action_types[street][player])): - if action_types[street][player][count]=="bet": - myStreet2Aggr=True - - for otherPlayer in range (len(player_ids)): - if player==otherPlayer: - pass - else: - for countOther in range (len(action_types[street][otherPlayer])): - if action_types[street][otherPlayer][countOther]=="bet": - myOtherRaisedStreet2=True - for countOtherFold in range (len(action_types[street][player])): - if action_types[street][player][countOtherFold]=="fold": - myFoldToOtherRaisedStreet2=True - - #river stuff - copy of flop with different vars - street=3 - if myStreet3Seen: - for count in range(len(action_types[street][player])): - if action_types[street][player][count]=="bet": - myStreet3Aggr=True - - for otherPlayer in range (len(player_ids)): - if player==otherPlayer: - pass - else: - for countOther in range (len(action_types[street][otherPlayer])): - if action_types[street][otherPlayer][countOther]=="bet": - myOtherRaisedStreet3=True - for countOtherFold in range (len(action_types[street][player])): - if action_types[street][player][countOtherFold]=="fold": - myFoldToOtherRaisedStreet3=True - - #stud river stuff - copy of flop with different vars - street=4 - if myStreet4Seen: - for count in range(len(action_types[street][player])): - if action_types[street][player][count]=="bet": - myStreet4Aggr=True - - for otherPlayer in range (len(player_ids)): - if player==otherPlayer: - pass - else: - for countOther in range (len(action_types[street][otherPlayer])): - if action_types[street][otherPlayer][countOther]=="bet": - myOtherRaisedStreet4=True - for countOtherFold in range (len(action_types[street][player])): - if action_types[street][player][countOtherFold]=="fold": - myFoldToOtherRaisedStreet4=True - - if winnings[player]!=0: - if myStreet1Seen: - myWonWhenSeenStreet1=winnings[player]/float(totalWinnings) - if mySawShowdown: - myWonAtSD=myWonWhenSeenStreet1 - - #add each value to the appropriate array - street0VPI.append(myStreet0VPI) - street0Aggr.append(myStreet0Aggr) - street0_3B4BChance.append(myStreet0_3B4BChance) - street0_3B4BDone.append(myStreet0_3B4BDone) - street1Seen.append(myStreet1Seen) - street2Seen.append(myStreet2Seen) - street3Seen.append(myStreet3Seen) - street4Seen.append(myStreet4Seen) - sawShowdown.append(mySawShowdown) - street1Aggr.append(myStreet1Aggr) - street2Aggr.append(myStreet2Aggr) - street3Aggr.append(myStreet3Aggr) - street4Aggr.append(myStreet4Aggr) - otherRaisedStreet1.append(myOtherRaisedStreet1) - otherRaisedStreet2.append(myOtherRaisedStreet2) - otherRaisedStreet3.append(myOtherRaisedStreet3) - otherRaisedStreet4.append(myOtherRaisedStreet4) - foldToOtherRaisedStreet1.append(myFoldToOtherRaisedStreet1) - foldToOtherRaisedStreet2.append(myFoldToOtherRaisedStreet2) - foldToOtherRaisedStreet3.append(myFoldToOtherRaisedStreet3) - foldToOtherRaisedStreet4.append(myFoldToOtherRaisedStreet4) - wonWhenSeenStreet1.append(myWonWhenSeenStreet1) - wonAtSD.append(myWonAtSD) - stealAttemptChance.append(myStealAttemptChance) - stealAttempted.append(myStealAttempted) - if base=="hold": - pos=positions[player] - if pos=='B': - hudDataPositions.append('B') - elif pos=='S': - hudDataPositions.append('S') - elif pos==0: - hudDataPositions.append('D') - elif pos==1: - hudDataPositions.append('C') - elif pos>=2 and pos<=4: - hudDataPositions.append('M') - elif pos>=5 and pos<=7: - hudDataPositions.append('E') - ### RHH Added this elif to handle being a dead hand before the BB (pos==9) - elif pos==9: - hudDataPositions.append('X') - else: - raise FpdbError("invalid position") - elif base=="stud": - #todo: stud positions and steals - pass - - #add each array to the to-be-returned dictionary - result={'street0VPI':street0VPI} - result['street0Aggr']=street0Aggr - result['street0_3B4BChance']=street0_3B4BChance - result['street0_3B4BDone']=street0_3B4BDone - result['street1Seen']=street1Seen - result['street2Seen']=street2Seen - result['street3Seen']=street3Seen - result['street4Seen']=street4Seen - result['sawShowdown']=sawShowdown - - result['street1Aggr']=street1Aggr - result['otherRaisedStreet1']=otherRaisedStreet1 - result['foldToOtherRaisedStreet1']=foldToOtherRaisedStreet1 - result['street2Aggr']=street2Aggr - result['otherRaisedStreet2']=otherRaisedStreet2 - result['foldToOtherRaisedStreet2']=foldToOtherRaisedStreet2 - result['street3Aggr']=street3Aggr - result['otherRaisedStreet3']=otherRaisedStreet3 - result['foldToOtherRaisedStreet3']=foldToOtherRaisedStreet3 - result['street4Aggr']=street4Aggr - result['otherRaisedStreet4']=otherRaisedStreet4 - result['foldToOtherRaisedStreet4']=foldToOtherRaisedStreet4 - result['wonWhenSeenStreet1']=wonWhenSeenStreet1 - result['wonAtSD']=wonAtSD - result['stealAttemptChance']=stealAttemptChance - result['stealAttempted']=stealAttempted - - #now the various steal values - foldBbToStealChance=[] - foldedBbToSteal=[] - foldSbToStealChance=[] - foldedSbToSteal=[] - for player in range (len(player_ids)): - myFoldBbToStealChance=False - myFoldedBbToSteal=False - myFoldSbToStealChance=False - myFoldedSbToSteal=False - - if base=="hold": - if someoneStole and (positions[player]=='B' or positions[player]=='S') and firstPfRaiserId!=player_ids[player]: - street=0 - for count in range (len(action_types[street][player])):#individual actions - if positions[player]=='B': - myFoldBbToStealChance=True - if action_types[street][player][count]=="fold": - myFoldedBbToSteal=True - if positions[player]=='S': - myFoldSbToStealChance=True - if action_types[street][player][count]=="fold": - myFoldedSbToSteal=True - - - foldBbToStealChance.append(myFoldBbToStealChance) - foldedBbToSteal.append(myFoldedBbToSteal) - foldSbToStealChance.append(myFoldSbToStealChance) - foldedSbToSteal.append(myFoldedSbToSteal) - result['foldBbToStealChance']=foldBbToStealChance - result['foldedBbToSteal']=foldedBbToSteal - result['foldSbToStealChance']=foldSbToStealChance - result['foldedSbToSteal']=foldedSbToSteal - - #now CB - street1CBChance=[] - street1CBDone=[] - didStreet1CB=[] - for player in range (len(player_ids)): - myStreet1CBChance=False - myStreet1CBDone=False - - if street0VPI[player]: - myStreet1CBChance=True - if street1Aggr[player]: - myStreet1CBDone=True - didStreet1CB.append(player_ids[player]) - - street1CBChance.append(myStreet1CBChance) - street1CBDone.append(myStreet1CBDone) - result['street1CBChance']=street1CBChance - result['street1CBDone']=street1CBDone - - #now 2B - street2CBChance=[] - street2CBDone=[] - didStreet2CB=[] - for player in range (len(player_ids)): - myStreet2CBChance=False - myStreet2CBDone=False - - if street1CBDone[player]: - myStreet2CBChance=True - if street2Aggr[player]: - myStreet2CBDone=True - didStreet2CB.append(player_ids[player]) - - street2CBChance.append(myStreet2CBChance) - street2CBDone.append(myStreet2CBDone) - result['street2CBChance']=street2CBChance - result['street2CBDone']=street2CBDone - - #now 3B - street3CBChance=[] - street3CBDone=[] - didStreet3CB=[] - for player in range (len(player_ids)): - myStreet3CBChance=False - myStreet3CBDone=False - - if street2CBDone[player]: - myStreet3CBChance=True - if street3Aggr[player]: - myStreet3CBDone=True - didStreet3CB.append(player_ids[player]) - - street3CBChance.append(myStreet3CBChance) - street3CBDone.append(myStreet3CBDone) - result['street3CBChance']=street3CBChance - result['street3CBDone']=street3CBDone - - #and 4B - street4CBChance=[] - street4CBDone=[] - didStreet4CB=[] - for player in range (len(player_ids)): - myStreet4CBChance=False - myStreet4CBDone=False - - if street3CBDone[player]: - myStreet4CBChance=True - if street4Aggr[player]: - myStreet4CBDone=True - didStreet4CB.append(player_ids[player]) - - street4CBChance.append(myStreet4CBChance) - street4CBDone.append(myStreet4CBDone) - result['street4CBChance']=street4CBChance - result['street4CBDone']=street4CBDone - - - result['position']=hudDataPositions - - foldToStreet1CBChance=[] - foldToStreet1CBDone=[] - foldToStreet2CBChance=[] - foldToStreet2CBDone=[] - foldToStreet3CBChance=[] - foldToStreet3CBDone=[] - foldToStreet4CBChance=[] - foldToStreet4CBDone=[] - - for player in range (len(player_ids)): - myFoldToStreet1CBChance=False - myFoldToStreet1CBDone=False - foldToStreet1CBChance.append(myFoldToStreet1CBChance) - foldToStreet1CBDone.append(myFoldToStreet1CBDone) - - myFoldToStreet2CBChance=False - myFoldToStreet2CBDone=False - foldToStreet2CBChance.append(myFoldToStreet2CBChance) - foldToStreet2CBDone.append(myFoldToStreet2CBDone) - - myFoldToStreet3CBChance=False - myFoldToStreet3CBDone=False - foldToStreet3CBChance.append(myFoldToStreet3CBChance) - foldToStreet3CBDone.append(myFoldToStreet3CBDone) - - myFoldToStreet4CBChance=False - myFoldToStreet4CBDone=False - foldToStreet4CBChance.append(myFoldToStreet4CBChance) - foldToStreet4CBDone.append(myFoldToStreet4CBDone) - - if len(didStreet1CB)>=1: - generateFoldToCB(1, player_ids, didStreet1CB, street1CBDone, foldToStreet1CBChance, foldToStreet1CBDone, actionTypeByNo) - - if len(didStreet2CB)>=1: - generateFoldToCB(2, player_ids, didStreet2CB, street2CBDone, foldToStreet2CBChance, foldToStreet2CBDone, actionTypeByNo) - - if len(didStreet3CB)>=1: - generateFoldToCB(3, player_ids, didStreet3CB, street3CBDone, foldToStreet3CBChance, foldToStreet3CBDone, actionTypeByNo) - - if len(didStreet4CB)>=1: - generateFoldToCB(4, player_ids, didStreet4CB, street4CBDone, foldToStreet4CBChance, foldToStreet4CBDone, actionTypeByNo) - - result['foldToStreet1CBChance']=foldToStreet1CBChance - result['foldToStreet1CBDone']=foldToStreet1CBDone - result['foldToStreet2CBChance']=foldToStreet2CBChance - result['foldToStreet2CBDone']=foldToStreet2CBDone - result['foldToStreet3CBChance']=foldToStreet3CBChance - result['foldToStreet3CBDone']=foldToStreet3CBDone - result['foldToStreet4CBChance']=foldToStreet4CBChance - result['foldToStreet4CBDone']=foldToStreet4CBDone - - - totalProfit=[] - - street1CheckCallRaiseChance=[] - street1CheckCallRaiseDone=[] - street2CheckCallRaiseChance=[] - street2CheckCallRaiseDone=[] - street3CheckCallRaiseChance=[] - street3CheckCallRaiseDone=[] - street4CheckCallRaiseChance=[] - street4CheckCallRaiseDone=[] - #print "b4 totprof calc, len(playerIds)=", len(player_ids) - for pl in range (len(player_ids)): - #print "pl=", pl - myTotalProfit=winnings[pl] # still need to deduct costs - for i in range (len(actionTypes)): #iterate through streets - #for j in range (len(actionTypes[i])): #iterate through names (using pl loop above) - for k in range (len(actionTypes[i][pl])): #iterate through individual actions of that player on that street - myTotalProfit -= actionAmounts[i][pl][k] - - myStreet1CheckCallRaiseChance=False - myStreet1CheckCallRaiseDone=False - myStreet2CheckCallRaiseChance=False - myStreet2CheckCallRaiseDone=False - myStreet3CheckCallRaiseChance=False - myStreet3CheckCallRaiseDone=False - myStreet4CheckCallRaiseChance=False - myStreet4CheckCallRaiseDone=False - - #print "myTotalProfit=", myTotalProfit - totalProfit.append(myTotalProfit) - #print "totalProfit[]=", totalProfit - - street1CheckCallRaiseChance.append(myStreet1CheckCallRaiseChance) - street1CheckCallRaiseDone.append(myStreet1CheckCallRaiseDone) - street2CheckCallRaiseChance.append(myStreet2CheckCallRaiseChance) - street2CheckCallRaiseDone.append(myStreet2CheckCallRaiseDone) - street3CheckCallRaiseChance.append(myStreet3CheckCallRaiseChance) - street3CheckCallRaiseDone.append(myStreet3CheckCallRaiseDone) - street4CheckCallRaiseChance.append(myStreet4CheckCallRaiseChance) - street4CheckCallRaiseDone.append(myStreet4CheckCallRaiseDone) - - result['totalProfit']=totalProfit - #print "res[totalProfit]=", result['totalProfit'] - - result['street1CheckCallRaiseChance']=street1CheckCallRaiseChance - result['street1CheckCallRaiseDone']=street1CheckCallRaiseDone - result['street2CheckCallRaiseChance']=street2CheckCallRaiseChance - result['street2CheckCallRaiseDone']=street2CheckCallRaiseDone - result['street3CheckCallRaiseChance']=street3CheckCallRaiseChance - result['street3CheckCallRaiseDone']=street3CheckCallRaiseDone - result['street4CheckCallRaiseChance']=street4CheckCallRaiseChance - result['street4CheckCallRaiseDone']=street4CheckCallRaiseDone - return result -#end def generateHudCacheData - -def generateFoldToCB(street, playerIDs, didStreetCB, streetCBDone, foldToStreetCBChance, foldToStreetCBDone, actionTypeByNo): - """fills the passed foldToStreetCB* arrays appropriately depending on the given street""" - #print "beginning of generateFoldToCB, street:", street, "len(actionTypeByNo):", len(actionTypeByNo) - #print "len(actionTypeByNo[street]):",len(actionTypeByNo[street]) - firstCBReaction=0 - for action in range(len(actionTypeByNo[street])): - if actionTypeByNo[street][action][1]=="bet": - for player in didStreetCB: - if player==actionTypeByNo[street][action][0] and firstCBReaction==0: - firstCBReaction=action+1 - break - - for action in actionTypeByNo[street][firstCBReaction:]: - for player in range(len(playerIDs)): - if playerIDs[player]==action[0]: - foldToStreetCBChance[player]=True - if action[1]=="fold": - foldToStreetCBDone[player]=True -#end def generateFoldToCB - -def storeHudCache(cursor, base, category, gametypeId, playerIds, hudImportData): -# if (category=="holdem" or category=="omahahi" or category=="omahahilo"): - - #print "storeHudCache, len(playerIds)=", len(playerIds), " len(vpip)=" \ - #, len(hudImportData['street0VPI']), " len(totprof)=", len(hudImportData['totalProfit']) - for player in range (len(playerIds)): - if base=="hold": - cursor.execute("SELECT * FROM HudCache WHERE gametypeId+0=%s AND playerId=%s AND activeSeats=%s AND position=%s", (gametypeId, playerIds[player], len(playerIds), hudImportData['position'][player])) - else: - cursor.execute("SELECT * FROM HudCache WHERE gametypeId+0=%s AND playerId=%s AND activeSeats=%s", (gametypeId, playerIds[player], len(playerIds))) - row=cursor.fetchone() - #print "gametypeId:", gametypeId, "playerIds[player]",playerIds[player], "len(playerIds):",len(playerIds), "row:",row - - try: len(row) - except TypeError: - row=[] - - if (len(row)==0): - #print "new huddata row" - doInsert=True - row=[] - row.append(0)#blank for id - row.append(gametypeId) - row.append(playerIds[player]) - row.append(len(playerIds))#seats - for i in range(len(hudImportData)+2): - row.append(0) - - else: - doInsert=False - newrow=[] - for i in range(len(row)): - newrow.append(row[i]) - row=newrow - - if base=="hold": - row[4]=hudImportData['position'][player] - else: - row[4]=0 - row[5]=1 #tourneysGametypeId - row[6]+=1 #HDs - if hudImportData['street0VPI'][player]: row[7]+=1 - if hudImportData['street0Aggr'][player]: row[8]+=1 - if hudImportData['street0_3B4BChance'][player]: row[9]+=1 - if hudImportData['street0_3B4BDone'][player]: row[10]+=1 - if hudImportData['street1Seen'][player]: row[11]+=1 - if hudImportData['street2Seen'][player]: row[12]+=1 - if hudImportData['street3Seen'][player]: row[13]+=1 - if hudImportData['street4Seen'][player]: row[14]+=1 - if hudImportData['sawShowdown'][player]: row[15]+=1 - if hudImportData['street1Aggr'][player]: row[16]+=1 - if hudImportData['street2Aggr'][player]: row[17]+=1 - if hudImportData['street3Aggr'][player]: row[18]+=1 - if hudImportData['street4Aggr'][player]: row[19]+=1 - if hudImportData['otherRaisedStreet1'][player]: row[20]+=1 - if hudImportData['otherRaisedStreet2'][player]: row[21]+=1 - if hudImportData['otherRaisedStreet3'][player]: row[22]+=1 - if hudImportData['otherRaisedStreet4'][player]: row[23]+=1 - if hudImportData['foldToOtherRaisedStreet1'][player]: row[24]+=1 - if hudImportData['foldToOtherRaisedStreet2'][player]: row[25]+=1 - if hudImportData['foldToOtherRaisedStreet3'][player]: row[26]+=1 - if hudImportData['foldToOtherRaisedStreet4'][player]: row[27]+=1 - if hudImportData['wonWhenSeenStreet1'][player]!=0.0: row[28]+=hudImportData['wonWhenSeenStreet1'][player] - if hudImportData['wonAtSD'][player]!=0.0: row[29]+=hudImportData['wonAtSD'][player] - if hudImportData['stealAttemptChance'][player]: row[30]+=1 - if hudImportData['stealAttempted'][player]: row[31]+=1 - if hudImportData['foldBbToStealChance'][player]: row[32]+=1 - if hudImportData['foldedBbToSteal'][player]: row[33]+=1 - if hudImportData['foldSbToStealChance'][player]: row[34]+=1 - if hudImportData['foldedSbToSteal'][player]: row[35]+=1 - - if hudImportData['street1CBChance'][player]: row[36]+=1 - if hudImportData['street1CBDone'][player]: row[37]+=1 - if hudImportData['street2CBChance'][player]: row[38]+=1 - if hudImportData['street2CBDone'][player]: row[39]+=1 - if hudImportData['street3CBChance'][player]: row[40]+=1 - if hudImportData['street3CBDone'][player]: row[41]+=1 - if hudImportData['street4CBChance'][player]: row[42]+=1 - if hudImportData['street4CBDone'][player]: row[43]+=1 - - if hudImportData['foldToStreet1CBChance'][player]: row[44]+=1 - if hudImportData['foldToStreet1CBDone'][player]: row[45]+=1 - if hudImportData['foldToStreet2CBChance'][player]: row[46]+=1 - if hudImportData['foldToStreet2CBDone'][player]: row[47]+=1 - if hudImportData['foldToStreet3CBChance'][player]: row[48]+=1 - if hudImportData['foldToStreet3CBDone'][player]: row[49]+=1 - if hudImportData['foldToStreet4CBChance'][player]: row[50]+=1 - if hudImportData['foldToStreet4CBDone'][player]: row[51]+=1 - - #print "player=", player - #print "len(totalProfit)=", len(hudImportData['totalProfit']) - if hudImportData['totalProfit'][player]: - row[52]+=hudImportData['totalProfit'][player] - - if hudImportData['street1CheckCallRaiseChance'][player]: row[53]+=1 - if hudImportData['street1CheckCallRaiseDone'][player]: row[54]+=1 - if hudImportData['street2CheckCallRaiseChance'][player]: row[55]+=1 - if hudImportData['street2CheckCallRaiseDone'][player]: row[56]+=1 - if hudImportData['street3CheckCallRaiseChance'][player]: row[57]+=1 - if hudImportData['street3CheckCallRaiseDone'][player]: row[58]+=1 - if hudImportData['street4CheckCallRaiseChance'][player]: row[59]+=1 - if hudImportData['street4CheckCallRaiseDone'][player]: row[60]+=1 - - if doInsert: - #print "playerid before insert:",row[2] - cursor.execute("""INSERT INTO HudCache -(gametypeId, playerId, activeSeats, position, tourneyTypeId, -HDs, street0VPI, street0Aggr, street0_3B4BChance, street0_3B4BDone, -street1Seen, street2Seen, street3Seen, street4Seen, sawShowdown, -street1Aggr, street2Aggr, street3Aggr, street4Aggr, otherRaisedStreet1, -otherRaisedStreet2, otherRaisedStreet3, otherRaisedStreet4, foldToOtherRaisedStreet1, foldToOtherRaisedStreet2, -foldToOtherRaisedStreet3, foldToOtherRaisedStreet4, wonWhenSeenStreet1, wonAtSD, stealAttemptChance, -stealAttempted, foldBbToStealChance, foldedBbToSteal, foldSbToStealChance, foldedSbToSteal, -street1CBChance, street1CBDone, street2CBChance, street2CBDone, street3CBChance, -street3CBDone, street4CBChance, street4CBDone, foldToStreet1CBChance, foldToStreet1CBDone, -foldToStreet2CBChance, foldToStreet2CBDone, foldToStreet3CBChance, foldToStreet3CBDone, foldToStreet4CBChance, -foldToStreet4CBDone, totalProfit, street1CheckCallRaiseChance, street1CheckCallRaiseDone, street2CheckCallRaiseChance, -street2CheckCallRaiseDone, street3CheckCallRaiseChance, street3CheckCallRaiseDone, street4CheckCallRaiseChance, street4CheckCallRaiseDone) -VALUES (%s, %s, %s, %s, %s, -%s, %s, %s, %s, %s, -%s, %s, %s, %s, %s, -%s, %s, %s, %s, %s, -%s, %s, %s, %s, %s, -%s, %s, %s, %s, %s, -%s, %s, %s, %s, %s, -%s, %s, %s, %s, %s, -%s, %s, %s, %s, %s, -%s, %s, %s, %s, %s, -%s, %s, %s, %s, %s, -%s, %s, %s, %s, %s)""", (row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8], row[9], row[10], row[11], row[12], row[13], row[14], row[15], row[16], row[17], row[18], row[19], row[20], row[21], row[22], row[23], row[24], row[25], row[26], row[27], row[28], row[29], row[30], row[31], row[32], row[33], row[34], row[35], row[36], row[37], row[38], row[39], row[40], row[41], row[42], row[43], row[44], row[45], row[46], row[47], row[48], row[49], row[50], row[51], row[52], row[53], row[54], row[55], row[56], row[57], row[58], row[59], row[60])) - else: - #print "storing updated hud data line" - cursor.execute("""UPDATE HudCache -SET HDs=%s, street0VPI=%s, street0Aggr=%s, street0_3B4BChance=%s, street0_3B4BDone=%s, -street1Seen=%s, street2Seen=%s, street3Seen=%s, street4Seen=%s, sawShowdown=%s, -street1Aggr=%s, street2Aggr=%s, street3Aggr=%s, street4Aggr=%s, otherRaisedStreet1=%s, -otherRaisedStreet2=%s, otherRaisedStreet3=%s, otherRaisedStreet4=%s, foldToOtherRaisedStreet1=%s, foldToOtherRaisedStreet2=%s, -foldToOtherRaisedStreet3=%s, foldToOtherRaisedStreet4=%s, wonWhenSeenStreet1=%s, wonAtSD=%s, stealAttemptChance=%s, -stealAttempted=%s, foldBbToStealChance=%s, foldedBbToSteal=%s, foldSbToStealChance=%s, foldedSbToSteal=%s, -street1CBChance=%s, street1CBDone=%s, street2CBChance=%s, street2CBDone=%s, street3CBChance=%s, -street3CBDone=%s, street4CBChance=%s, street4CBDone=%s, foldToStreet1CBChance=%s, foldToStreet1CBDone=%s, -foldToStreet2CBChance=%s, foldToStreet2CBDone=%s, foldToStreet3CBChance=%s, foldToStreet3CBDone=%s, foldToStreet4CBChance=%s, -foldToStreet4CBDone=%s, totalProfit=%s, street1CheckCallRaiseChance=%s, street1CheckCallRaiseDone=%s, street2CheckCallRaiseChance=%s, -street2CheckCallRaiseDone=%s, street3CheckCallRaiseChance=%s, street3CheckCallRaiseDone=%s, street4CheckCallRaiseChance=%s, street4CheckCallRaiseDone=%s -WHERE gametypeId=%s AND playerId=%s AND activeSeats=%s AND position=%s AND tourneyTypeId=%s""", (row[6], row[7], row[8], row[9], row[10], - row[11], row[12], row[13], row[14], row[15], - row[16], row[17], row[18], row[19], row[20], - row[21], row[22], row[23], row[24], row[25], - row[26], row[27], row[28], row[29], row[30], - row[31], row[32], row[33], row[34], row[35], row[36], row[37], row[38], row[39], row[40], - row[41], row[42], row[43], row[44], row[45], row[46], row[47], row[48], row[49], row[50], - row[51], row[52], row[53], row[54], row[55], row[56], row[57], row[58], row[59], row[60], - row[1], row[2], row[3], str(row[4]), row[5])) -# else: -# print "todo: implement storeHudCache for stud base" -#end def storeHudCache - -def store_tourneys(cursor, tourneyTypeId, siteTourneyNo, entries, prizepool, startTime): - cursor.execute("SELECT id FROM Tourneys WHERE siteTourneyNo=%s AND tourneyTypeId+0=%s", (siteTourneyNo, tourneyTypeId)) - tmp=cursor.fetchone() - #print "tried SELECTing tourneys.id, result:",tmp - - try: - len(tmp) - except TypeError:#means we have to create new one - cursor.execute("""INSERT INTO Tourneys -(tourneyTypeId, siteTourneyNo, entries, prizepool, startTime) -VALUES (%s, %s, %s, %s, %s)""", (tourneyTypeId, siteTourneyNo, entries, prizepool, startTime)) - cursor.execute("SELECT id FROM Tourneys WHERE siteTourneyNo=%s AND tourneyTypeId+0=%s", (siteTourneyNo, tourneyTypeId)) - tmp=cursor.fetchone() - #print "created new tourneys.id:",tmp - return tmp[0] -#end def store_tourneys - -def store_tourneys_players(cursor, tourney_id, player_ids, payin_amounts, ranks, winnings): - result=[] - #print "in store_tourneys_players. tourney_id:",tourney_id - #print "player_ids:",player_ids - #print "payin_amounts:",payin_amounts - #print "ranks:",ranks - #print "winnings:",winnings - for i in range (len(player_ids)): - cursor.execute("SELECT id FROM TourneysPlayers WHERE tourneyId=%s AND playerId+0=%s", (tourney_id, player_ids[i])) - tmp=cursor.fetchone() - #print "tried SELECTing tourneys_players.id:",tmp - - try: - len(tmp) - except TypeError: - cursor.execute("""INSERT INTO TourneysPlayers -(tourneyId, playerId, payinAmount, rank, winnings) VALUES (%s, %s, %s, %s, %s)""", - (tourney_id, player_ids[i], payin_amounts[i], ranks[i], winnings[i])) - - cursor.execute("SELECT id FROM TourneysPlayers WHERE tourneyId=%s AND playerId+0=%s", - (tourney_id, player_ids[i])) - tmp=cursor.fetchone() - #print "created new tourneys_players.id:",tmp - result.append(tmp[0]) - return result -#end def store_tourneys_players +#!/usr/bin/python + +#Copyright 2008 Steffen Jobbagy-Felso +#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 . +#In the "official" distribution you can find the license in +#agpl-3.0.txt in the docs folder of the package. + +#This file contains simple functions for fpdb + +import datetime +import re + +PS=1 +FTP=2 + +MYSQL_INNODB=2 +PGSQL=3 +SQLITE=4 + + +class DuplicateError(Exception): + def __init__(self, value): + self.value = value + def __str__(self): + return repr(self.value) + +class FpdbError(Exception): + def __init__(self, value): + self.value = value + def __str__(self): + return repr(self.value) + +# gets value for last auto-increment key generated +# returns -1 if a problem occurs +def getLastInsertId(backend, conn, cursor): + if backend == MYSQL_INNODB: + ret = conn.insert_id() + if ret < 1 or ret > 999999999: + print "getLastInsertId(): problem fetching insert_id? ret=", ret + ret = -1 + elif backend == PGSQL: + # some options: + # currval(hands_id_seq) - use name of implicit seq here + # lastval() - still needs sequences set up? + # insert ... returning is useful syntax (but postgres specific?) + # see rules (fancy trigger type things) + cursor.execute ("SELECT lastval()") + row = cursor.fetchone() + if not row: + print "getLastInsertId(%s): problem fetching lastval? row=" % seq, row + ret = -1 + else: + ret = row[0] + elif backend == SQLITE: + # don't know how to do this in sqlite + print "getLastInsertId(): not coded for sqlite yet" + ret = -1 + else: + print "getLastInsertId(): unknown backend ", backend + ret = -1 + return ret +#end def getLastInsertId + +#returns an array of the total money paid. intending to add rebuys/addons here +def calcPayin(count, buyin, fee): + result=[] + for i in range(count): + result.append (buyin+fee) + return result +#end def calcPayin + +def checkPositions(positions): + """verifies that these positions are valid""" + for i in range (len(positions)): + pos=positions[i] + try:#todo: use type recognition instead of error + if (len(pos)!=1): + raise FpdbError("invalid position found in checkPositions. i: "+str(i)+" position: "+pos) #dont need to str() here + except TypeError:#->not string->is int->fine + pass + + ### RHH modified to allow for "position 9" here (pos==9 is when you're a dead hand before the BB + if (pos!="B" and pos!="S" and pos!=0 and pos!=1 and pos!=2 and pos!=3 and pos!=4 and pos!=5 and pos!=6 and pos!=7 and pos!=9): + raise FpdbError("invalid position found in checkPositions. i: "+str(i)+" position: "+str(pos)) +#end def fpdb_simple.checkPositions + +#classifies each line for further processing in later code. Manipulates the passed arrays. +def classifyLines(hand, category, lineTypes, lineStreets): + currentStreet="predeal" + done=False #set this to true once we reach the last relevant line (the summary, except rake, is all repeats) + for i in range (len(hand)): + if (done): + if (hand[i].find("[")==-1 or hand[i].find("mucked [")==-1): + lineTypes.append("ignore") + else: #it's storing a mucked card + lineTypes.append("cards") + elif (hand[i].startswith("Dealt to")): + lineTypes.append("cards") + elif (i==0): + lineTypes.append("header") + elif (hand[i].startswith("Seat ") and ((hand[i].find("in chips")!=-1) or (hand[i].find("($")!=-1))): + lineTypes.append("name") + elif (isActionLine(hand[i])): + lineTypes.append("action") + if (hand[i].find(" posts ")!=-1 or hand[i].find(" posts the ")!=-1):#need to set this here so the "action" of posting blinds is registered properly + currentStreet="preflop" + elif (isWinLine(hand[i])): + lineTypes.append("win") + elif (hand[i].startswith("Total pot ") and hand[i].find("Rake")!=-1): + lineTypes.append("rake") + done=True + elif (hand[i]=="*** SHOW DOWN ***" or hand[i]=="*** SUMMARY ***"): + lineTypes.append("ignore") + #print "in classifyLine, showdown or summary" + elif (hand[i].find(" antes ")!=-1 or hand[i].find(" posts the ante ")!=-1): + lineTypes.append("ante") + elif (hand[i].startswith("*** FLOP *** [")): + lineTypes.append("cards") + currentStreet="flop" + elif (hand[i].startswith("*** TURN *** [")): + lineTypes.append("cards") + currentStreet="turn" + elif (hand[i].startswith("*** RIVER *** [")): + lineTypes.append("cards") + currentStreet="river" + elif (hand[i].startswith("*** 3")): + lineTypes.append("ignore") + currentStreet=0 + elif (hand[i].startswith("*** 4")): + lineTypes.append("ignore") + currentStreet=1 + elif (hand[i].startswith("*** 5")): + lineTypes.append("ignore") + currentStreet=2 + elif (hand[i].startswith("*** 6")): + lineTypes.append("ignore") + currentStreet=3 + elif (hand[i].startswith("*** 7") or hand[i]=="*** RIVER ***"): + lineTypes.append("ignore") + currentStreet=4 + elif (hand[i].find(" shows [")!=-1): + lineTypes.append("cards") + elif (hand[i].startswith("Table '")): + lineTypes.append("table") + else: + raise FpdbError("unrecognised linetype in:"+hand[i]) + lineStreets.append(currentStreet) +#end def classifyLines + +def convert3B4B(site, category, limit_type, actionTypes, actionAmounts): + """calculates the actual bet amounts in the given amount array and changes it accordingly.""" + for i in range (len(actionTypes)): + for j in range (len(actionTypes[i])): + bets=[] + for k in range (len(actionTypes[i][j])): + if (actionTypes[i][j][k]=="bet"): + bets.append((i,j,k)) + if (len(bets)==2): + #print "len(bets) 2 or higher, need to correct it. bets:",bets,"len:",len(bets) + amount2=actionAmounts[bets[1][0]][bets[1][1]][bets[1][2]] + amount1=actionAmounts[bets[0][0]][bets[0][1]][bets[0][2]] + actionAmounts[bets[1][0]][bets[1][1]][bets[1][2]]=amount2-amount1 + elif (len(bets)>2): + fail=True + #todo: run correction for below + if (site=="ps" and category=="holdem" and limit_type=="nl" and len(bets)==3): + fail=False + if (site=="ftp" and category=="omahahi" and limit_type=="pl" and len(bets)==3): + fail=False + + if fail: + print "len(bets)>2 in convert3B4B, i didnt think this is possible. i:",i,"j:",j,"k:",k + print "actionTypes:",actionTypes + raise FpdbError ("too many bets in convert3B4B") + #print "actionAmounts postConvert",actionAmounts +#end def convert3B4B(actionTypes, actionAmounts) + +#Corrects the bet amount if the player had to pay blinds +def convertBlindBet(actionTypes, actionAmounts): + i=0#setting street to pre-flop + for j in range (len(actionTypes[i])):#playerloop + blinds=[] + bets=[] + for k in range (len(actionTypes[i][j])): + if (actionTypes[i][j][k]=="blind"): + blinds.append((i,j,k)) + + if (len(blinds)>0 and actionTypes[i][j][k]=="bet"): + bets.append((i,j,k)) + if (len(bets)==1): + blind_amount=actionAmounts[blinds[0][0]][blinds[0][1]][blinds[0][2]] + bet_amount=actionAmounts[bets[0][0]][bets[0][1]][bets[0][2]] + actionAmounts[bets[0][0]][bets[0][1]][bets[0][2]]=bet_amount-blind_amount +#end def convertBlindBet + +#converts the strings in the given array to ints (changes the passed array, no returning). see table design for conversion details +#todo: make this use convertCardValuesBoard +def convertCardValues(arr): + for i in range (len(arr)): + for j in range (len(arr[i])): + if (arr[i][j]=="A"): + arr[i][j]=14 + elif (arr[i][j]=="K"): + arr[i][j]=13 + elif (arr[i][j]=="Q"): + arr[i][j]=12 + elif (arr[i][j]=="J"): + arr[i][j]=11 + elif (arr[i][j]=="T"): + arr[i][j]=10 + else: + arr[i][j]=int(arr[i][j]) +#end def convertCardValues + +#converts the strings in the given array to ints (changes the passed array, no returning). see table design for conversion details +def convertCardValuesBoard(arr): + for i in range (len(arr)): + if (arr[i]=="A"): + arr[i]=14 + elif (arr[i]=="K"): + arr[i]=13 + elif (arr[i]=="Q"): + arr[i]=12 + elif (arr[i]=="J"): + arr[i]=11 + elif (arr[i]=="T"): + arr[i]=10 + else: + arr[i]=int(arr[i]) +#end def convertCardValuesBoard + +#this creates the 2D/3D arrays. manipulates the passed arrays instead of returning. +def createArrays(category, seats, card_values, card_suits, antes, winnings, rakes, action_types, allIns, action_amounts, actionNos, actionTypeByNo): + for i in range(seats):#create second dimension arrays + tmp=[] + card_values.append(tmp) + tmp=[] + card_suits.append(tmp) + antes.append(0) + winnings.append(0) + rakes.append(0) + + if (category=="holdem" or category=="omahahi" or category=="omahahilo"): + streetCount=4 + else: + streetCount=5 + + for i in range(streetCount): #build the first dimension array, for streets + tmp=[] + action_types.append(tmp) + tmp=[] + allIns.append(tmp) + tmp=[] + action_amounts.append(tmp) + tmp=[] + actionNos.append(tmp) + tmp=[] + actionTypeByNo.append(tmp) + for j in range (seats): #second dimension arrays: players + tmp=[] + action_types[i].append(tmp) + tmp=[] + allIns[i].append(tmp) + tmp=[] + action_amounts[i].append(tmp) + tmp=[] + actionNos[i].append(tmp) + if (category=="holdem" or category=="omahahi" or category=="omahahilo"): + pass + elif (category=="razz" or category=="studhi" or category=="studhilo"):#need to fill card arrays. + for i in range(seats): + for j in range (7): + card_values[i].append(0) + card_suits[i].append("x") + else: + raise FpdbError("invalid category") +#end def createArrays + +def fill_board_cards(board_values, board_suits): +#fill up the two board card arrays + while (len(board_values)<5): + board_values.append(0) + board_suits.append("x") +#end def fill_board_cards + +def fillCardArrays(player_count, base, category, card_values, card_suits): + """fills up the two card arrays""" + if (category=="holdem"): + cardCount=2 + elif (category=="omahahi" or category=="omahahilo"): + cardCount=4 + elif base=="stud": + cardCount=7 + else: + raise fpdb_simple.FpdbError ("invalid category:", category) + + for i in range (player_count): + while (len(card_values[i])=1): + raise DuplicateError ("dupl") +#end isAlreadyInDB + +def isRebuyOrAddon(topline): + """isRebuyOrAddon not implemented yet""" + return False +#end def isRebuyOrAddon + +#returns whether the passed topline indicates a tournament or not +def isTourney(topline): + if (topline.find("Tournament")!=-1): + return True + else: + return False +#end def isTourney + +#returns boolean whether the passed line is a win line +def isWinLine(line): + if (line.find("wins the pot")!=-1): + return True + elif (line.find("ties for the high pot")!=-1): + return True + elif (line.find("ties for the high main pot")!=-1): + return True + elif (line.find("ties for the high side pot")!=-1): + return True + elif (line.find("ties for the low pot")!=-1): + return True + elif (line.find("ties for the low main pot")!=-1): + return True + elif (line.find("ties for the low side pot")!=-1): + return True + elif (line.find("ties for the main pot")!=-1): #for ftp tied main pot of split pot + return True + elif (line.find("ties for the pot")!=-1): #for ftp tie + return True + elif (line.find("ties for the side pot")!=-1): #for ftp tied split pots + return True + elif (line.find("wins side pot #")!=-1): #for ftp multi split pots + return True + elif (line.find("wins the low main pot")!=-1): + return True + elif (line.find("wins the low pot")!=-1): + return True + elif (line.find("wins the low side pot")!=-1): + return True + elif (line.find("wins the high main pot")!=-1): + return True + elif (line.find("wins the high pot")!=-1): + return True + elif (line.find("wins the high side pot")!=-1): + return True + elif (line.find("wins the main pot")!=-1): + return True + elif (line.find("wins the side pot")!=-1): #for ftp split pots + return True + elif (line.find("collected")!=-1): + return True + else: + return False #not raising error here, any unknown line wouldve been detected in isActionLine already +#end def isWinLine + +#returns the amount of cash/chips put into the put in the given action line +def parseActionAmount(line, atype, site, isTourney): + #if (line.endswith(" and is all-in")): + # line=line[:-14] + #elif (line.endswith(", and is all in")): + # line=line[:-15] + + if line.endswith(", and is capped"):#ideally we should recognise this as an all-in if category is capXl + line=line[:-15] + if line.endswith(" and is capped"): + line=line[:-14] + + + if (atype=="fold"): + amount=0 + elif (atype=="check"): + amount=0 + elif (atype=="unbet" and site=="ftp"): + pos1=line.find("$")+1 + pos2=line.find(" returned to") + amount=float2int(line[pos1:pos2]) + elif (atype=="unbet" and site=="ps"): + #print "ps unbet, line:",line + pos1=line.find("$")+1 + if pos1==0: + pos1=line.find("(")+1 + pos2=line.find(")") + amount=float2int(line[pos1:pos2]) + elif (atype=="bet" and site=="ps" and line.find(": raises $")!=-1 and line.find("to $")!=-1): + pos=line.find("to $")+4 + amount=float2int(line[pos:]) + else: + if not isTourney: + pos=line.rfind("$")+1 + #print "parseActionAmount, line:", line, "line[pos:]:", line[pos:] + amount=float2int(line[pos:]) + else: + #print "line:"+line+"EOL" + pos=line.rfind(" ")+1 + #print "pos:",pos + #print "pos of 20:", line.find("20") + amount=int(line[pos:]) + + if atype=="unbet": + amount*=-1 + return amount +#end def parseActionAmount + +#doesnt return anything, simply changes the passed arrays action_types and +# action_amounts. For stud this expects numeric streets (3-7), for +# holdem/omaha it expects predeal, preflop, flop, turn or river +def parseActionLine(site, base, isTourney, line, street, playerIDs, names, action_types, allIns, action_amounts, actionNos, actionTypeByNo): + if (street=="predeal" or street=="preflop"): + street=0 + elif (street=="flop"): + street=1 + elif (street=="turn"): + street=2 + elif (street=="river"): + street=3 + + nextActionNo=0 + for player in range(len(actionNos[street])): + for count in range(len(actionNos[street][player])): + if actionNos[street][player][count]>=nextActionNo: + nextActionNo=actionNos[street][player][count]+1 + + line, allIn=goesAllInOnThisLine(line) + atype=parseActionType(line) + playerno=recognisePlayerNo(line, names, atype) + amount=parseActionAmount(line, atype, site, isTourney) + + action_types[street][playerno].append(atype) + allIns[street][playerno].append(allIn) + action_amounts[street][playerno].append(amount) + actionNos[street][playerno].append(nextActionNo) + tmp=(playerIDs[playerno], atype) + actionTypeByNo[street].append(tmp) +#end def parseActionLine + +def goesAllInOnThisLine(line): + """returns whether the player went all-in on this line and removes the all-in text from the line.""" + isAllIn=False + if (line.endswith(" and is all-in")): + line=line[:-14] + isAllIn=True + elif (line.endswith(", and is all in")): + line=line[:-15] + isAllIn=True + return (line, isAllIn) +#end def goesAllInOnThisLine + +#returns the action type code (see table design) of the given action line +def parseActionType(line): + if (line.startswith("Uncalled bet")): + return "unbet" + elif (line.endswith("folds")): + return "fold" + elif (line.endswith("checks")): + return "check" + elif (line.find("calls")!=-1): + return "call" + elif (line.find("brings in for")!=-1): + return "blind" + elif (line.find("completes it to")!=-1): + return "bet" + #todo: what if someone completes instead of bringing in? + elif (line.find(" posts $")!=-1): + return "blind" + elif (line.find(" posts a dead ")!=-1): + return "blind" + elif (line.find(": posts small blind ")!=-1): + return "blind" + elif (line.find(" posts the small blind of $")!=-1): + return "blind" + elif (line.find(": posts big blind ")!=-1): + return "blind" + elif (line.find(" posts the big blind of $")!=-1): + return "blind" + elif (line.find(": posts small & big blinds $")!=-1): + return "blind" + #todo: seperately record voluntary blind payments made to join table out of turn + elif (line.find("bets")!=-1): + return "bet" + elif (line.find("raises")!=-1): + return "bet" + else: + raise FpdbError ("failed to recognise actiontype in parseActionLine in: "+line) +#end def parseActionType + +#parses the ante out of the given line and checks which player paid it, updates antes accordingly. +def parseAnteLine(line, site, isTourney, names, antes): + for i in range(len(names)): + if (line.startswith(names[i].encode("latin-1"))): #found the ante'er + pos=line.rfind("$")+1 + if not isTourney: + antes[i]+=float2int(line[pos:]) + else: + if line.find("all-in")==-1: + pos=line.rfind(" ")+1 + antes[i]+=int(line[pos:]) + else: + pos1=line.rfind("ante")+5 + pos2=line.find(" ",pos1) + antes[i]+=int(line[pos1:pos2]) + #print "parseAnteLine line: ", line, "antes[i]", antes[i], "antes", antes +#end def parseAntes + +#returns the buyin of a tourney in cents +def parseBuyin(topline): + pos1=topline.find("$")+1 + pos2=topline.find("+") + return float2int(topline[pos1:pos2]) +#end def parseBuyin + +#parses a card line and changes the passed arrays accordingly +#todo: reorganise this messy method +def parseCardLine(site, category, street, line, names, cardValues, cardSuits, boardValues, boardSuits): + if (line.startswith("Dealt to ") or line.find(" shows [")!=-1 or line.find("mucked [")!=-1): + playerNo=recognisePlayerNo(line, names, "card") #anything but unbet will be ok for that string + + pos=line.rfind("[")+1 + if (category=="holdem"): + for i in (pos, pos+3): + cardValues[playerNo].append(line[i:i+1]) + cardSuits[playerNo].append(line[i+1:i+2]) + if (len(cardValues[playerNo])!=2): + if cardValues[playerNo][0]==cardValues[playerNo][2] and cardSuits[playerNo][1]==cardSuits[playerNo][3]: #two tests will do + cardValues[playerNo]=cardValues[playerNo][0:2] + cardSuits[playerNo]=cardSuits[playerNo][0:2] + else: + print "line:",line,"cardValues[playerNo]:",cardValues[playerNo] + raise FpdbError("read too many/too few holecards in parseCardLine") + elif (category=="omahahi" or category=="omahahilo"): + for i in (pos, pos+3, pos+6, pos+9): + cardValues[playerNo].append(line[i:i+1]) + cardSuits[playerNo].append(line[i+1:i+2]) + if (len(cardValues[playerNo])!=4): + if cardValues[playerNo][0]==cardValues[playerNo][4] and cardSuits[playerNo][3]==cardSuits[playerNo][7]: #two tests will do + cardValues[playerNo]=cardValues[playerNo][0:4] + cardSuits[playerNo]=cardSuits[playerNo][0:4] + else: + print "line:",line,"cardValues[playerNo]:",cardValues[playerNo] + raise FpdbError("read too many/too few holecards in parseCardLine") + elif (category=="razz" or category=="studhi" or category=="studhilo"): + if (line.find("shows")==-1 and line.find("mucked")==-1): + #print "parseCardLine(in stud if), street:", street + if line[pos+2]=="]": #-> not (hero and 3rd street) + cardValues[playerNo][street+2]=line[pos:pos+1] + cardSuits[playerNo][street+2]=line[pos+1:pos+2] + else: + #print "hero card1:", line[pos:pos+2], "hero card2:", line[pos+3:pos+5], "hero card3:", line[pos+6:pos+8], + cardValues[playerNo][street]=line[pos:pos+1] + cardSuits[playerNo][street]=line[pos+1:pos+2] + cardValues[playerNo][street+1]=line[pos+3:pos+4] + cardSuits[playerNo][street+1]=line[pos+4:pos+5] + cardValues[playerNo][street+2]=line[pos+6:pos+7] + cardSuits[playerNo][street+2]=line[pos+7:pos+8] + else: + #print "parseCardLine(in stud else), street:", street + cardValues[playerNo][0]=line[pos:pos+1] + cardSuits[playerNo][0]=line[pos+1:pos+2] + pos+=3 + cardValues[playerNo][1]=line[pos:pos+1] + cardSuits[playerNo][1]=line[pos+1:pos+2] + if street==4: + pos=pos=line.rfind("]")-2 + cardValues[playerNo][6]=line[pos:pos+1] + cardSuits[playerNo][6]=line[pos+1:pos+2] + #print "cardValues:", cardValues + #print "cardSuits:", cardSuits + else: + print "line:",line,"street:",street + raise FpdbError("invalid category") + #print "end of parseCardLine/playercards, cardValues:",cardValues + elif (line.startswith("*** FLOP ***")): + pos=line.find("[")+1 + for i in (pos, pos+3, pos+6): + boardValues.append(line[i:i+1]) + boardSuits.append(line[i+1:i+2]) + #print boardValues + elif (line.startswith("*** TURN ***") or line.startswith("*** RIVER ***")): + pos=line.find("[")+1 + pos=line.find("[", pos+1)+1 + boardValues.append(line[pos:pos+1]) + boardSuits.append(line[pos+1:pos+2]) + #print boardValues + else: + raise FpdbError ("unrecognised line:"+line) +#end def parseCardLine + +def parseCashesAndSeatNos(lines, site): + """parses the startCashes and seatNos of each player out of the given lines and returns them as a dictionary of two arrays""" + cashes = [] + seatNos = [] + for i in range (len(lines)): + pos2=lines[i].find(":") + seatNos.append(int(lines[i][5:pos2])) + + pos1=lines[i].rfind("($")+2 + if pos1==1: #for tourneys - it's 1 instead of -1 due to adding 2 above + pos1=lines[i].rfind("(")+1 + if (site=="ftp"): + pos2=lines[i].rfind(")") + elif (site=="ps"): + pos2=lines[i].find(" in chips") + cashes.append(float2int(lines[i][pos1:pos2])) + return {'startCashes':cashes, 'seatNos':seatNos} +#end def parseCashesAndSeatNos + +#returns the buyin of a tourney in cents +def parseFee(topline): + pos1=topline.find("$")+1 + pos1=topline.find("$",pos1)+1 + pos2=topline.find(" ", pos1) + return float2int(topline[pos1:pos2]) +#end def parsefee + +#returns a datetime object with the starttime indicated in the given topline +def parseHandStartTime(topline, site): + #convert x:13:35 to 0x:13:35 + counter=0 + while (True): + pos=topline.find(" "+str(counter)+":") + if (pos!=-1): + topline=topline[0:pos+1]+"0"+topline[pos+1:] + counter+=1 + if counter==10: break + + isUTC=False + if site=="ftp": + pos = topline.find(" ", len(topline)-26)+1 + tmp = topline[pos:] + #print "year:", tmp[14:18], "month", tmp[19:21], "day", tmp[22:24], "hour", tmp[0:2], "minute", tmp[3:5], "second", tmp[6:8] + result = datetime.datetime(int(tmp[14:18]), int(tmp[19:21]), int(tmp[22:24]), int(tmp[0:2]), int(tmp[3:5]), int(tmp[6:8])) + elif site=="ps": + if topline.find("UTC")!=-1: + pos1 = topline.find("-")+2 + pos2 = topline.find("UTC") + tmp=topline[pos1:pos2] + isUTC=True + else: + tmp=topline[-30:] + #print "parsehandStartTime, tmp:", tmp + pos = tmp.find("-")+2 + tmp = tmp[pos:] + #Need to match either + # 2008/09/07 06:23:14 ET or + # 2008/08/17 - 01:14:43 (ET) + m = re.match('(?P[0-9]{4})\/(?P[0-9]{2})\/(?P[0-9]{2})[\- ]+(?P
[0-9]{2}):(?P[0-9]{2}):(?P[0-9]{2})',tmp) + #print "year:", int(m.group('YEAR')), "month", int(m.group('MON')), "day", int(m.group('DAY')), "hour", int(m.group('HR')), "minute", int(m.group('MIN')), "second", int(m.group('SEC')) + result = datetime.datetime(int(m.group('YEAR')), int(m.group('MON')), int(m.group('DAY')), int(m.group('HR')), int(m.group('MIN')), int(m.group('SEC'))) + else: + raise FpdbError("invalid site in parseHandStartTime") + + if (site=="ftp" or site=="ps") and not isUTC: #these use US ET + result+=datetime.timedelta(hours=5) + + return result +#end def parseHandStartTime + +#parses the names out of the given lines and returns them as an array +def parseNames(lines): + result = [] + for i in range (len(lines)): + pos1=lines[i].find(":")+2 + pos2=lines[i].rfind("(")-1 + tmp=lines[i][pos1:pos2] + #print "parseNames, tmp original:",tmp + tmp=unicode(tmp,"latin-1") + #print "parseNames, tmp after unicode latin-1 conversion:",tmp + result.append(tmp) + return result +#end def parseNames + +#returns an array with the positions of the respective players +def parsePositions (hand, names): + #prep array + positions=[] + for i in range(len(names)): + positions.append(-1) + + #find blinds + sb,bb=-1,-1 + for i in range (len(hand)): + if (sb==-1 and hand[i].find("small blind")!=-1 and hand[i].find("dead small blind")==-1): + sb=hand[i] + #print "sb:",sb + if (bb==-1 and hand[i].find("big blind")!=-1 and hand[i].find("dead big blind")==-1): + bb=hand[i] + #print "bb:",bb + + #identify blinds + #print "parsePositions before recognising sb/bb. names:",names + sbExists=True + if (sb!=-1): + sb=recognisePlayerNo(sb, names, "bet") + else: + sbExists=False + if (bb!=-1): + bb=recognisePlayerNo(bb, names, "bet") + + #write blinds into array + if (sbExists): + positions[sb]="S" + positions[bb]="B" + + #fill up rest of array + if (sbExists): + arraypos=sb-1 + else: + arraypos=bb-1 + distFromBtn=0 + while (arraypos>=0 and arraypos != bb): + #print "parsePositions first while, arraypos:",arraypos,"positions:",positions + positions[arraypos]=distFromBtn + arraypos-=1 + distFromBtn+=1 + + ### RHH - Changed to set the null seats before BB to "9" + i=bb-1 + while positions[i] < 0: + positions[i]=9 + i-=1 + + arraypos=len(names)-1 + if (bb!=0 or (bb==0 and sbExists==False)): + while (arraypos>bb): + positions[arraypos]=distFromBtn + arraypos-=1 + distFromBtn+=1 + + for i in range (len(names)): + if positions[i]==-1: + print "parsePositions names:",names + print "result:",positions + raise FpdbError ("failed to read positions") + return positions +#end def parsePositions + +#simply parses the rake amount and returns it as an int +def parseRake(line): + pos=line.find("Rake")+6 + rake=float2int(line[pos:]) + return rake +#end def parseRake + +def parseSiteHandNo(topline): + """returns the hand no assigned by the poker site""" + pos1=topline.find("#")+1 + pos2=topline.find(":") + return topline[pos1:pos2] +#end def parseSiteHandNo + +def parseTableLine(site, base, line): + """returns a dictionary with maxSeats and tableName""" + if site=="ps": + pos1=line.find('\'')+1 + pos2=line.find('\'', pos1) + #print "table:",line[pos1:pos2] + pos3=pos2+2 + pos4=line.find("-max") + #print "seats:",line[pos3:pos4] + return {'maxSeats':int(line[pos3:pos4]), 'tableName':line[pos1:pos2]} + elif site=="ftp": + pos1=line.find("Table ")+6 + pos2=line.find("-")-1 + if base=="hold": + maxSeats=9 + elif base=="stud": + maxSeats=8 + + if line.find("6 max")!=-1: + maxSeats=6 + elif line.find("4 max")!=-1: + maxSeats=4 + elif line.find("heads up")!=-1: + maxSeats=2 + + return {'maxSeats':maxSeats, 'tableName':line[pos1:pos2]} + else: + raise FpdbError("invalid site ID") +#end def parseTableLine + +#returns the hand no assigned by the poker site +def parseTourneyNo(topline): + pos1=topline.find("Tournament #")+12 + pos2=topline.find(",", pos1) + #print "parseTourneyNo pos1:",pos1," pos2:",pos2, " result:",topline[pos1:pos2] + return topline[pos1:pos2] +#end def parseTourneyNo + +#parses a win/collect line. manipulates the passed array winnings, no explicit return +def parseWinLine(line, site, names, winnings, isTourney): + #print "parseWinLine: line:",line + for i in range(len(names)): + if (line.startswith(names[i].encode("latin-1"))): #found a winner + if isTourney: + pos1=line.rfind("collected ")+10 + if (site=="ftp"): + pos2=line.find(")", pos1) + elif (site=="ps"): + pos2=line.find(" ", pos1) + winnings[i]+=int(line[pos1:pos2]) + else: + pos1=line.rfind("$")+1 + if (site=="ftp"): + pos2=line.find(")", pos1) + elif (site=="ps"): + pos2=line.find(" ", pos1) + winnings[i]+=float2int(line[pos1:pos2]) +#end def parseWinLine + +#returns the category (as per database) string for the given line +def recogniseCategory(line): + if (line.find("Razz")!=-1): + return "razz" + elif (line.find("Hold'em")!=-1): + return "holdem" + elif (line.find("Omaha")!=-1 and line.find("Hi/Lo")==-1 and line.find("H/L")==-1): + return "omahahi" + elif (line.find("Omaha")!=-1 and (line.find("Hi/Lo")!=-1 or line.find("H/L")!=-1)): + return "omahahilo" + elif (line.find("Stud")!=-1 and line.find("Hi/Lo")==-1 and line.find("H/L")==-1): + return "studhi" + elif (line.find("Stud")!=-1 and (line.find("Hi/Lo")!=-1 or line.find("H/L")!=-1)): + return "studhilo" + else: + raise FpdbError("failed to recognise category, line:"+line) +#end def recogniseCategory + +#returns the int for the gametype_id for the given line +def recogniseGametypeID(backend, db, cursor, topline, smallBlindLine, site_id, category, isTourney):#todo: this method is messy + #if (topline.find("HORSE")!=-1): + # raise FpdbError("recogniseGametypeID: HORSE is not yet supported.") + + #note: the below variable names small_bet and big_bet are misleading, in NL/PL they mean small/big blind + if isTourney: + type="tour" + pos1=topline.find("(")+1 + if (topline[pos1]=="H" or topline[pos1]=="O" or topline[pos1]=="R" or topline[pos1]=="S" or topline[pos1+2]=="C"): + pos1=topline.find("(", pos1)+1 + pos2=topline.find("/", pos1) + small_bet=int(topline[pos1:pos2]) + else: + type="ring" + pos1=topline.find("$")+1 + pos2=topline.find("/$") + small_bet=float2int(topline[pos1:pos2]) + + pos1=pos2+2 + if isTourney: + pos1-=1 + if (site_id==1): #ftp + pos2=topline.find(" ", pos1) + elif (site_id==2): #ps + pos2=topline.find(")") + + if pos2<=pos1: + pos2=topline.find(")", pos1) + + if isTourney: + big_bet=int(topline[pos1:pos2]) + else: + big_bet=float2int(topline[pos1:pos2]) + + if (topline.find("No Limit")!=-1): + limit_type="nl" + if (topline.find("Cap No")!=-1): + limit_type="cn" + elif (topline.find("Pot Limit")!=-1): + limit_type="pl" + if (topline.find("Cap Pot")!=-1): + limit_type="cp" + else: + limit_type="fl" + + #print "recogniseGametypeID small_bet/blind:",small_bet,"big bet/blind:", big_bet,"limit type:",limit_type + if (limit_type=="fl"): + cursor.execute ("SELECT id FROM Gametypes WHERE siteId=%s AND type=%s AND category=%s AND limitType=%s AND smallBet=%s AND bigBet=%s", (site_id, type, category, limit_type, small_bet, big_bet)) + else: + cursor.execute ("SELECT id FROM Gametypes WHERE siteId=%s AND type=%s AND category=%s AND limitType=%s AND smallBlind=%s AND bigBlind=%s", (site_id, type, category, limit_type, small_bet, big_bet)) + result=cursor.fetchone() + #print "recgt1 result=",result + #ret=result[0] + #print "recgt1 ret=",ret + #print "tried SELECTing gametypes.id, result:",result + + try: + len(result) + except TypeError: + if category=="holdem" or category=="omahahi" or category=="omahahilo": + base="hold" + else: + base="stud" + + if category=="holdem" or category=="omahahi" or category=="studhi": + hiLo='h' + elif category=="razz": + hiLo='l' + else: + hiLo='s' + + if (limit_type=="fl"): + big_blind=small_bet + if base=="hold": + if smallBlindLine==topline: + raise FpdbError("invalid small blind line") + elif isTourney: + pos=smallBlindLine.rfind(" ")+1 + small_blind=int(smallBlindLine[pos:]) + else: + pos=smallBlindLine.rfind("$")+1 + small_blind=float2int(smallBlindLine[pos:]) + else: + small_blind=0 + cursor.execute( """INSERT INTO Gametypes(siteId, type, base, category, limitType + ,hiLo, smallBlind, bigBlind, smallBet, bigBet) + VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""" + , (site_id, type, base, category, limit_type, hiLo + ,small_blind, big_blind, small_bet, big_bet) ) + #cursor.execute ("SELECT id FROM Gametypes WHERE siteId=%s AND type=%s AND category=%s + #AND limitType=%s AND smallBet=%s AND bigBet=%s", (site_id, type, category, limit_type, small_bet, big_bet)) + else: + cursor.execute( """INSERT INTO Gametypes(siteId, type, base, category, limitType + ,hiLo, smallBlind, bigBlind, smallBet, bigBet) + VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""" + , (site_id, type, base, category, limit_type + ,hiLo, small_bet, big_bet, 0, 0))#remember, for these bet means blind + #cursor.execute ("SELECT id FROM Gametypes WHERE siteId=%s AND type=%s AND category=%s + #AND limitType=%s AND smallBlind=%s AND bigBlind=%s", (site_id, type, category, limit_type, small_bet, big_bet)) + + #result=(db.insert_id(),) + result=(getLastInsertId(backend,db,cursor),) + + return result[0] +#end def recogniseGametypeID + +def recogniseTourneyTypeId(cursor, siteId, buyin, fee, knockout, rebuyOrAddon): + cursor.execute ("SELECT id FROM TourneyTypes WHERE siteId=%s AND buyin=%s AND fee=%s AND knockout=%s AND rebuyOrAddon=%s", (siteId, buyin, fee, knockout, rebuyOrAddon)) + result=cursor.fetchone() + #print "tried SELECTing gametypes.id, result:",result + + try: + len(result) + except TypeError:#this means we need to create a new entry + cursor.execute("""INSERT INTO TourneyTypes (siteId, buyin, fee, knockout, rebuyOrAddon) VALUES (%s, %s, %s, %s, %s)""", (siteId, buyin, fee, knockout, rebuyOrAddon)) + cursor.execute("SELECT id FROM TourneyTypes WHERE siteId=%s AND buyin=%s AND fee=%s AND knockout=%s AND rebuyOrAddon=%s", (siteId, buyin, fee, knockout, rebuyOrAddon)) + result=cursor.fetchone() + return result[0] +#end def recogniseTourneyTypeId + +#returns the SQL ids of the names given in an array +def recognisePlayerIDs(cursor, names, site_id): + result = [] + for i in range (len(names)): + cursor.execute ("SELECT id FROM Players WHERE name=%s", (names[i],)) + tmp=cursor.fetchall() + if (len(tmp)==0): #new player + cursor.execute ("INSERT INTO Players (name, siteId) VALUES (%s, %s)", (names[i], site_id)) + #print "Number of players rows inserted: %d" % cursor.rowcount + cursor.execute ("SELECT id FROM Players WHERE name=%s", (names[i],)) + tmp=cursor.fetchall() + #print "recognisePlayerIDs, names[i]:",names[i],"tmp:",tmp + result.append(tmp[0][0]) + return result +#end def recognisePlayerIDs + +#recognises the name in the given line and returns its array position in the given array +def recognisePlayerNo(line, names, atype): + #print "recogniseplayerno, names:",names + for i in range (len(names)): + if (atype=="unbet"): + if (line.endswith(names[i].encode("latin-1"))): + return (i) + elif (line.startswith("Dealt to ")): + #print "recognisePlayerNo, card precut, line:",line + tmp=line[9:] + #print "recognisePlayerNo, card postcut, tmp:",tmp + if (tmp.startswith(names[i].encode("latin-1"))): + return (i) + elif (line.startswith("Seat ")): + if (line.startswith("Seat 10")): + tmp=line[9:] + else: + tmp=line[8:] + + if (tmp.startswith(names[i].encode("latin-1"))): + return (i) + else: + if (line.startswith(names[i].encode("latin-1"))): + return (i) + #if we're here we mustve failed + raise FpdbError ("failed to recognise player in: "+line+" atype:"+atype) +#end def recognisePlayerNo + +#returns the site abbreviation for the given site +def recogniseSite(line): + if (line.startswith("Full Tilt Poker")): + return "ftp" + elif (line.startswith("PokerStars")): + return "ps" + else: + raise FpdbError("failed to recognise site, line:"+line) +#end def recogniseSite + +#returns the ID of the given site +def recogniseSiteID(cursor, site): + if (site=="ftp"): + return 1 + #cursor.execute("SELECT id FROM Sites WHERE name = ('Full Tilt Poker')") + elif (site=="ps"): + return 2 + #cursor.execute("SELECT id FROM Sites WHERE name = ('PokerStars')") + else: + raise FpdbError("invalid site in recogniseSiteID: "+site) + return cursor.fetchall()[0][0] +#end def recogniseSiteID + +#removes trailing \n from the given array +def removeTrailingEOL(arr): + for i in range(len(arr)): + if (arr[i].endswith("\n")): + #print "arr[i] before removetrailingEOL:", arr[i] + arr[i]=arr[i][:-1] + #print "arr[i] after removetrailingEOL:", arr[i] + return arr +#end def removeTrailingEOL + +#splits the rake according to the proportion of pot won. manipulates the second passed array. +def splitRake(winnings, rakes, totalRake): + winnercnt=0 + totalWin=0 + for i in range(len(winnings)): + if winnings[i]!=0: + winnercnt+=1 + totalWin+=winnings[i] + firstWinner=i + if winnercnt==1: + rakes[firstWinner]=totalRake + else: + totalWin=float(totalWin) + for i in range(len(winnings)): + if winnings[i]!=0: + winPortion=winnings[i]/totalWin + rakes[i]=totalRake*winPortion +#end def splitRake + +def storeActions(cursor, handsPlayersIds, actionTypes, allIns, actionAmounts, actionNos): +#stores into table hands_actions + #print "start of storeActions, actionNos:",actionNos + #print " action_amounts:",action_amounts + for i in range (len(actionTypes)): #iterate through streets + for j in range (len(actionTypes[i])): #iterate through names + for k in range (len(actionTypes[i][j])): #iterate through individual actions of that player on that street + cursor.execute ("INSERT INTO HandsActions (handPlayerId, street, actionNo, action, allIn, amount) VALUES (%s, %s, %s, %s, %s, %s)" + , (handsPlayersIds[j], i, actionNos[i][j][k], actionTypes[i][j][k], allIns[i][j][k], actionAmounts[i][j][k])) +#end def storeActions + +def store_board_cards(cursor, hands_id, board_values, board_suits): +#stores into table board_cards + cursor.execute ("""INSERT INTO BoardCards (handId, card1Value, card1Suit, +card2Value, card2Suit, card3Value, card3Suit, card4Value, card4Suit, +card5Value, card5Suit) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""", + (hands_id, board_values[0], board_suits[0], board_values[1], board_suits[1], + board_values[2], board_suits[2], board_values[3], board_suits[3], + board_values[4], board_suits[4])) +#end def store_board_cards + +def storeHands(backend, conn, cursor, site_hand_no, gametype_id + ,hand_start_time, names, tableName, maxSeats): +#stores into table hands + cursor.execute ("INSERT INTO Hands (siteHandNo, gametypeId, handStart, seats, tableName, importTime, maxSeats) VALUES (%s, %s, %s, %s, %s, %s, %s)", (site_hand_no, gametype_id, hand_start_time, len(names), tableName, datetime.datetime.today(), maxSeats)) + #todo: find a better way of doing this... + #cursor.execute("SELECT id FROM Hands WHERE siteHandNo=%s AND gametypeId=%s", (site_hand_no, gametype_id)) + #return cursor.fetchall()[0][0] + return getLastInsertId(backend, conn, cursor) + #return db.insert_id() # mysql only +#end def storeHands + +def store_hands_players_holdem_omaha(backend, conn, cursor, category, hands_id, player_ids, start_cashes + ,positions, card_values, card_suits, winnings, rakes, seatNos): + result=[] + if (category=="holdem"): + for i in range (len(player_ids)): + cursor.execute (""" +INSERT INTO HandsPlayers +(handId, playerId, startCash, position, +card1Value, card1Suit, card2Value, card2Suit, winnings, rake, seatNo) +VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""", + (hands_id, player_ids[i], start_cashes[i], positions[i], + card_values[i][0], card_suits[i][0], card_values[i][1], card_suits[i][1], + winnings[i], rakes[i], seatNos[i])) + #cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId=%s", (hands_id, player_ids[i])) + #result.append(cursor.fetchall()[0][0]) + result.append( getLastInsertId(backend, conn, cursor) ) # mysql only + elif (category=="omahahi" or category=="omahahilo"): + for i in range (len(player_ids)): + cursor.execute ("""INSERT INTO HandsPlayers +(handId, playerId, startCash, position, +card1Value, card1Suit, card2Value, card2Suit, +card3Value, card3Suit, card4Value, card4Suit, winnings, rake, seatNo) +VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""", + (hands_id, player_ids[i], start_cashes[i], positions[i], + card_values[i][0], card_suits[i][0], card_values[i][1], card_suits[i][1], + card_values[i][2], card_suits[i][2], card_values[i][3], card_suits[i][3], + winnings[i], rakes[i], seatNos[i])) + #cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[i])) + #result.append(cursor.fetchall()[0][0]) + result.append( getLastInsertId(backend, conn, cursor) ) # mysql only + else: + raise FpdbError("invalid category") + return result +#end def store_hands_players_holdem_omaha + +def store_hands_players_stud(backend, conn, cursor, hands_id, player_ids, start_cashes, antes, + card_values, card_suits, winnings, rakes, seatNos): +#stores hands_players rows for stud/razz games. returns an array of the resulting IDs + result=[] + #print "before inserts in store_hands_players_stud, antes:", antes + for i in range (len(player_ids)): + cursor.execute ("""INSERT INTO HandsPlayers +(handId, playerId, startCash, ante, +card1Value, card1Suit, card2Value, card2Suit, +card3Value, card3Suit, card4Value, card4Suit, +card5Value, card5Suit, card6Value, card6Suit, +card7Value, card7Suit, winnings, rake, seatNo) +VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, +%s, %s, %s, %s)""", + (hands_id, player_ids[i], start_cashes[i], antes[i], + card_values[i][0], card_suits[i][0], card_values[i][1], card_suits[i][1], + card_values[i][2], card_suits[i][2], card_values[i][3], card_suits[i][3], + card_values[i][4], card_suits[i][4], card_values[i][5], card_suits[i][5], + card_values[i][6], card_suits[i][6], winnings[i], rakes[i], seatNos[i])) + #cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[i])) + #result.append(cursor.fetchall()[0][0]) + result.append( getLastInsertId(backend, conn, cursor) ) # mysql only + return result +#end def store_hands_players_stud + +def store_hands_players_holdem_omaha_tourney(backend, conn, cursor, category, hands_id, player_ids + ,start_cashes, positions, card_values, card_suits + , winnings, rakes, seatNos, tourneys_players_ids): + #stores hands_players for tourney holdem/omaha hands + result=[] + for i in range (len(player_ids)): + if len(card_values[0])==2: + cursor.execute ("""INSERT INTO HandsPlayers +(handId, playerId, startCash, position, +card1Value, card1Suit, card2Value, card2Suit, +winnings, rake, tourneysPlayersId, seatNo) +VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""", + (hands_id, player_ids[i], start_cashes[i], positions[i], + card_values[i][0], card_suits[i][0], card_values[i][1], card_suits[i][1], + winnings[i], rakes[i], tourneys_players_ids[i], seatNos[i])) + elif len(card_values[0])==4: + cursor.execute ("""INSERT INTO HandsPlayers +(handId, playerId, startCash, position, +card1Value, card1Suit, card2Value, card2Suit, +card3Value, card3Suit, card4Value, card4Suit, +winnings, rake, tourneysPlayersId, seatNo) +VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""", + (hands_id, player_ids[i], start_cashes[i], positions[i], + card_values[i][0], card_suits[i][0], card_values[i][1], card_suits[i][1], + card_values[i][2], card_suits[i][2], card_values[i][3], card_suits[i][3], + winnings[i], rakes[i], tourneys_players_ids[i], seatNos[i])) + else: + raise FpdbError ("invalid card_values length:"+str(len(card_values[0]))) + #cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[i])) + #result.append(cursor.fetchall()[0][0]) + result.append( getLastInsertId(backend, conn, cursor) ) # mysql only + + return result +#end def store_hands_players_holdem_omaha_tourney + +def store_hands_players_stud_tourney(backend, conn, cursor, hands_id, player_ids, start_cashes, + antes, card_values, card_suits, winnings, rakes, seatNos, tourneys_players_ids): +#stores hands_players for tourney stud/razz hands + result=[] + for i in range (len(player_ids)): + cursor.execute ("""INSERT INTO HandsPlayers +(handId, playerId, startCash, ante, +card1Value, card1Suit, card2Value, card2Suit, +card3Value, card3Suit, card4Value, card4Suit, +card5Value, card5Suit, card6Value, card6Suit, +card7Value, card7Suit, winnings, rake, tourneysPlayersId, seatNo) +VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, +%s, %s, %s, %s, %s, %s)""", + (hands_id, player_ids[i], start_cashes[i], antes[i], + card_values[i][0], card_suits[i][0], card_values[i][1], card_suits[i][1], + card_values[i][2], card_suits[i][2], card_values[i][3], card_suits[i][3], + card_values[i][4], card_suits[i][4], card_values[i][5], card_suits[i][5], + card_values[i][6], card_suits[i][6], winnings[i], rakes[i], tourneys_players_ids[i], seatNos[i])) + #cursor.execute("SELECT id FROM HandsPlayers WHERE handId=%s AND playerId+0=%s", (hands_id, player_ids[i])) + #result.append(cursor.fetchall()[0][0]) + result.append( getLastInsertId(backend, conn, cursor) ) # mysql only + return result +#end def store_hands_players_stud_tourney + +def generateHudCacheData(player_ids, base, category, action_types, allIns, actionTypeByNo + ,winnings, totalWinnings, positions, actionTypes, actionAmounts): + """calculates data for the HUD during import. IMPORTANT: if you change this method make +sure to also change the following storage method and table_viewer.prepare_data if necessary +""" + #print "generateHudCacheData, len(player_ids)=", len(player_ids) + #setup subarrays of the result dictionary. + street0VPI=[] + street0Aggr=[] + street0_3B4BChance=[] + street0_3B4BDone=[] + street1Seen=[] + street2Seen=[] + street3Seen=[] + street4Seen=[] + sawShowdown=[] + street1Aggr=[] + street2Aggr=[] + street3Aggr=[] + street4Aggr=[] + otherRaisedStreet1=[] + otherRaisedStreet2=[] + otherRaisedStreet3=[] + otherRaisedStreet4=[] + foldToOtherRaisedStreet1=[] + foldToOtherRaisedStreet2=[] + foldToOtherRaisedStreet3=[] + foldToOtherRaisedStreet4=[] + wonWhenSeenStreet1=[] + + wonAtSD=[] + stealAttemptChance=[] + stealAttempted=[] + hudDataPositions=[] + + firstPfRaiseByNo=-1 + firstPfRaiserId=-1 + firstPfRaiserNo=-1 + firstPfCallByNo=-1 + firstPfCallerId=-1 + for i in range(len(actionTypeByNo[0])): + if actionTypeByNo[0][i][1]=="bet": + firstPfRaiseByNo=i + firstPfRaiserId=actionTypeByNo[0][i][0] + for j in range(len(player_ids)): + if player_ids[j]==firstPfRaiserId: + firstPfRaiserNo=j + break + break + for i in range(len(actionTypeByNo[0])): + if actionTypeByNo[0][i][1]=="call": + firstPfCallByNo=i + firstPfCallerId=actionTypeByNo[0][i][0] + break + + cutoffId=-1 + buttonId=-1 + sbId=-1 + bbId=-1 + if base=="hold": + for player in range(len(positions)): + if positions==1: + cutoffId=player_ids[player] + if positions==0: + buttonId=player_ids[player] + if positions=='S': + sbId=player_ids[player] + if positions=='B': + bbId=player_ids[player] + + someoneStole=False + + #run a loop for each player preparing the actual values that will be commited to SQL + for player in range (len(player_ids)): + #set default values + myStreet0VPI=False + myStreet0Aggr=False + myStreet0_3B4BChance=False + myStreet0_3B4BDone=False + myStreet1Seen=False + myStreet2Seen=False + myStreet3Seen=False + myStreet4Seen=False + mySawShowdown=False + myStreet1Aggr=False + myStreet2Aggr=False + myStreet3Aggr=False + myStreet4Aggr=False + myOtherRaisedStreet1=False + myOtherRaisedStreet2=False + myOtherRaisedStreet3=False + myOtherRaisedStreet4=False + myFoldToOtherRaisedStreet1=False + myFoldToOtherRaisedStreet2=False + myFoldToOtherRaisedStreet3=False + myFoldToOtherRaisedStreet4=False + myWonWhenSeenStreet1=0.0 + myWonAtSD=0.0 + myStealAttemptChance=False + myStealAttempted=False + + #calculate VPIP and PFR + street=0 + heroPfRaiseCount=0 + for count in range (len(action_types[street][player])):#finally individual actions + currentAction=action_types[street][player][count] + if currentAction=="bet": + myStreet0Aggr=True + if (currentAction=="bet" or currentAction=="call"): + myStreet0VPI=True + + #PF3B4BChance and PF3B4B + pfFold=-1 + pfRaise=-1 + if firstPfRaiseByNo!=-1: + for i in range(len(actionTypeByNo[0])): + if actionTypeByNo[0][i][0]==player_ids[player]: + if actionTypeByNo[0][i][1]=="bet" and pfRaise==-1 and i>firstPfRaiseByNo: + pfRaise=i + if actionTypeByNo[0][i][1]=="fold" and pfFold==-1: + pfFold=i + if pfFold==-1 or pfFold>firstPfRaiseByNo: + myStreet0_3B4BChance=True + if pfRaise>firstPfRaiseByNo: + myStreet0_3B4BDone=True + + #steal calculations + if base=="hold": + if len(player_ids)>=5: #no point otherwise + if positions[player]==1: + if firstPfRaiserId==player_ids[player]: + myStealAttemptChance=True + myStealAttempted=True + elif firstPfRaiserId==buttonId or firstPfRaiserId==sbId or firstPfRaiserId==bbId or firstPfRaiserId==-1: + myStealAttemptChance=True + if positions[player]==0: + if firstPfRaiserId==player_ids[player]: + myStealAttemptChance=True + myStealAttempted=True + elif firstPfRaiserId==sbId or firstPfRaiserId==bbId or firstPfRaiserId==-1: + myStealAttemptChance=True + if positions[player]=='S': + if firstPfRaiserId==player_ids[player]: + myStealAttemptChance=True + myStealAttempted=True + elif firstPfRaiserId==bbId or firstPfRaiserId==-1: + myStealAttemptChance=True + if positions[player]=='B': + pass + + if myStealAttempted: + someoneStole=True + + + #calculate saw* values + isAllIn=False + for i in range(len(allIns[0][player])): + if allIns[0][player][i]: + isAllIn=True + if (len(action_types[1][player])>0 or isAllIn): + myStreet1Seen=True + + for i in range(len(allIns[1][player])): + if allIns[1][player][i]: + isAllIn=True + if (len(action_types[2][player])>0 or isAllIn): + myStreet2Seen=True + + for i in range(len(allIns[2][player])): + if allIns[2][player][i]: + isAllIn=True + if (len(action_types[3][player])>0 or isAllIn): + myStreet3Seen=True + + #print "base:", base + if base=="hold": + mySawShowdown=True + for count in range (len(action_types[3][player])): + if action_types[3][player][count]=="fold": + mySawShowdown=False + else: + #print "in else" + for i in range(len(allIns[3][player])): + if allIns[3][player][i]: + isAllIn=True + if (len(action_types[4][player])>0 or isAllIn): + #print "in if" + myStreet4Seen=True + + mySawShowdown=True + for count in range (len(action_types[4][player])): + if action_types[4][player][count]=="fold": + mySawShowdown=False + + + #flop stuff + street=1 + if myStreet1Seen: + for count in range(len(action_types[street][player])): + if action_types[street][player][count]=="bet": + myStreet1Aggr=True + + for otherPlayer in range (len(player_ids)): + if player==otherPlayer: + pass + else: + for countOther in range (len(action_types[street][otherPlayer])): + if action_types[street][otherPlayer][countOther]=="bet": + myOtherRaisedStreet1=True + for countOtherFold in range (len(action_types[street][player])): + if action_types[street][player][countOtherFold]=="fold": + myFoldToOtherRaisedStreet1=True + + #turn stuff - copy of flop with different vars + street=2 + if myStreet2Seen: + for count in range(len(action_types[street][player])): + if action_types[street][player][count]=="bet": + myStreet2Aggr=True + + for otherPlayer in range (len(player_ids)): + if player==otherPlayer: + pass + else: + for countOther in range (len(action_types[street][otherPlayer])): + if action_types[street][otherPlayer][countOther]=="bet": + myOtherRaisedStreet2=True + for countOtherFold in range (len(action_types[street][player])): + if action_types[street][player][countOtherFold]=="fold": + myFoldToOtherRaisedStreet2=True + + #river stuff - copy of flop with different vars + street=3 + if myStreet3Seen: + for count in range(len(action_types[street][player])): + if action_types[street][player][count]=="bet": + myStreet3Aggr=True + + for otherPlayer in range (len(player_ids)): + if player==otherPlayer: + pass + else: + for countOther in range (len(action_types[street][otherPlayer])): + if action_types[street][otherPlayer][countOther]=="bet": + myOtherRaisedStreet3=True + for countOtherFold in range (len(action_types[street][player])): + if action_types[street][player][countOtherFold]=="fold": + myFoldToOtherRaisedStreet3=True + + #stud river stuff - copy of flop with different vars + street=4 + if myStreet4Seen: + for count in range(len(action_types[street][player])): + if action_types[street][player][count]=="bet": + myStreet4Aggr=True + + for otherPlayer in range (len(player_ids)): + if player==otherPlayer: + pass + else: + for countOther in range (len(action_types[street][otherPlayer])): + if action_types[street][otherPlayer][countOther]=="bet": + myOtherRaisedStreet4=True + for countOtherFold in range (len(action_types[street][player])): + if action_types[street][player][countOtherFold]=="fold": + myFoldToOtherRaisedStreet4=True + + if winnings[player]!=0: + if myStreet1Seen: + myWonWhenSeenStreet1=winnings[player]/float(totalWinnings) + if mySawShowdown: + myWonAtSD=myWonWhenSeenStreet1 + + #add each value to the appropriate array + street0VPI.append(myStreet0VPI) + street0Aggr.append(myStreet0Aggr) + street0_3B4BChance.append(myStreet0_3B4BChance) + street0_3B4BDone.append(myStreet0_3B4BDone) + street1Seen.append(myStreet1Seen) + street2Seen.append(myStreet2Seen) + street3Seen.append(myStreet3Seen) + street4Seen.append(myStreet4Seen) + sawShowdown.append(mySawShowdown) + street1Aggr.append(myStreet1Aggr) + street2Aggr.append(myStreet2Aggr) + street3Aggr.append(myStreet3Aggr) + street4Aggr.append(myStreet4Aggr) + otherRaisedStreet1.append(myOtherRaisedStreet1) + otherRaisedStreet2.append(myOtherRaisedStreet2) + otherRaisedStreet3.append(myOtherRaisedStreet3) + otherRaisedStreet4.append(myOtherRaisedStreet4) + foldToOtherRaisedStreet1.append(myFoldToOtherRaisedStreet1) + foldToOtherRaisedStreet2.append(myFoldToOtherRaisedStreet2) + foldToOtherRaisedStreet3.append(myFoldToOtherRaisedStreet3) + foldToOtherRaisedStreet4.append(myFoldToOtherRaisedStreet4) + wonWhenSeenStreet1.append(myWonWhenSeenStreet1) + wonAtSD.append(myWonAtSD) + stealAttemptChance.append(myStealAttemptChance) + stealAttempted.append(myStealAttempted) + if base=="hold": + pos=positions[player] + if pos=='B': + hudDataPositions.append('B') + elif pos=='S': + hudDataPositions.append('S') + elif pos==0: + hudDataPositions.append('D') + elif pos==1: + hudDataPositions.append('C') + elif pos>=2 and pos<=4: + hudDataPositions.append('M') + elif pos>=5 and pos<=7: + hudDataPositions.append('E') + ### RHH Added this elif to handle being a dead hand before the BB (pos==9) + elif pos==9: + hudDataPositions.append('X') + else: + raise FpdbError("invalid position") + elif base=="stud": + #todo: stud positions and steals + pass + + #add each array to the to-be-returned dictionary + result={'street0VPI':street0VPI} + result['street0Aggr']=street0Aggr + result['street0_3B4BChance']=street0_3B4BChance + result['street0_3B4BDone']=street0_3B4BDone + result['street1Seen']=street1Seen + result['street2Seen']=street2Seen + result['street3Seen']=street3Seen + result['street4Seen']=street4Seen + result['sawShowdown']=sawShowdown + + result['street1Aggr']=street1Aggr + result['otherRaisedStreet1']=otherRaisedStreet1 + result['foldToOtherRaisedStreet1']=foldToOtherRaisedStreet1 + result['street2Aggr']=street2Aggr + result['otherRaisedStreet2']=otherRaisedStreet2 + result['foldToOtherRaisedStreet2']=foldToOtherRaisedStreet2 + result['street3Aggr']=street3Aggr + result['otherRaisedStreet3']=otherRaisedStreet3 + result['foldToOtherRaisedStreet3']=foldToOtherRaisedStreet3 + result['street4Aggr']=street4Aggr + result['otherRaisedStreet4']=otherRaisedStreet4 + result['foldToOtherRaisedStreet4']=foldToOtherRaisedStreet4 + result['wonWhenSeenStreet1']=wonWhenSeenStreet1 + result['wonAtSD']=wonAtSD + result['stealAttemptChance']=stealAttemptChance + result['stealAttempted']=stealAttempted + + #now the various steal values + foldBbToStealChance=[] + foldedBbToSteal=[] + foldSbToStealChance=[] + foldedSbToSteal=[] + for player in range (len(player_ids)): + myFoldBbToStealChance=False + myFoldedBbToSteal=False + myFoldSbToStealChance=False + myFoldedSbToSteal=False + + if base=="hold": + if someoneStole and (positions[player]=='B' or positions[player]=='S') and firstPfRaiserId!=player_ids[player]: + street=0 + for count in range (len(action_types[street][player])):#individual actions + if positions[player]=='B': + myFoldBbToStealChance=True + if action_types[street][player][count]=="fold": + myFoldedBbToSteal=True + if positions[player]=='S': + myFoldSbToStealChance=True + if action_types[street][player][count]=="fold": + myFoldedSbToSteal=True + + + foldBbToStealChance.append(myFoldBbToStealChance) + foldedBbToSteal.append(myFoldedBbToSteal) + foldSbToStealChance.append(myFoldSbToStealChance) + foldedSbToSteal.append(myFoldedSbToSteal) + result['foldBbToStealChance']=foldBbToStealChance + result['foldedBbToSteal']=foldedBbToSteal + result['foldSbToStealChance']=foldSbToStealChance + result['foldedSbToSteal']=foldedSbToSteal + + #now CB + street1CBChance=[] + street1CBDone=[] + didStreet1CB=[] + for player in range (len(player_ids)): + myStreet1CBChance=False + myStreet1CBDone=False + + if street0VPI[player]: + myStreet1CBChance=True + if street1Aggr[player]: + myStreet1CBDone=True + didStreet1CB.append(player_ids[player]) + + street1CBChance.append(myStreet1CBChance) + street1CBDone.append(myStreet1CBDone) + result['street1CBChance']=street1CBChance + result['street1CBDone']=street1CBDone + + #now 2B + street2CBChance=[] + street2CBDone=[] + didStreet2CB=[] + for player in range (len(player_ids)): + myStreet2CBChance=False + myStreet2CBDone=False + + if street1CBDone[player]: + myStreet2CBChance=True + if street2Aggr[player]: + myStreet2CBDone=True + didStreet2CB.append(player_ids[player]) + + street2CBChance.append(myStreet2CBChance) + street2CBDone.append(myStreet2CBDone) + result['street2CBChance']=street2CBChance + result['street2CBDone']=street2CBDone + + #now 3B + street3CBChance=[] + street3CBDone=[] + didStreet3CB=[] + for player in range (len(player_ids)): + myStreet3CBChance=False + myStreet3CBDone=False + + if street2CBDone[player]: + myStreet3CBChance=True + if street3Aggr[player]: + myStreet3CBDone=True + didStreet3CB.append(player_ids[player]) + + street3CBChance.append(myStreet3CBChance) + street3CBDone.append(myStreet3CBDone) + result['street3CBChance']=street3CBChance + result['street3CBDone']=street3CBDone + + #and 4B + street4CBChance=[] + street4CBDone=[] + didStreet4CB=[] + for player in range (len(player_ids)): + myStreet4CBChance=False + myStreet4CBDone=False + + if street3CBDone[player]: + myStreet4CBChance=True + if street4Aggr[player]: + myStreet4CBDone=True + didStreet4CB.append(player_ids[player]) + + street4CBChance.append(myStreet4CBChance) + street4CBDone.append(myStreet4CBDone) + result['street4CBChance']=street4CBChance + result['street4CBDone']=street4CBDone + + + result['position']=hudDataPositions + + foldToStreet1CBChance=[] + foldToStreet1CBDone=[] + foldToStreet2CBChance=[] + foldToStreet2CBDone=[] + foldToStreet3CBChance=[] + foldToStreet3CBDone=[] + foldToStreet4CBChance=[] + foldToStreet4CBDone=[] + + for player in range (len(player_ids)): + myFoldToStreet1CBChance=False + myFoldToStreet1CBDone=False + foldToStreet1CBChance.append(myFoldToStreet1CBChance) + foldToStreet1CBDone.append(myFoldToStreet1CBDone) + + myFoldToStreet2CBChance=False + myFoldToStreet2CBDone=False + foldToStreet2CBChance.append(myFoldToStreet2CBChance) + foldToStreet2CBDone.append(myFoldToStreet2CBDone) + + myFoldToStreet3CBChance=False + myFoldToStreet3CBDone=False + foldToStreet3CBChance.append(myFoldToStreet3CBChance) + foldToStreet3CBDone.append(myFoldToStreet3CBDone) + + myFoldToStreet4CBChance=False + myFoldToStreet4CBDone=False + foldToStreet4CBChance.append(myFoldToStreet4CBChance) + foldToStreet4CBDone.append(myFoldToStreet4CBDone) + + if len(didStreet1CB)>=1: + generateFoldToCB(1, player_ids, didStreet1CB, street1CBDone, foldToStreet1CBChance, foldToStreet1CBDone, actionTypeByNo) + + if len(didStreet2CB)>=1: + generateFoldToCB(2, player_ids, didStreet2CB, street2CBDone, foldToStreet2CBChance, foldToStreet2CBDone, actionTypeByNo) + + if len(didStreet3CB)>=1: + generateFoldToCB(3, player_ids, didStreet3CB, street3CBDone, foldToStreet3CBChance, foldToStreet3CBDone, actionTypeByNo) + + if len(didStreet4CB)>=1: + generateFoldToCB(4, player_ids, didStreet4CB, street4CBDone, foldToStreet4CBChance, foldToStreet4CBDone, actionTypeByNo) + + result['foldToStreet1CBChance']=foldToStreet1CBChance + result['foldToStreet1CBDone']=foldToStreet1CBDone + result['foldToStreet2CBChance']=foldToStreet2CBChance + result['foldToStreet2CBDone']=foldToStreet2CBDone + result['foldToStreet3CBChance']=foldToStreet3CBChance + result['foldToStreet3CBDone']=foldToStreet3CBDone + result['foldToStreet4CBChance']=foldToStreet4CBChance + result['foldToStreet4CBDone']=foldToStreet4CBDone + + + totalProfit=[] + + street1CheckCallRaiseChance=[] + street1CheckCallRaiseDone=[] + street2CheckCallRaiseChance=[] + street2CheckCallRaiseDone=[] + street3CheckCallRaiseChance=[] + street3CheckCallRaiseDone=[] + street4CheckCallRaiseChance=[] + street4CheckCallRaiseDone=[] + #print "b4 totprof calc, len(playerIds)=", len(player_ids) + for pl in range (len(player_ids)): + #print "pl=", pl + myTotalProfit=winnings[pl] # still need to deduct costs + for i in range (len(actionTypes)): #iterate through streets + #for j in range (len(actionTypes[i])): #iterate through names (using pl loop above) + for k in range (len(actionTypes[i][pl])): #iterate through individual actions of that player on that street + myTotalProfit -= actionAmounts[i][pl][k] + + myStreet1CheckCallRaiseChance=False + myStreet1CheckCallRaiseDone=False + myStreet2CheckCallRaiseChance=False + myStreet2CheckCallRaiseDone=False + myStreet3CheckCallRaiseChance=False + myStreet3CheckCallRaiseDone=False + myStreet4CheckCallRaiseChance=False + myStreet4CheckCallRaiseDone=False + + #print "myTotalProfit=", myTotalProfit + totalProfit.append(myTotalProfit) + #print "totalProfit[]=", totalProfit + + street1CheckCallRaiseChance.append(myStreet1CheckCallRaiseChance) + street1CheckCallRaiseDone.append(myStreet1CheckCallRaiseDone) + street2CheckCallRaiseChance.append(myStreet2CheckCallRaiseChance) + street2CheckCallRaiseDone.append(myStreet2CheckCallRaiseDone) + street3CheckCallRaiseChance.append(myStreet3CheckCallRaiseChance) + street3CheckCallRaiseDone.append(myStreet3CheckCallRaiseDone) + street4CheckCallRaiseChance.append(myStreet4CheckCallRaiseChance) + street4CheckCallRaiseDone.append(myStreet4CheckCallRaiseDone) + + result['totalProfit']=totalProfit + #print "res[totalProfit]=", result['totalProfit'] + + result['street1CheckCallRaiseChance']=street1CheckCallRaiseChance + result['street1CheckCallRaiseDone']=street1CheckCallRaiseDone + result['street2CheckCallRaiseChance']=street2CheckCallRaiseChance + result['street2CheckCallRaiseDone']=street2CheckCallRaiseDone + result['street3CheckCallRaiseChance']=street3CheckCallRaiseChance + result['street3CheckCallRaiseDone']=street3CheckCallRaiseDone + result['street4CheckCallRaiseChance']=street4CheckCallRaiseChance + result['street4CheckCallRaiseDone']=street4CheckCallRaiseDone + return result +#end def generateHudCacheData + +def generateFoldToCB(street, playerIDs, didStreetCB, streetCBDone, foldToStreetCBChance, foldToStreetCBDone, actionTypeByNo): + """fills the passed foldToStreetCB* arrays appropriately depending on the given street""" + #print "beginning of generateFoldToCB, street:", street, "len(actionTypeByNo):", len(actionTypeByNo) + #print "len(actionTypeByNo[street]):",len(actionTypeByNo[street]) + firstCBReaction=0 + for action in range(len(actionTypeByNo[street])): + if actionTypeByNo[street][action][1]=="bet": + for player in didStreetCB: + if player==actionTypeByNo[street][action][0] and firstCBReaction==0: + firstCBReaction=action+1 + break + + for action in actionTypeByNo[street][firstCBReaction:]: + for player in range(len(playerIDs)): + if playerIDs[player]==action[0]: + foldToStreetCBChance[player]=True + if action[1]=="fold": + foldToStreetCBDone[player]=True +#end def generateFoldToCB + +def storeHudCache(cursor, base, category, gametypeId, playerIds, hudImportData): +# if (category=="holdem" or category=="omahahi" or category=="omahahilo"): + + #print "storeHudCache, len(playerIds)=", len(playerIds), " len(vpip)=" \ + #, len(hudImportData['street0VPI']), " len(totprof)=", len(hudImportData['totalProfit']) + for player in range (len(playerIds)): + if base=="hold": + cursor.execute("SELECT * FROM HudCache WHERE gametypeId+0=%s AND playerId=%s AND activeSeats=%s AND position=%s", (gametypeId, playerIds[player], len(playerIds), hudImportData['position'][player])) + else: + cursor.execute("SELECT * FROM HudCache WHERE gametypeId+0=%s AND playerId=%s AND activeSeats=%s", (gametypeId, playerIds[player], len(playerIds))) + row=cursor.fetchone() + #print "gametypeId:", gametypeId, "playerIds[player]",playerIds[player], "len(playerIds):",len(playerIds), "row:",row + + try: len(row) + except TypeError: + row=[] + + if (len(row)==0): + #print "new huddata row" + doInsert=True + row=[] + row.append(0)#blank for id + row.append(gametypeId) + row.append(playerIds[player]) + row.append(len(playerIds))#seats + for i in range(len(hudImportData)+2): + row.append(0) + + else: + doInsert=False + newrow=[] + for i in range(len(row)): + newrow.append(row[i]) + row=newrow + + if base=="hold": + row[4]=hudImportData['position'][player] + else: + row[4]=0 + row[5]=1 #tourneysGametypeId + row[6]+=1 #HDs + if hudImportData['street0VPI'][player]: row[7]+=1 + if hudImportData['street0Aggr'][player]: row[8]+=1 + if hudImportData['street0_3B4BChance'][player]: row[9]+=1 + if hudImportData['street0_3B4BDone'][player]: row[10]+=1 + if hudImportData['street1Seen'][player]: row[11]+=1 + if hudImportData['street2Seen'][player]: row[12]+=1 + if hudImportData['street3Seen'][player]: row[13]+=1 + if hudImportData['street4Seen'][player]: row[14]+=1 + if hudImportData['sawShowdown'][player]: row[15]+=1 + if hudImportData['street1Aggr'][player]: row[16]+=1 + if hudImportData['street2Aggr'][player]: row[17]+=1 + if hudImportData['street3Aggr'][player]: row[18]+=1 + if hudImportData['street4Aggr'][player]: row[19]+=1 + if hudImportData['otherRaisedStreet1'][player]: row[20]+=1 + if hudImportData['otherRaisedStreet2'][player]: row[21]+=1 + if hudImportData['otherRaisedStreet3'][player]: row[22]+=1 + if hudImportData['otherRaisedStreet4'][player]: row[23]+=1 + if hudImportData['foldToOtherRaisedStreet1'][player]: row[24]+=1 + if hudImportData['foldToOtherRaisedStreet2'][player]: row[25]+=1 + if hudImportData['foldToOtherRaisedStreet3'][player]: row[26]+=1 + if hudImportData['foldToOtherRaisedStreet4'][player]: row[27]+=1 + if hudImportData['wonWhenSeenStreet1'][player]!=0.0: row[28]+=hudImportData['wonWhenSeenStreet1'][player] + if hudImportData['wonAtSD'][player]!=0.0: row[29]+=hudImportData['wonAtSD'][player] + if hudImportData['stealAttemptChance'][player]: row[30]+=1 + if hudImportData['stealAttempted'][player]: row[31]+=1 + if hudImportData['foldBbToStealChance'][player]: row[32]+=1 + if hudImportData['foldedBbToSteal'][player]: row[33]+=1 + if hudImportData['foldSbToStealChance'][player]: row[34]+=1 + if hudImportData['foldedSbToSteal'][player]: row[35]+=1 + + if hudImportData['street1CBChance'][player]: row[36]+=1 + if hudImportData['street1CBDone'][player]: row[37]+=1 + if hudImportData['street2CBChance'][player]: row[38]+=1 + if hudImportData['street2CBDone'][player]: row[39]+=1 + if hudImportData['street3CBChance'][player]: row[40]+=1 + if hudImportData['street3CBDone'][player]: row[41]+=1 + if hudImportData['street4CBChance'][player]: row[42]+=1 + if hudImportData['street4CBDone'][player]: row[43]+=1 + + if hudImportData['foldToStreet1CBChance'][player]: row[44]+=1 + if hudImportData['foldToStreet1CBDone'][player]: row[45]+=1 + if hudImportData['foldToStreet2CBChance'][player]: row[46]+=1 + if hudImportData['foldToStreet2CBDone'][player]: row[47]+=1 + if hudImportData['foldToStreet3CBChance'][player]: row[48]+=1 + if hudImportData['foldToStreet3CBDone'][player]: row[49]+=1 + if hudImportData['foldToStreet4CBChance'][player]: row[50]+=1 + if hudImportData['foldToStreet4CBDone'][player]: row[51]+=1 + + #print "player=", player + #print "len(totalProfit)=", len(hudImportData['totalProfit']) + if hudImportData['totalProfit'][player]: + row[52]+=hudImportData['totalProfit'][player] + + if hudImportData['street1CheckCallRaiseChance'][player]: row[53]+=1 + if hudImportData['street1CheckCallRaiseDone'][player]: row[54]+=1 + if hudImportData['street2CheckCallRaiseChance'][player]: row[55]+=1 + if hudImportData['street2CheckCallRaiseDone'][player]: row[56]+=1 + if hudImportData['street3CheckCallRaiseChance'][player]: row[57]+=1 + if hudImportData['street3CheckCallRaiseDone'][player]: row[58]+=1 + if hudImportData['street4CheckCallRaiseChance'][player]: row[59]+=1 + if hudImportData['street4CheckCallRaiseDone'][player]: row[60]+=1 + + if doInsert: + #print "playerid before insert:",row[2] + cursor.execute("""INSERT INTO HudCache +(gametypeId, playerId, activeSeats, position, tourneyTypeId, +HDs, street0VPI, street0Aggr, street0_3B4BChance, street0_3B4BDone, +street1Seen, street2Seen, street3Seen, street4Seen, sawShowdown, +street1Aggr, street2Aggr, street3Aggr, street4Aggr, otherRaisedStreet1, +otherRaisedStreet2, otherRaisedStreet3, otherRaisedStreet4, foldToOtherRaisedStreet1, foldToOtherRaisedStreet2, +foldToOtherRaisedStreet3, foldToOtherRaisedStreet4, wonWhenSeenStreet1, wonAtSD, stealAttemptChance, +stealAttempted, foldBbToStealChance, foldedBbToSteal, foldSbToStealChance, foldedSbToSteal, +street1CBChance, street1CBDone, street2CBChance, street2CBDone, street3CBChance, +street3CBDone, street4CBChance, street4CBDone, foldToStreet1CBChance, foldToStreet1CBDone, +foldToStreet2CBChance, foldToStreet2CBDone, foldToStreet3CBChance, foldToStreet3CBDone, foldToStreet4CBChance, +foldToStreet4CBDone, totalProfit, street1CheckCallRaiseChance, street1CheckCallRaiseDone, street2CheckCallRaiseChance, +street2CheckCallRaiseDone, street3CheckCallRaiseChance, street3CheckCallRaiseDone, street4CheckCallRaiseChance, street4CheckCallRaiseDone) +VALUES (%s, %s, %s, %s, %s, +%s, %s, %s, %s, %s, +%s, %s, %s, %s, %s, +%s, %s, %s, %s, %s, +%s, %s, %s, %s, %s, +%s, %s, %s, %s, %s, +%s, %s, %s, %s, %s, +%s, %s, %s, %s, %s, +%s, %s, %s, %s, %s, +%s, %s, %s, %s, %s, +%s, %s, %s, %s, %s, +%s, %s, %s, %s, %s)""", (row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8], row[9], row[10], row[11], row[12], row[13], row[14], row[15], row[16], row[17], row[18], row[19], row[20], row[21], row[22], row[23], row[24], row[25], row[26], row[27], row[28], row[29], row[30], row[31], row[32], row[33], row[34], row[35], row[36], row[37], row[38], row[39], row[40], row[41], row[42], row[43], row[44], row[45], row[46], row[47], row[48], row[49], row[50], row[51], row[52], row[53], row[54], row[55], row[56], row[57], row[58], row[59], row[60])) + else: + #print "storing updated hud data line" + cursor.execute("""UPDATE HudCache +SET HDs=%s, street0VPI=%s, street0Aggr=%s, street0_3B4BChance=%s, street0_3B4BDone=%s, +street1Seen=%s, street2Seen=%s, street3Seen=%s, street4Seen=%s, sawShowdown=%s, +street1Aggr=%s, street2Aggr=%s, street3Aggr=%s, street4Aggr=%s, otherRaisedStreet1=%s, +otherRaisedStreet2=%s, otherRaisedStreet3=%s, otherRaisedStreet4=%s, foldToOtherRaisedStreet1=%s, foldToOtherRaisedStreet2=%s, +foldToOtherRaisedStreet3=%s, foldToOtherRaisedStreet4=%s, wonWhenSeenStreet1=%s, wonAtSD=%s, stealAttemptChance=%s, +stealAttempted=%s, foldBbToStealChance=%s, foldedBbToSteal=%s, foldSbToStealChance=%s, foldedSbToSteal=%s, +street1CBChance=%s, street1CBDone=%s, street2CBChance=%s, street2CBDone=%s, street3CBChance=%s, +street3CBDone=%s, street4CBChance=%s, street4CBDone=%s, foldToStreet1CBChance=%s, foldToStreet1CBDone=%s, +foldToStreet2CBChance=%s, foldToStreet2CBDone=%s, foldToStreet3CBChance=%s, foldToStreet3CBDone=%s, foldToStreet4CBChance=%s, +foldToStreet4CBDone=%s, totalProfit=%s, street1CheckCallRaiseChance=%s, street1CheckCallRaiseDone=%s, street2CheckCallRaiseChance=%s, +street2CheckCallRaiseDone=%s, street3CheckCallRaiseChance=%s, street3CheckCallRaiseDone=%s, street4CheckCallRaiseChance=%s, street4CheckCallRaiseDone=%s +WHERE gametypeId=%s AND playerId=%s AND activeSeats=%s AND position=%s AND tourneyTypeId=%s""", (row[6], row[7], row[8], row[9], row[10], + row[11], row[12], row[13], row[14], row[15], + row[16], row[17], row[18], row[19], row[20], + row[21], row[22], row[23], row[24], row[25], + row[26], row[27], row[28], row[29], row[30], + row[31], row[32], row[33], row[34], row[35], row[36], row[37], row[38], row[39], row[40], + row[41], row[42], row[43], row[44], row[45], row[46], row[47], row[48], row[49], row[50], + row[51], row[52], row[53], row[54], row[55], row[56], row[57], row[58], row[59], row[60], + row[1], row[2], row[3], str(row[4]), row[5])) +# else: +# print "todo: implement storeHudCache for stud base" +#end def storeHudCache + +def store_tourneys(cursor, tourneyTypeId, siteTourneyNo, entries, prizepool, startTime): + cursor.execute("SELECT id FROM Tourneys WHERE siteTourneyNo=%s AND tourneyTypeId+0=%s", (siteTourneyNo, tourneyTypeId)) + tmp=cursor.fetchone() + #print "tried SELECTing tourneys.id, result:",tmp + + try: + len(tmp) + except TypeError:#means we have to create new one + cursor.execute("""INSERT INTO Tourneys +(tourneyTypeId, siteTourneyNo, entries, prizepool, startTime) +VALUES (%s, %s, %s, %s, %s)""", (tourneyTypeId, siteTourneyNo, entries, prizepool, startTime)) + cursor.execute("SELECT id FROM Tourneys WHERE siteTourneyNo=%s AND tourneyTypeId+0=%s", (siteTourneyNo, tourneyTypeId)) + tmp=cursor.fetchone() + #print "created new tourneys.id:",tmp + return tmp[0] +#end def store_tourneys + +def store_tourneys_players(cursor, tourney_id, player_ids, payin_amounts, ranks, winnings): + result=[] + #print "in store_tourneys_players. tourney_id:",tourney_id + #print "player_ids:",player_ids + #print "payin_amounts:",payin_amounts + #print "ranks:",ranks + #print "winnings:",winnings + for i in range (len(player_ids)): + cursor.execute("SELECT id FROM TourneysPlayers WHERE tourneyId=%s AND playerId+0=%s", (tourney_id, player_ids[i])) + tmp=cursor.fetchone() + #print "tried SELECTing tourneys_players.id:",tmp + + try: + len(tmp) + except TypeError: + cursor.execute("""INSERT INTO TourneysPlayers +(tourneyId, playerId, payinAmount, rank, winnings) VALUES (%s, %s, %s, %s, %s)""", + (tourney_id, player_ids[i], payin_amounts[i], ranks[i], winnings[i])) + + cursor.execute("SELECT id FROM TourneysPlayers WHERE tourneyId=%s AND playerId+0=%s", + (tourney_id, player_ids[i])) + tmp=cursor.fetchone() + #print "created new tourneys_players.id:",tmp + result.append(tmp[0]) + return result +#end def store_tourneys_players From a429cbb6e1f3c9e0d886890584de5f43a9ae419c Mon Sep 17 00:00:00 2001 From: sqlcoder Date: Sun, 14 Dec 2008 02:23:40 +0000 Subject: [PATCH 04/43] added new routines to drop and recreate indexes and foreign keys. These could be called from any combination of standalone menu options, as part of the database re-create option or as part of the bulk import option --- pyfpdb/fpdb_simple.py | 303 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 302 insertions(+), 1 deletion(-) diff --git a/pyfpdb/fpdb_simple.py b/pyfpdb/fpdb_simple.py index e03666fc..8bfd3ba9 100755 --- a/pyfpdb/fpdb_simple.py +++ b/pyfpdb/fpdb_simple.py @@ -27,6 +27,307 @@ MYSQL_INNODB=2 PGSQL=3 SQLITE=4 +# Data Structures for index and foreign key creation +# drop_code is an int with possible values: 0 - don't drop for bulk import +# 1 - drop during bulk import +# db differences: +# - note that mysql automatically creates indexes on constrained columns when +# foreign keys are created, while postgres does not. Hence the much longer list +# of indexes is required for postgres. +# all primary keys are left on all the time +# +# table column drop_code + +indexes = [ + [ ] # no db with index 0 + , [ ] # no db with index 1 + , [ # indexes for mysql (list index 2) + {'tab':'Players', 'col':'name', 'drop':0} + , {'tab':'Hands', 'col':'siteHandNo', 'drop':0} + , {'tab':'Tourneys', 'col':'siteTourneyNo', 'drop':0} + ] + , [ # indexes for postgres (list index 3) + {'tab':'Boardcards', 'col':'handId', 'drop':0} + , {'tab':'Gametypes', 'col':'siteId', 'drop':0} + , {'tab':'Hands', 'col':'gametypeId', 'drop':1} + , {'tab':'Hands', 'col':'siteHandNo', 'drop':0} + , {'tab':'HandsActions', 'col':'handplayerId', 'drop':0} + , {'tab':'HandsPlayers', 'col':'handId', 'drop':1} + , {'tab':'HandsPlayers', 'col':'playerId', 'drop':1} + , {'tab':'HandsPlayers', 'col':'tourneysPlayersId', 'drop':0} + , {'tab':'HudCache', 'col':'gametypeId', 'drop':1} + , {'tab':'HudCache', 'col':'playerId', 'drop':0} + , {'tab':'HudCache', 'col':'tourneyTypeId', 'drop':0} + , {'tab':'Players', 'col':'siteId', 'drop':1} + , {'tab':'Players', 'col':'name', 'drop':0} + , {'tab':'Tourneys', 'col':'tourneyTypeId', 'drop':1} + , {'tab':'Tourneys', 'col':'siteTourneyNo', 'drop':0} + , {'tab':'TourneysPlayers', 'col':'playerId', 'drop':0} + , {'tab':'TourneysPlayers', 'col':'tourneyId', 'drop':0} + , {'tab':'TourneyTypes', 'col':'siteId', 'drop':0} + ] + ] + +foreignKeys = [ + [ ] # no db with index 0 + , [ ] # no db with index 1 + , [ # foreign keys for mysql + {'fktab':'Hands', 'fkcol':'gametypeId', 'rtab':'Gametypes', 'rcol':'id', 'drop':1} + , {'fktab':'HandsPlayers', 'fkcol':'handId', 'rtab':'Hands', 'rcol':'id', 'drop':1} + , {'fktab':'HandsPlayers', 'fkcol':'playerId', 'rtab':'Players', 'rcol':'id', 'drop':1} + , {'fktab':'HandsActions', 'fkcol':'handPlayerId', 'rtab':'HandsPlayers', 'rcol':'id', 'drop':1} + , {'fktab':'HudCache', 'fkcol':'gametypeId', 'rtab':'Gametypes', 'rcol':'id', 'drop':1} + , {'fktab':'HudCache', 'fkcol':'playerId', 'rtab':'Players', 'rcol':'id', 'drop':0} + , {'fktab':'HudCache', 'fkcol':'tourneyTypeId', 'rtab':'TourneyTypes', 'rcol':'id', 'drop':1} + ] + , [ # foreign keys for postgres + {'fktab':'Hands', 'fkcol':'gametypeId', 'rtab':'Gametypes', 'rcol':'id', 'drop':1} + , {'fktab':'HandsPlayers', 'fkcol':'handId', 'rtab':'Hands', 'rcol':'id', 'drop':1} + , {'fktab':'HandsPlayers', 'fkcol':'playerId', 'rtab':'Players', 'rcol':'id', 'drop':1} + , {'fktab':'HandsActions', 'fkcol':'handPlayerId', 'rtab':'HandsPlayers', 'rcol':'id', 'drop':1} + , {'fktab':'HudCache', 'fkcol':'gametypeId', 'rtab':'Gametypes', 'rcol':'id', 'drop':1} + , {'fktab':'HudCache', 'fkcol':'playerId', 'rtab':'Players', 'rcol':'id', 'drop':0} + , {'fktab':'HudCache', 'fkcol':'tourneyTypeId', 'rtab':'TourneyTypes', 'rcol':'id', 'drop':1} + ] + ] + + +# MySQL Notes: +# "FOREIGN KEY (handId) REFERENCES Hands(id)" - requires index on Hands.id +# - creates index handId on .handId +# alter table t drop foreign key fk +# alter table t add foreign key (fkcol) references tab(rcol) +# alter table t add constraint c foreign key (fkcol) references tab(rcol) +# (fkcol is used for foreigh key name) + +# mysql to list indexes: +# SELECT table_name, index_name, non_unique, column_name +# FROM INFORMATION_SCHEMA.STATISTICS +# WHERE table_name = 'tbl_name' +# AND table_schema = 'db_name' +# ORDER BY table_name, index_name, seq_in_index +# +# ALTER TABLE Tourneys ADD INDEX siteTourneyNo(siteTourneyNo) +# ALTER TABLE tab DROP INDEX idx + +# mysql to list fks: +# SELECT constraint_name, table_name, column_name, referenced_table_name, referenced_column_name +# FROM information_schema.KEY_COLUMN_USAGE +# WHERE REFERENCED_TABLE_SCHEMA = (your schema name here) +# AND REFERENCED_TABLE_NAME is not null +# ORDER BY TABLE_NAME, COLUMN_NAME; + +# this may indicate missing object +# _mysql_exceptions.OperationalError: (1025, "Error on rename of '.\\fpdb\\hands' to '.\\fpdb\\#sql2-7f0-1b' (errno: 152)") + + +# PG notes: + +# To add a foreign key constraint to a table: +# ALTER TABLE tab ADD CONSTRAINT c FOREIGN KEY (col) REFERENCES t2(col2) MATCH FULL; +# ALTER TABLE tab DROP CONSTRAINT zipchk +# +# Note: index names must be unique across a schema +# CREATE INDEX idx ON tab(col) +# DROP INDEX idx + +def prepareBulkImport(fdb): + """Drop some indexes/foreign keys to prepare for bulk import. + Currently keeping the standalone indexes as needed to import quickly""" + # fdb is a fpdb_db object including backend, db, cursor, sql variables + if fdb.backend == PGSQL: + fdb.db.set_isolation_level(0) # allow table/index operations to work + for fk in foreignKeys[fdb.backend]: + if fk['drop'] == 1: + if fdb.backend == MYSQL_INNODB: + fdb.cursor.execute("SELECT constraint_name " + + "FROM information_schema.KEY_COLUMN_USAGE " + + #"WHERE REFERENCED_TABLE_SCHEMA = 'fpdb' + "WHERE 1=1 " + + "AND table_name = %s AND column_name = %s " + + "AND referenced_table_name = %s " + + "AND referenced_column_name = %s ", + (fk['fktab'], fk['fkcol'], fk['rtab'], fk['rcol']) ) + cons = fdb.cursor.fetchone() + print "preparebulk: cons=", cons + if cons: + print "dropping mysql fk", cons[0], fk['fktab'], fk['fkcol'] + try: + fdb.cursor.execute("alter table " + fk['fktab'] + " drop foreign key " + cons[0]) + except: + pass + elif fdb.backend == PGSQL: + print "dropping pg fk", fk['fktab'], fk['fkcol'] + try: + fdb.cursor.execute("alter table " + fk['fktab'] + " drop constraint " + + fk['fktab'] + '_' + fk['fkcol'] + '_fkey') + except: + pass + else: + print "Only MySQL and Postgres supported so far" + return -1 + + for idx in indexes[fdb.backend]: + if idx['drop'] == 1: + if fdb.backend == MYSQL_INNODB: + print "dropping mysql index ", idx['tab'], idx['col'] + try: + fdb.cursor.execute( "alter table %s drop index %s", (idx['tab'],idx['col']) ) + except: + pass + elif fdb.backend == PGSQL: + print "dropping pg index ", idx['tab'], idx['col'] + # mod to use tab_col for index name? + try: + fdb.cursor.execute( "drop index %s_%s_idx" % (idx['tab'],idx['col']) ) + except: + pass + else: + print "Only MySQL and Postgres supported so far" + return -1 + + if fdb.backend == PGSQL: + fdb.db.set_isolation_level(1) # go back to normal isolation level + fdb.db.commit() # seems to clear up errors if there were any in postgres +#end def prepareBulkImport + +def afterBulkImport(fdb): + """Re-create any dropped indexes/foreign keys after bulk import""" + # fdb is a fpdb_db object including backend, db, cursor, sql variables + if fdb.backend == PGSQL: + fdb.db.set_isolation_level(0) # allow table/index operations to work + for fk in foreignKeys[fdb.backend]: + if fk['drop'] == 1: + if fdb.backend == MYSQL_INNODB: + fdb.cursor.execute("SELECT constraint_name " + + "FROM information_schema.KEY_COLUMN_USAGE " + + #"WHERE REFERENCED_TABLE_SCHEMA = 'fpdb' + "WHERE 1=1 " + + "AND table_name = %s AND column_name = %s " + + "AND referenced_table_name = %s " + + "AND referenced_column_name = %s ", + (fk['fktab'], fk['fkcol'], fk['rtab'], fk['rcol']) ) + cons = fdb.cursor.fetchone() + print "afterbulk: cons=", cons + if cons: + pass + else: + print "creating fk ", fk['fktab'], fk['fkcol'], "->", fk['rtab'], fk['rcol'] + try: + fdb.cursor.execute("alter table " + fk['fktab'] + " add foreign key (" + + fk['fkcol'] + ") references " + fk['rtab'] + "(" + + fk['rcol'] + ")") + except: + pass + elif fdb.backend == PGSQL: + print "creating fk ", fk['fktab'], fk['fkcol'], "->", fk['rtab'], fk['rcol'] + try: + fdb.cursor.execute("alter table " + fk['fktab'] + " add constraint " + + fk['fktab'] + '_' + fk['fkcol'] + '_fkey' + + " foreign key (" + fk['fkcol'] + + ") references " + fk['rtab'] + "(" + fk['rcol'] + ")") + except: + pass + else: + print "Only MySQL and Postgres supported so far" + return -1 + + for idx in indexes[fdb.backend]: + if idx['drop'] == 1: + if fdb.backend == MYSQL_INNODB: + print "creating mysql index ", idx['tab'], idx['col'] + try: + fdb.cursor.execute( "alter table %s add index %s(%s)" + , (idx['tab'],idx['col'],idx['col']) ) + except: + pass + elif fdb.backend == PGSQL: + # mod to use tab_col for index name? + print "creating pg index ", idx['tab'], idx['col'] + try: + print "create index %s_%s_idx on %s(%s)" % (idx['tab'], idx['col'], idx['tab'], idx['col']) + fdb.cursor.execute( "create index %s_%s_idx on %s(%s)" + % (idx['tab'], idx['col'], idx['tab'], idx['col']) ) + except: + print " ERROR! :-(" + pass + else: + print "Only MySQL and Postgres supported so far" + return -1 + + if fdb.backend == PGSQL: + fdb.db.set_isolation_level(1) # go back to normal isolation level + fdb.db.commit() # seems to clear up errors if there were any in postgres +#end def afterBulkImport + +def createAllIndexes(fdb): + """Create new indexes""" + if fdb.backend == PGSQL: + fdb.db.set_isolation_level(0) # allow table/index operations to work + for idx in indexes[fdb.backend]: + if fdb.backend == MYSQL_INNODB: + print "creating mysql index ", idx['tab'], idx['col'] + try: + fdb.cursor.execute( "alter table %s add index %s(%s)" + , (idx['tab'],idx['col'],idx['col']) ) + except: + pass + elif fdb.backend == PGSQL: + # mod to use tab_col for index name? + print "creating pg index ", idx['tab'], idx['col'] + try: + print "create index %s_%s_idx on %s(%s)" % (idx['tab'], idx['col'], idx['tab'], idx['col']) + fdb.cursor.execute( "create index %s_%s_idx on %s(%s)" + % (idx['tab'], idx['col'], idx['tab'], idx['col']) ) + except: + print " ERROR! :-(" + pass + else: + print "Only MySQL and Postgres supported so far" + return -1 + if fdb.backend == PGSQL: + fdb.db.set_isolation_level(1) # go back to normal isolation level +#end def createAllIndexes + +def dropAllIndexes(fdb): + """Drop all standalone indexes (i.e. not including primary keys or foreign keys) + using list of indexes in indexes data structure""" + # maybe upgrade to use data dictionary?? (but take care to exclude PK and FK) + if fdb.backend == PGSQL: + fdb.db.set_isolation_level(0) # allow table/index operations to work + for idx in indexes[fdb.backend]: + if fdb.backend == MYSQL_INNODB: + print "dropping mysql index ", idx['tab'], idx['col'] + try: + fdb.cursor.execute( "alter table %s drop index %s" + , (idx['tab'],idx['col']) ) + except: + pass + elif fdb.backend == PGSQL: + print "dropping pg index ", idx['tab'], idx['col'] + # mod to use tab_col for index name? + try: + fdb.cursor.execute( "drop index %s_%s_idx" + % (idx['tab'],idx['col']) ) + except: + pass + else: + print "Only MySQL and Postgres supported so far" + return -1 + if fdb.backend == PGSQL: + fdb.db.set_isolation_level(1) # go back to normal isolation level +#end def dropAllIndexes + +def analyzeDB(fdb): + """Do whatever the DB can offer to update index/table statistics""" + if fdb.backend == PGSQL: + fdb.db.set_isolation_level(0) # allow vacuum to work + try: + fdb.cursor.execute("vacuum analyze") + except: + print "Error during vacuum" + fdb.db.set_isolation_level(1) # go back to normal isolation level +#end def analyzeDB class DuplicateError(Exception): def __init__(self, value): @@ -39,7 +340,7 @@ class FpdbError(Exception): self.value = value def __str__(self): return repr(self.value) - + # gets value for last auto-increment key generated # returns -1 if a problem occurs def getLastInsertId(backend, conn, cursor): From d4e03424bfa5966f4f07ba8974f207237d012011 Mon Sep 17 00:00:00 2001 From: sqlcoder Date: Sun, 14 Dec 2008 02:30:19 +0000 Subject: [PATCH 05/43] call routines to drop and recreate some indexes and foreign keys before and after bulk import --- pyfpdb/fpdb_import.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyfpdb/fpdb_import.py b/pyfpdb/fpdb_import.py index 306c0880..2d35a97a 100644 --- a/pyfpdb/fpdb_import.py +++ b/pyfpdb/fpdb_import.py @@ -116,8 +116,11 @@ class Importer: #Run full import on filelist def runImport(self): + fpdb_simple.prepareBulkImport(self.fdb) for file in self.filelist: self.import_file_dict(file, self.filelist[file][0], self.filelist[file][1]) + fpdb_simple.afterBulkImport(self.fdb) + fpdb_simple.analyzeDB(self.fdb) #Run import on updated files, then store latest update time. def runUpdated(self): From fb6b8e5a5bd7827af5003c47dca40b31e9dc2f1a Mon Sep 17 00:00:00 2001 From: sqlcoder Date: Sun, 14 Dec 2008 02:38:09 +0000 Subject: [PATCH 06/43] add variable at top of file to control whether actions are saved or not and commented out timing debug statement --- pyfpdb/fpdb_save_to_db.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/pyfpdb/fpdb_save_to_db.py b/pyfpdb/fpdb_save_to_db.py index 3a7989ca..a7e75e7f 100644 --- a/pyfpdb/fpdb_save_to_db.py +++ b/pyfpdb/fpdb_save_to_db.py @@ -22,6 +22,11 @@ from time import time import fpdb_simple +saveActions=False # set this to False to avoid storing action data + # Pros: speeds up imports + # Cons: no action data is saved, need to keep the hand histories + # variance not available on stats page + #stores a stud/razz hand into the database def ring_stud(backend, db, cursor, base, category, site_hand_no, gametype_id, hand_start_time ,names, player_ids, start_cashes, antes, card_values, card_suits, winnings, rakes @@ -39,8 +44,9 @@ def ring_stud(backend, db, cursor, base, category, site_hand_no, gametype_id, ha fpdb_simple.storeHudCache(cursor, base, category, gametype_id, player_ids, hudImportData) - fpdb_simple.storeActions(cursor, hands_players_ids, action_types - ,allIns, action_amounts, actionNos) + if saveActions: + fpdb_simple.storeActions(cursor, hands_players_ids, action_types + ,allIns, action_amounts, actionNos) return hands_id #end def ring_stud @@ -66,10 +72,10 @@ def ring_holdem_omaha(backend, db, cursor, base, category, site_hand_no, gametyp t5 = time() fpdb_simple.store_board_cards(cursor, hands_id, board_values, board_suits) t6 = time() - fpdb_simple.storeActions(cursor, hands_players_ids, action_types, allIns, action_amounts, actionNos) + if saveActions: + fpdb_simple.storeActions(cursor, hands_players_ids, action_types, allIns, action_amounts, actionNos) t7 = time() - print "cards=%4.3f board=%4.3f hands=%4.3f plyrs=%4.3f hudcache=%4.3f board=%4.3f actions=%4.3f" \ - % (t1-t0, t2-t1, t3-t2, t4-t3, t5-t4, t6-t5, t7-t6) + #print "fills=(%4.3f) saves=(%4.3f,%4.3f,%4.3f,%4.3f)" % (t2-t0, t3-t2, t4-t3, t5-t4, t6-t5) return hands_id #end def ring_holdem_omaha @@ -98,7 +104,8 @@ def tourney_holdem_omaha(backend, db, cursor, base, category, siteTourneyNo, buy fpdb_simple.store_board_cards(cursor, hands_id, board_values, board_suits) - fpdb_simple.storeActions(cursor, hands_players_ids, action_types, allIns, action_amounts, actionNos) + if saveActions: + fpdb_simple.storeActions(cursor, hands_players_ids, action_types, allIns, action_amounts, actionNos) return hands_id #end def tourney_holdem_omaha @@ -122,6 +129,7 @@ def tourney_stud(backend, db, cursor, base, category, siteTourneyNo, buyin, fee, fpdb_simple.storeHudCache(cursor, base, category, gametypeId, playerIds, hudImportData) - fpdb_simple.storeActions(cursor, hands_players_ids, actionTypes, allIns, actionAmounts, actionNos) + if saveActions: + fpdb_simple.storeActions(cursor, hands_players_ids, actionTypes, allIns, actionAmounts, actionNos) return hands_id #end def tourney_stud From 26506b3421c5a23497ce56ec7c741b7f391ab12b Mon Sep 17 00:00:00 2001 From: sqlcoder Date: Sun, 14 Dec 2008 02:42:07 +0000 Subject: [PATCH 07/43] use new routine in fpdb_simple to create indexes --- pyfpdb/fpdb_db.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pyfpdb/fpdb_db.py b/pyfpdb/fpdb_db.py index e1c61c6d..e51538ee 100644 --- a/pyfpdb/fpdb_db.py +++ b/pyfpdb/fpdb_db.py @@ -108,9 +108,9 @@ class fpdb_db: self.cursor.execute(self.sql.query['createHandsPlayersTable']) self.cursor.execute(self.sql.query['createHandsActionsTable']) self.cursor.execute(self.sql.query['createHudCacheTable']) - self.cursor.execute(self.sql.query['addTourneyIndex']) - self.cursor.execute(self.sql.query['addHandsIndex']) - self.cursor.execute(self.sql.query['addPlayersIndex']) + #self.cursor.execute(self.sql.query['addTourneyIndex']) + #self.cursor.execute(self.sql.query['addHandsIndex']) + #self.cursor.execute(self.sql.query['addPlayersIndex']) self.fillDefaultData() self.db.commit() #end def disconnect @@ -185,6 +185,7 @@ class fpdb_db: self.drop_tables() self.create_tables() + fpdb_simple.createAllIndexes(self) self.db.commit() print "Finished recreating tables" #end def recreate_tables From 791068d24f008817857a5a1c61f403754f02f36e Mon Sep 17 00:00:00 2001 From: sqlcoder Date: Sun, 14 Dec 2008 02:50:09 +0000 Subject: [PATCH 08/43] refine column headings, handle null stats and remove debug message --- pyfpdb/GuiPlayerStats.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/pyfpdb/GuiPlayerStats.py b/pyfpdb/GuiPlayerStats.py index 8220e204..5a53813f 100644 --- a/pyfpdb/GuiPlayerStats.py +++ b/pyfpdb/GuiPlayerStats.py @@ -60,7 +60,7 @@ class GuiPlayerStats (threading.Thread): vbox.add(self.stats_table) # Create header row - titles = ("Game", "Hands", "VPIP", "PFR", "saw_f", "sawsd", "wtsdwsf", "wmsd", "FlAFq", "TuAFq", "RvAFq", "PFAFq", "Net($)", "BB/100", "$/hand", "Variance") + titles = ("Game", "Hands", "VPIP", "PFR", "Saw_F", "SawSD", "WtSDwsF", "W$SD", "FlAFq", "TuAFq", "RvAFq", "PoFAFq", "Net($)", "BB/100", "$/hand", "Variance") col = 0 row = 0 @@ -71,14 +71,17 @@ class GuiPlayerStats (threading.Thread): col +=1 for row in range(rows-1): + if(row%2 == 0): + bgcolor = "white" + else: + bgcolor = "lightgrey" 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][col]) + if result[row][col]: + l = gtk.Label(result[row][col]) + else: + l = gtk.Label(' ') if col == 0: l.set_alignment(xalign=0.0, yalign=0.5) else: @@ -127,7 +130,6 @@ class GuiPlayerStats (threading.Thread): def __set_hero_name(self, w, site): self.heroes[site] = w.get_text() - print "DEBUG: settings heroes[%s]: %s"%(site, self.heroes[site]) def __init__(self, db, config, querylist, debug=True): self.debug=debug From 2ae8c792a63642418ec2f6b60b8aaf55f5393f6f Mon Sep 17 00:00:00 2001 From: sqlcoder Date: Sun, 14 Dec 2008 03:07:05 +0000 Subject: [PATCH 09/43] playerstats: round value for variance before displaying, correct calculation of $/hand --- pyfpdb/FpdbSQLQueries.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pyfpdb/FpdbSQLQueries.py b/pyfpdb/FpdbSQLQueries.py index 13899707..d233f2d2 100644 --- a/pyfpdb/FpdbSQLQueries.py +++ b/pyfpdb/FpdbSQLQueries.py @@ -694,7 +694,7 @@ class FpdbSQLQueries: AS BBlPer100 ,hprof2.profitperhand AS Profitperhand */ - ,hprof2.variance as Variance + ,format(hprof2.variance,2) AS Variance FROM (select /* stats from hudcache */ gt.base @@ -793,7 +793,7 @@ class FpdbSQLQueries: AS BBper100 ,hprof2.profitperhand AS Profitperhand */ - ,hprof2.variance as Variance + ,round(hprof2.variance,2) AS Variance FROM (select gt.base ,gt.category @@ -825,10 +825,10 @@ class FpdbSQLQueries: else to_char(100.0*(sum(street1Aggr)+sum(street2Aggr)+sum(street3Aggr)) /(sum(street1Seen)+sum(street2Seen)+sum(street3Seen)),'90D0') end AS PoFAFq - ,to_char(sum(totalProfit)/100.0,'9G999G990D00') AS Net + ,round(sum(totalProfit)/100.0,2) AS Net ,to_char((sum(totalProfit)/(gt.bigBlind+0.0)) / (sum(HDs)/100.0), '990D00') AS BBper100 - ,to_char(sum(totalProfit) / (sum(HDs)+0.0), '990D0000') AS Profitperhand + ,to_char(sum(totalProfit/100.0) / (sum(HDs)+0.0), '990D0000') AS Profitperhand from Gametypes gt inner join Sites s on s.Id = gt.siteId inner join HudCache hc on hc.gameTypeId = gt.Id From f1be7c2ec0e19b693e3dc37dea8daf9752ab4977 Mon Sep 17 00:00:00 2001 From: sqlcoder Date: Sun, 14 Dec 2008 11:31:47 +0000 Subject: [PATCH 10/43] saveActions setting was supposed to be True in 'official' code --- pyfpdb/fpdb_save_to_db.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyfpdb/fpdb_save_to_db.py b/pyfpdb/fpdb_save_to_db.py index a7e75e7f..c8d5b208 100644 --- a/pyfpdb/fpdb_save_to_db.py +++ b/pyfpdb/fpdb_save_to_db.py @@ -22,9 +22,9 @@ from time import time import fpdb_simple -saveActions=False # set this to False to avoid storing action data +saveActions=True # set this to False to avoid storing action data # Pros: speeds up imports - # Cons: no action data is saved, need to keep the hand histories + # Cons: no action data is saved, so you need to keep the hand histories # variance not available on stats page #stores a stud/razz hand into the database From 7926ac9def71f9ef4c2179123d2d8d8cd1430bb8 Mon Sep 17 00:00:00 2001 From: Matt Turnbull Date: Sun, 14 Dec 2008 19:25:04 +0000 Subject: [PATCH 11/43] multiple 'collected pots' handles side pots, rake calculated from totalbets - totalcollected. --- pyfpdb/EverleafToFpdb.py | 8 +- pyfpdb/Hand.py | 411 +++++++++++++++++++++++++++++++++ pyfpdb/HandHistoryConverter.py | 359 +--------------------------- 3 files changed, 417 insertions(+), 361 deletions(-) create mode 100644 pyfpdb/Hand.py diff --git a/pyfpdb/EverleafToFpdb.py b/pyfpdb/EverleafToFpdb.py index 68e4ce5b..ce7bddd2 100755 --- a/pyfpdb/EverleafToFpdb.py +++ b/pyfpdb/EverleafToFpdb.py @@ -193,18 +193,16 @@ class Everleaf(HandHistoryConverter): hand.addShownCards(cards, shows.group('PNAME')) def readCollectPot(self,hand): - m = self.rexx.collect_pot_re.search(hand.string) - if m is not None: + for m in self.rexx.collect_pot_re.finditer(hand.string): if m.group('HAND') is not None: re_card = re.compile('(?P[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 + hand.rake = hand.totalpot - hand.totalcollected # * Decimal('0.05') # probably not quite right if __name__ == "__main__": c = Configuration.Config() diff --git a/pyfpdb/Hand.py b/pyfpdb/Hand.py new file mode 100644 index 00000000..5f5d0fd3 --- /dev/null +++ b/pyfpdb/Hand.py @@ -0,0 +1,411 @@ +#!/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 . +#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.totalcollected = 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) + + if self.totalcollected is None: + self.totalcollected = 0; + for amount in self.collected.values(): + self.totalcollected += Decimal(amount) + + + 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.totalcollected, 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 diff --git a/pyfpdb/HandHistoryConverter.py b/pyfpdb/HandHistoryConverter.py index 77d76eb3..a00df74d 100644 --- a/pyfpdb/HandHistoryConverter.py +++ b/pyfpdb/HandHistoryConverter.py @@ -17,6 +17,7 @@ import Configuration import FpdbRegex +import Hand import re import sys import traceback @@ -176,7 +177,7 @@ class HandHistoryConverter: def getRake(self, hand): abstract def sanityCheck(self): - sane = True + sane = False base_w = False #Check if hhbase exists and is writable #Note: Will not try to create the base HH directory @@ -208,7 +209,7 @@ class HandHistoryConverter: 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): @@ -241,357 +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.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]: - return "Hold'em No Limit" - - 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 \ No newline at end of file From 3dbb0e83760dbbed2519c77ec2d2def3bf823968 Mon Sep 17 00:00:00 2001 From: Matt Turnbull Date: Sun, 14 Dec 2008 22:05:51 +0000 Subject: [PATCH 12/43] Everleaf uncalled bets; pot total; rake --- pyfpdb/EverleafToFpdb.py | 25 +++++------- pyfpdb/Hand.py | 74 ++++++++++++++++++++++++++++++---- pyfpdb/HandHistoryConverter.py | 9 +++-- 3 files changed, 83 insertions(+), 25 deletions(-) diff --git a/pyfpdb/EverleafToFpdb.py b/pyfpdb/EverleafToFpdb.py index ce7bddd2..188e4ade 100755 --- a/pyfpdb/EverleafToFpdb.py +++ b/pyfpdb/EverleafToFpdb.py @@ -75,6 +75,7 @@ class Everleaf(HandHistoryConverter): self.rexx.setPlayerInfoRegex('Seat (?P[0-9]+): (?P.*) \(\s+(\$ (?P[.0-9]+) USD|new player|All-in) \)') self.rexx.setPostSbRegex('.*\n(?P.*): posts small blind \[\$? (?P[.0-9]+)') self.rexx.setPostBbRegex('.*\n(?P.*): posts big blind \[\$? (?P[.0-9]+)') + self.rexx.setPostBothRegex('.*\n(?P.*): posts small \& big blinds \[\$? (?P[.0-9]+)') # mct : what about posting small & big blinds simultaneously? self.rexx.setHeroCardsRegex('.*\nDealt\sto\s(?P.*)\s\[ (?P\S\S), (?P\S\S) \]') self.rexx.setActionStepRegex('.*\n(?P.*)(?P: bets| checks| raises| calls| folds)(\s\[\$ (?P[.\d]+) USD\])?') @@ -131,7 +132,8 @@ class Everleaf(HandHistoryConverter): r"(\*\* Dealing Turn \*\* \[ \S\S \](?P.+(?=\*\* Dealing River \*\*)|.+))?" r"(\*\* Dealing River \*\* \[ \S\S \](?P.+))?", hand.string,re.DOTALL) - hand.streets = m + hand.addStreets(m) + def readCommunityCards(self, hand): # currently regex in wrong place pls fix my brain's fried @@ -146,15 +148,13 @@ class Everleaf(HandHistoryConverter): def readBlinds(self, hand): try: m = self.rexx.small_blind_re.search(hand.string) - hand.addBlind(m.group('PNAME'), m.group('SB')) - #hand.posted = [m.group('PNAME')] - except: - hand.addBlind(None, 0) - #hand.posted = ["FpdbNBP"] - m = self.rexx.big_blind_re.finditer(hand.string) - for a in m: - hand.addBlind(a.group('PNAME'), a.group('BB')) - #hand.posted = hand.posted + [a.group('PNAME')] + hand.addBlind(m.group('PNAME'), 'small blind', m.group('SB')) + except: # no small blind + hand.addBlind(None, None, None) + for a in self.rexx.big_blind_re.finditer(hand.string): + hand.addBlind(a.group('PNAME'), 'big blind', a.group('BB')) + for a in self.rexx.both_blinds_re.finditer(hand.string): + hand.addBlind(a.group('PNAME'), 'small & big blinds', a.group('SBBB')) def readHeroCards(self, hand): m = self.rexx.hero_cards_re.search(hand.string) @@ -167,7 +167,6 @@ class Everleaf(HandHistoryConverter): def readAction(self, hand, street): m = self.rexx.action_re.finditer(hand.streets.group(street)) - hand.actions[street] = [] for action in m: if action.group('ATYPE') == ' raises': hand.addRaiseTo( street, action.group('PNAME'), action.group('BET') ) @@ -182,6 +181,7 @@ class Everleaf(HandHistoryConverter): else: print "DEBUG: unimplemented readAction: %s %s" %(action.group('PNAME'),action.group('ATYPE'),) #hand.actions[street] += [[action.group('PNAME'), action.group('ATYPE')]] + # TODO: Everleaf does not record uncalled bets. def readShowdownActions(self, hand): @@ -201,9 +201,6 @@ class Everleaf(HandHistoryConverter): hand.addCollectPot(player=m.group('PNAME'),pot=m.group('POT')) - def getRake(self, hand): - hand.rake = hand.totalpot - hand.totalcollected # * Decimal('0.05') # probably not quite right - if __name__ == "__main__": c = Configuration.Config() e = Everleaf(c, "regression-test-files/everleaf/Speed_Kuala_full.txt") diff --git a/pyfpdb/Hand.py b/pyfpdb/Hand.py index 5f5d0fd3..809c1be7 100644 --- a/pyfpdb/Hand.py +++ b/pyfpdb/Hand.py @@ -88,6 +88,7 @@ class Hand: self.action = [] self.totalpot = None self.totalcollected = None + self.rake = None self.bets = {} @@ -110,6 +111,17 @@ If a player has None chips he won't be added.""" self.bets[street][name] = [] + def addStreets(self, match): + # go through m and initialise actions to empty list for each street. + if match is not None: + self.streets = match + for street in match.groupdict(): + if match.group(street) is not None: + self.actions[street] = [] + + else: + print "empty markStreets match" # better to raise exception and put process hand in a try block + def addHoleCards(self, cards, player): """\ Assigns observed holecards to a player. @@ -162,12 +174,18 @@ For when a player shows cards for any reason (for showdown or out of choice). c = c.replace(k,v) return c - def addBlind(self, player, amount): + def addBlind(self, player, blindtype, 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.actions['PREFLOP'] += [(player, 'posts', blindtype, amount)] + if blindtype == 'big blind': + self.lastBet['PREFLOP'] = Decimal(amount) + elif blindtype == 'small & big blinds': + # extra small blind is 'dead' + self.lastBet['PREFLOP'] = Decimal(self.bb) self.posted += [player] + def addCall(self, street, player=None, amount=None): @@ -176,7 +194,7 @@ For when a player shows cards for any reason (for showdown or out of choice). if amount is not None: self.bets[street][player].append(Decimal(amount)) #self.lastBet[street] = Decimal(amount) - self.actions[street] += [[player, 'calls', amount]] + self.actions[street] += [(player, 'calls', amount)] def addRaiseTo(self, street, player, amountTo): """\ @@ -193,21 +211,22 @@ Add a raise on [street] by [player] to [amountTo] self.lastBet[street] = Decimal(amountTo) amountBy = Decimal(amountTo) - amountToCall self.bets[street][player].append(amountBy+amountToCall) - self.actions[street] += [[player, 'raises', amountBy, amountTo]] + self.actions[street] += [(player, 'raises', amountBy, amountTo, amountToCall)] def addBet(self, street, player, amount): self.checkPlayerExists(player) self.bets[street][player].append(Decimal(amount)) - self.actions[street] += [[player, 'bets', amount]] + self.actions[street] += [(player, 'bets', amount)] + self.lastBet[street] = Decimal(amount) def addFold(self, street, player): self.checkPlayerExists(player) self.folded.add(player) - self.actions[street] += [[player, 'folds']] + self.actions[street] += [(player, 'folds')] def addCheck(self, street, player): self.checkPlayerExists(player) - self.actions[street] += [[player, 'checks']] + self.actions[street] += [(player, 'checks')] def addCollectPot(self,player, pot): self.checkPlayerExists(player) @@ -231,12 +250,50 @@ Known bug: doesn't take into account side pots""" for street in self.streetList: #print street, self.bets[street][player] self.totalpot += reduce(operator.add, self.bets[street][player], 0) + print "conventional totalpot:", self.totalpot + self.totalpot = 0 + + print self.actions + for street in self.actions: + uncalled = 0 + calls = [0] + for act in self.actions[street]: + if act[1] == 'bets': # [name, 'bets', amount] + self.totalpot += Decimal(act[2]) + uncalled = Decimal(act[2]) # only the last bet or raise can be uncalled + calls = [0] + print "uncalled: ", uncalled + elif act[1] == 'raises': # [name, 'raises', amountby, amountto, amountcalled] + print "calls %s and raises %s to %s" % (act[4],act[2],act[3]) + self.totalpot += Decimal(act[2]) + Decimal(act[4]) + calls = [0] + uncalled = Decimal(act[2]) + print "uncalled: ", uncalled + elif act[1] == 'calls': # [name, 'calls', amount] + self.totalpot += Decimal(act[2]) + calls = calls + [Decimal(act[2])] + print "calls:", calls + if act[1] == ('posts'): + self.totalpot += Decimal(act[3]) + uncalled = Decimal(act[3]) + if uncalled > 0 and max(calls+[0]) < uncalled: + + print "returning some bet, calls:", calls + print "returned: %.2f from %.2f" % ((uncalled - max(calls)), self.totalpot,) + self.totalpot -= (uncalled - max(calls)) + print "new totalpot:", self.totalpot if self.totalcollected is None: self.totalcollected = 0; for amount in self.collected.values(): self.totalcollected += Decimal(amount) + # TODO: Some sites (Everleaf) don't record uncalled bets. Figure out if a bet is uncalled and subtract it from self.totalcollected. + # remember that portions of bets may be uncalled, so: + # bet followed by no call is an uncalled bet + # bet x followed by call y where y < x has x-y uncalled (and second player all in) + + def getGameTypeAsString(self): """\ @@ -316,7 +373,8 @@ Map the tuple self.gametype onto the pokerstars string describing it print "what do they show" print "*** SUMMARY ***" - print "Total pot $%s | Rake $%.2f" % (self.totalcollected, self.rake) # TODO side pots + print "Total pot $%s | Rake $%.2f" % (self.totalcollected, self.rake) # TODO: side pots + board = [] for s in self.board.values(): board += s diff --git a/pyfpdb/HandHistoryConverter.py b/pyfpdb/HandHistoryConverter.py index a00df74d..68d00e96 100644 --- a/pyfpdb/HandHistoryConverter.py +++ b/pyfpdb/HandHistoryConverter.py @@ -161,7 +161,8 @@ class HandHistoryConverter: def readPlayerStacks(self, hand): abstract # Needs to return a MatchObject with group names identifying the streets into the Hand object - # that is, pulls the chunks of preflop, flop, turn and river text into hand.streets MatchObject. + # so groups are called by street names 'PREFLOP', 'FLOP', 'STREET2' etc + # blinds are done seperately def markStreets(self, hand): abstract #Needs to return a list in the format @@ -173,8 +174,10 @@ class HandHistoryConverter: 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. - def getRake(self, hand): abstract + # an inheriting class can calculate it for the specific site if need be. + def getRake(self, hand): + hand.rake = hand.totalpot - hand.totalcollected # * Decimal('0.05') # probably not quite right + def sanityCheck(self): sane = False From 691608b6ca22eee8596fde6f64dfcb1779942542 Mon Sep 17 00:00:00 2001 From: eblade Date: Mon, 15 Dec 2008 02:04:35 -0500 Subject: [PATCH 13/43] update_table_position will kill huds that are closed, timer will not be called when under unix, currently, as this function only works in windows. checkPositions() accepts position 9 again. --- pyfpdb/Hud.py | 13 +++++++++++-- pyfpdb/fpdb_simple.py | 2 +- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/pyfpdb/Hud.py b/pyfpdb/Hud.py index 92f38bb7..1de69413 100644 --- a/pyfpdb/Hud.py +++ b/pyfpdb/Hud.py @@ -123,6 +123,15 @@ class Hud: self.main_window.set_destroy_with_parent(True) def update_table_position(self): +# self.main_window.parentgdkhandle = gtk.gdk.window_foreign_new(self.table.number) +# if self.main_window.parentgdkhandle == None: + if os.name == 'nt': + if not win32gui.IsWindow(self.table.number): + self.kill_hud() + return False + else: + return False # kill the timer under Unix, and ignore it until we have a way to check the validity of the window. + (x, y) = self.main_window.parentgdkhandle.get_origin() if self.table.x != x or self.table.y != y: self.table.x = x @@ -134,7 +143,7 @@ class Hud: (x, y) = loc[adj[i]] if self.stat_windows.has_key(i): self.stat_windows[i].relocate(x, y) - + return True def on_button_press(self, widget, event): @@ -229,7 +238,7 @@ class Hud: aux_params = config.get_aux_parameters(game_params['aux']) self.aux_windows.append(eval("%s.%s(gtk.Window(), self, config, 'fpdb')" % (aux_params['module'], aux_params['class']))) -# gobject.timeout_add(500, self.update_table_position) + gobject.timeout_add(500, self.update_table_position) def update(self, hand, config, stat_dict): self.hand = hand # this is the last hand, so it is available later diff --git a/pyfpdb/fpdb_simple.py b/pyfpdb/fpdb_simple.py index 7c9bbe21..6e70b699 100644 --- a/pyfpdb/fpdb_simple.py +++ b/pyfpdb/fpdb_simple.py @@ -90,7 +90,7 @@ def checkPositions(positions): pass ### RHH modified to allow for "position 9" here (pos==9 is when you're a dead hand before the BB - if (pos!="B" and pos!="S" and pos!=0 and pos!=1 and pos!=2 and pos!=3 and pos!=4 and pos!=5 and pos!=6 and pos!=7 and pos!=9): + if (pos!="B" and pos!="S" and pos!=0 and pos!=1 and pos!=2 and pos!=3 and pos!=4 and pos!=5 and pos!=6 and pos!=7 and pos != 8 and pos!=9): raise FpdbError("invalid position found in checkPositions. i: "+str(i)+" position: "+str(pos)) #end def fpdb_simple.checkPositions From 695b3a53cf383b87ac4ea6ad0550810762251184 Mon Sep 17 00:00:00 2001 From: sqlcoder Date: Mon, 15 Dec 2008 23:15:54 +0000 Subject: [PATCH 14/43] update mysql query for position stats (postgres one still to do ...) --- pyfpdb/FpdbSQLQueries.py | 171 ++++++++++++++++++++++++++++----------- 1 file changed, 124 insertions(+), 47 deletions(-) diff --git a/pyfpdb/FpdbSQLQueries.py b/pyfpdb/FpdbSQLQueries.py index d233f2d2..07fa15bb 100644 --- a/pyfpdb/FpdbSQLQueries.py +++ b/pyfpdb/FpdbSQLQueries.py @@ -867,53 +867,130 @@ class FpdbSQLQueries: if(self.dbname == 'MySQL InnoDB'): self.query['playerStatsByPosition'] = """ - select /* stats from hudcache */ - hc.position - ,sum(HDs) as n - ,format(round(100.0*sum(street0VPI)/sum(HDs)),1) AS vpip - ,format(round(100.0*sum(street0Aggr)/sum(HDs)),1) AS pfr - ,format(round(100.0*sum(street1Seen)/sum(HDs)),1) AS saw_f - ,format(round(100.0*sum(sawShowdown)/sum(HDs)),1) AS sawsd - ,case when sum(street1Seen) = 0 then 'oo' - else format(round(100.0*sum(sawShowdown)/sum(street1Seen)),1) - end AS wtsdwsf - ,case when sum(sawShowdown) = 0 then 'oo' - else format(round(100.0*sum(wonAtSD)/sum(sawShowdown)),1) - end AS wmsd - ,case when sum(street1Seen) = 0 then 'oo' - else format(round(100.0*sum(street1Aggr)/sum(street1Seen)),1) - end AS FlAFq - ,case when sum(street2Seen) = 0 then 'oo' - else format(round(100.0*sum(street2Aggr)/sum(street2Seen)),1) - end AS TuAFq - ,case when sum(street3Seen) = 0 then 'oo' - else format(round(100.0*sum(street3Aggr)/sum(street3Seen)),1) - end AS RvAFq - ,case when sum(street1Seen)+sum(street2Seen)+sum(street3Seen) = 0 then 'oo' - else format(round(100.0*(sum(street1Aggr)+sum(street2Aggr)+sum(street3Aggr)) - /(sum(street1Seen)+sum(street2Seen)+sum(street3Seen))),1) - end AS PoFAFq - ,format(sum(totalProfit)/100.0,2) AS Net - ,case when sum(HDs) = 0 then 'oo' - else format((sum(totalProfit)/(gt.bigBlind+0.0)) / (sum(HDs)/100.0),2) - end AS BBper100 - from Gametypes gt - inner join Sites s on (s.Id = gt.siteId) - inner join HudCache hc on (hc.gameTypeId = gt.Id) - inner join Players p on (p.id = hc.playerId) - where hc.playerId in - and gt.type = 'ring' - and gt.id = /* must specify gametypeid */ - /* and stats.n > 100 optional stat-based queries */ - group by hc.position, gt.bigBlind - order by case when hc.position = 'B' then -2 - when hc.position = 'S' then -1 - when hc.position = 'D' then 0 - when hc.position = 'C' then 1 - when hc.position = 'M' then 2 - when hc.position = 'E' then 5 - else 9 - end + SELECT + concat(upper(stats.limitType), ' ' + ,concat(upper(substring(stats.category,1,1)),substring(stats.category,2) ), ' ' + ,stats.name, ' $' + ,cast(trim(leading ' ' from + case when stats.bigBlind < 100 then format(stats.bigBlind/100.0,2) + else format(stats.bigBlind/100.0,0) + end ) as char) + ) AS Game + ,case when stats.PlPosition = -2 then 'BB' + when stats.PlPosition = -1 then 'SB' + when stats.PlPosition = 0 then 'Btn' + when stats.PlPosition = 1 then 'CO' + when stats.PlPosition = 2 then 'MP' + when stats.PlPosition = 5 then 'EP' + else '??' + end AS PlPosition + ,stats.n + ,stats.vpip + ,stats.pfr + ,stats.saw_f + ,stats.sawsd + ,stats.wtsdwsf + ,stats.wmsd + ,stats.FlAFq + ,stats.TuAFq + ,stats.RvAFq + ,stats.PoFAFq + /* if you have handsactions data the next 3 fields should give same answer as + following 3 commented out fields */ + ,stats.Net + ,stats.BBper100 + ,stats.Profitperhand + /*,format(hprof2.sum_profit/100.0,2) AS Net + ,format((hprof2.sum_profit/(stats.bigBlind+0.0)) / (stats.n/100.0),2) + AS BBlPer100 + ,hprof2.profitperhand AS Profitperhand + */ + ,format(hprof2.variance,2) AS Variance + FROM + (select /* stats from hudcache */ + gt.base + ,gt.category + ,upper(gt.limitType) as limitType + ,s.name + ,gt.bigBlind + ,hc.gametypeId + ,case when hc.position = 'B' then -2 + when hc.position = 'S' then -1 + when hc.position = 'D' then 0 + when hc.position = 'C' then 1 + when hc.position = 'M' then 2 + when hc.position = 'E' then 5 + else 9 + end as PlPosition + ,sum(HDs) AS n + ,format(100.0*sum(street0VPI)/sum(HDs),1) AS vpip + ,format(100.0*sum(street0Aggr)/sum(HDs),1) AS pfr + ,format(100.0*sum(street1Seen)/sum(HDs),1) AS saw_f + ,format(100.0*sum(sawShowdown)/sum(HDs),1) AS sawsd + ,case when sum(street1Seen) = 0 then 'oo' + else format(100.0*sum(sawShowdown)/sum(street1Seen),1) + end AS wtsdwsf + ,case when sum(sawShowdown) = 0 then 'oo' + else format(100.0*sum(wonAtSD)/sum(sawShowdown),1) + end AS wmsd + ,case when sum(street1Seen) = 0 then 'oo' + else format(100.0*sum(street1Aggr)/sum(street1Seen),1) + end AS FlAFq + ,case when sum(street2Seen) = 0 then 'oo' + else format(100.0*sum(street2Aggr)/sum(street2Seen),1) + end AS TuAFq + ,case when sum(street3Seen) = 0 then 'oo' + else format(100.0*sum(street3Aggr)/sum(street3Seen),1) + end AS RvAFq + ,case when sum(street1Seen)+sum(street2Seen)+sum(street3Seen) = 0 then 'oo' + else format(100.0*(sum(street1Aggr)+sum(street2Aggr)+sum(street3Aggr)) + /(sum(street1Seen)+sum(street2Seen)+sum(street3Seen)),1) + end AS PoFAFq + ,format(sum(totalProfit)/100.0,2) AS Net + ,format((sum(totalProfit)/(gt.bigBlind+0.0)) / (sum(HDs)/100.0),2) + AS BBper100 + ,format( (sum(totalProfit)/100.0) / sum(HDs), 4) AS Profitperhand + 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 + # use here ? + group by gt.base + ,gt.category + ,upper(gt.limitType) + ,s.name + ,gt.bigBlind + ,hc.gametypeId + ,PlPosition + ) stats + inner join + ( select # profit from handsplayers/handsactions + hprof.gameTypeId, + case when hprof.position = 'B' then -2 + when hprof.position = 'S' then -1 + when hprof.position in ('3','4') then 2 + when hprof.position in ('6','7') then 5 + else hprof.position + end as PlPosition, + sum(hprof.profit) as sum_profit, + avg(hprof.profit/100.0) as profitperhand, + variance(hprof.profit/100.0) as variance + from + (select hp.handId, h.gameTypeId, hp.position, hp.winnings, SUM(ha.amount) + costs, hp.winnings - SUM(ha.amount) profit + from HandsPlayers hp + inner join Hands h ON h.id = hp.handId + left join HandsActions ha ON ha.handPlayerId = hp.id + where hp.playerId in + # use here ? + and hp.tourneysPlayersId IS NULL + group by hp.handId, h.gameTypeId, hp.position, hp.winnings + ) hprof + group by hprof.gameTypeId, PlPosition + ) hprof2 + on ( hprof2.gameTypeId = stats.gameTypeId + and hprof2.PlPosition = stats.PlPosition) + order by stats.category, stats.limittype, stats.bigBlind, cast(stats.PlPosition as signed) """ elif(self.dbname == 'PostgreSQL'): self.query['playerStatsByPosition'] = """ From 9c5d0f45981c899827a6902d585636ba3af879a8 Mon Sep 17 00:00:00 2001 From: Matt Turnbull Date: Mon, 15 Dec 2008 23:56:19 +0000 Subject: [PATCH 15/43] Writes hands to stderr, miscellanous crap to stdout; usuable as cmdline filter: ./Everleaf 'hhfile' 1>/dev/null 2>outfile Holecards are sets -- should work on Omaha hi hands also. Successfully imported Speed_Kuala_full.txt to fpdb. Added gettext. cards strings are handled a little better (one fewer regex) Testfile can be supplied as first cmd line arg. --- pyfpdb/EverleafToFpdb.py | 38 +++++++----- pyfpdb/Hand.py | 110 +++++++++++++++++---------------- pyfpdb/HandHistoryConverter.py | 9 ++- 3 files changed, 89 insertions(+), 68 deletions(-) diff --git a/pyfpdb/EverleafToFpdb.py b/pyfpdb/EverleafToFpdb.py index 188e4ade..a34f2a63 100755 --- a/pyfpdb/EverleafToFpdb.py +++ b/pyfpdb/EverleafToFpdb.py @@ -76,11 +76,11 @@ class Everleaf(HandHistoryConverter): self.rexx.setPostSbRegex('.*\n(?P.*): posts small blind \[\$? (?P[.0-9]+)') self.rexx.setPostBbRegex('.*\n(?P.*): posts big blind \[\$? (?P[.0-9]+)') self.rexx.setPostBothRegex('.*\n(?P.*): posts small \& big blinds \[\$? (?P[.0-9]+)') - # mct : what about posting small & big blinds simultaneously? - self.rexx.setHeroCardsRegex('.*\nDealt\sto\s(?P.*)\s\[ (?P\S\S), (?P\S\S) \]') + self.rexx.setHeroCardsRegex('.*\nDealt\sto\s(?P.*)\s\[ (?P.*) \]') self.rexx.setActionStepRegex('.*\n(?P.*)(?P: bets| checks| raises| calls| folds)(\s\[\$ (?P[.\d]+) USD\])?') self.rexx.setShowdownActionRegex('.*\n(?P.*) shows \[ (?P.*) \]') - self.rexx.setCollectPotRegex('.*\n(?P.*) wins \$ (?P[.\d]+) USD(.*\[ (?P.*) \])?') + self.rexx.setCollectPotRegex('.*\n(?P.*) wins \$ (?P[.\d]+) USD(.*\[ (?P.*) \])?') + self.rexx.sits_out_re = re.compile('(?P.*) sits out') self.rexx.compileRegexes() def readSupportedGames(self): @@ -163,7 +163,11 @@ class Everleaf(HandHistoryConverter): hand.involved = False else: hand.hero = m.group('PNAME') - hand.addHoleCards([m.group('HOLE1'), m.group('HOLE2')], m.group('PNAME')) + # "2c, qh" -> set(["2c","qc"]) + # Also works with Omaha hands. + cards = m.group('CARDS') + cards = set(cards.split(', ')) + hand.addHoleCards(cards, m.group('PNAME')) def readAction(self, hand, street): m = self.rexx.action_re.finditer(hand.streets.group(street)) @@ -185,25 +189,31 @@ class Everleaf(HandHistoryConverter): def readShowdownActions(self, hand): - for shows in self.rexx.showdown_action_re.finditer(hand.string): - print shows.groups() - re_card = re.compile('(?P[0-9tjqka][schd])') # copied from earlier - cards = [card.group('CARD') for card in re_card.finditer(shows.group('CARDS'))] - print cards + for shows in self.rexx.showdown_action_re.finditer(hand.string): + cards = shows.group('CARDS') + cards = set(cards.split(', ')) + #re_card = re.compile('(?P[0-9tjqka][schd])') # copied from earlier + #cards = set([card.group('CARD') for card in re_card.finditer(shows.group('CARDS'))]) hand.addShownCards(cards, shows.group('PNAME')) def readCollectPot(self,hand): for m in self.rexx.collect_pot_re.finditer(hand.string): - if m.group('HAND') is not None: - re_card = re.compile('(?P[0-9tjqka][schd])') # copied from earlier - cards = set([hand.card(card.group('CARD')) for card in re_card.finditer(m.group('HAND'))]) + if m.group('CARDS') is not None: + cards = m.group('CARDS') + cards = set(cards.split(', ')) + #re_card = re.compile('(?P[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')) if __name__ == "__main__": c = Configuration.Config() - e = Everleaf(c, "regression-test-files/everleaf/Speed_Kuala_full.txt") + if sys.argv[0] == '': + testfile = "regression-test-files/everleaf/Speed_Kuala_full.txt" + else: + testfile = sys.argv[1] + print "Converting: ", testfile + e = Everleaf(c, testfile) e.processFile() print str(e) - diff --git a/pyfpdb/Hand.py b/pyfpdb/Hand.py index d9af1a42..d746f3ab 100644 --- a/pyfpdb/Hand.py +++ b/pyfpdb/Hand.py @@ -106,7 +106,7 @@ 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] = [] + self.holecards[name] = set() for street in self.streetList: self.bets[street][name] = [] @@ -125,29 +125,27 @@ If a player has None chips he won't be added.""" def addHoleCards(self, cards, player): """\ Assigns observed holecards to a player. -cards list of card bigrams e.g. ['2h','jc'] +cards set of card bigrams e.g. set(['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]) + cards = set([self.card(c) for c in cards]) + self.holecards[player].update(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). +Card ranks will be uppercased """ if cards is not None: self.shown.add(player) self.addHoleCards(cards,player) elif holeandboard is not None: + holeandboard = set([self.card(c) for c in holeandboard]) 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) @@ -232,14 +230,11 @@ Add a raise on [street] by [player] to [amountTo] 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?" + print "[WARNING] %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 all bets and blinds have been added, totals up the total pot size""" if self.totalpot is None: self.totalpot = 0 @@ -288,10 +283,6 @@ Known bug: doesn't take into account side pots""" for amount in self.collected.values(): self.totalcollected += Decimal(amount) - # TODO: Some sites (Everleaf) don't record uncalled bets. Figure out if a bet is uncalled and subtract it from self.totalcollected. - # remember that portions of bets may be uncalled, so: - # bet followed by no call is an uncalled bet - # bet x followed by call y where y < x has x-y uncalled (and second player all in) @@ -322,81 +313,89 @@ Map the tuple self.gametype onto the pokerstars string describing it return string - def printHand(self): + def writeHand(self, fh=sys.__stdout__): # 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]) + #print "\n### Pseudo stars format ###" + #print >>fh, _("%s Game #%s: %s ($%s/$%s) - %s" %(self.sitename, self.handid, self.getGameTypeAsString(), self.sb, self.bb, self.starttime)) + print >>fh, _("%s Game #%s: %s ($%s/$%s) - %s" %("PokerStars", self.handid, self.getGameTypeAsString(), self.sb, self.bb, self.starttime)) + print >>fh, _("Table '%s' %d-max Seat #%s is the button" %(self.tablename, self.maxseats, self.buttonpos)) + + players_who_act_preflop = set([x[0] for x in self.actions['PREFLOP']]) + print players_who_act_preflop + print [x[1] for x in self.players] + print [x for x in self.players if x[1] in players_who_act_preflop] + for player in [x for x in self.players if x[1] in players_who_act_preflop]: + #Only print stacks of players who do something preflop + print >>fh, _("Seat %s: %s ($%s)" %(player[0], player[1], player[2])) if(self.posted[0] is None): - print "No small blind posted" + #print >>fh, _("No small blind posted") # PS doesn't say this + pass else: - print "%s: posts small blind $%s" %(self.posted[0], self.sb) + print >>fh, _("%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) + print >>fh, _("%s: posts big blind $%s" %(self.posted[1], self.bb)) - # What about big & small blinds? + # TODO: What about big & small blinds? - print "*** HOLE CARDS ***" + print >>fh, _("*** HOLE CARDS ***") if self.involved: - print "Dealt to %s [%s]" %(self.hero , " ".join(self.holecards[self.hero])) + print >>fh, _("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) + self.printActionLine(act, fh) if 'FLOP' in self.actions: - print "*** FLOP *** [%s]" %( " ".join(self.board['Flop'])) + print >>fh, _("*** FLOP *** [%s]" %( " ".join(self.board['Flop']))) for act in self.actions['FLOP']: - self.printActionLine(act) + self.printActionLine(act, fh) if 'TURN' in self.actions: - print "*** TURN *** [%s] [%s]" %( " ".join(self.board['Flop']), " ".join(self.board['Turn'])) + print >>fh, _("*** TURN *** [%s] [%s]" %( " ".join(self.board['Flop']), " ".join(self.board['Turn']))) for act in self.actions['TURN']: - self.printActionLine(act) + self.printActionLine(act, fh) if 'RIVER' in self.actions: - print "*** RIVER *** [%s] [%s]" %(" ".join(self.board['Flop']+self.board['Turn']), " ".join(self.board['River']) ) + print >>fh, _("*** RIVER *** [%s] [%s]" %(" ".join(self.board['Flop']+self.board['Turn']), " ".join(self.board['River']) )) for act in self.actions['RIVER']: - self.printActionLine(act) + self.printActionLine(act, fh) #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 >>fh, _("*** SHOW DOWN ***") + print >>fh, "DEBUG: what do they show" - print "*** SUMMARY ***" - print "Total pot $%s | Rake $%.2f" % (self.totalcollected, self.rake) # TODO: side pots + print >>fh, _("*** SUMMARY ***") + print >>fh, _("Total pot $%s | Rake $%.2f" % (self.totalcollected, 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)) + print >>fh, _("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]) + print >>fh, _("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]) + print >>fh, _("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])) + print >>fh, _("Seat %d: %s showed [%s]" % (seatnum, name, " ".join(self.holecards[name]))) elif player[1] in self.folded: - print "Seat %d: %s folded" % (seatnum, name) + print >>fh, _("Seat %d: %s folded" % (seatnum, name)) else: - print "Seat %d: %s mucked" % (seatnum, name) + print >>fh, _("Seat %d: %s mucked" % (seatnum, name)) - print + print >>fh, "\n\n" # TODO: # logic for side pots # logic for which players get to showdown @@ -411,17 +410,22 @@ Map the tuple self.gametype onto the pokerstars string describing it #print "Seat %d: %s showed %s" % (player[0], player[1], hole) #else: #print "Seat %d: %s mucked or folded" % (player[0], player[1]) + + def printHand(self): + self.writeHand(sys.stdout) - def printActionLine(self, act): - if act[1] == 'folds' or act[1] == 'checks': - print "%s: %s " %(act[0], act[1]) + def printActionLine(self, act, fh): + if act[1] == 'folds': + print >>fh, _("%s: folds" %(act[0])) + elif act[1] == 'checks': + print >>fh, _("%s: checks" %(act[0])) if act[1] == 'calls': - print "%s: %s $%s" %(act[0], act[1], act[2]) + print >>fh, _("%s: calls $%s" %(act[0], act[2])) if act[1] == 'bets': - print "%s: %s $%s" %(act[0], act[1], act[2]) + print >>fh, _("%s: bets $%s" %(act[0], act[2])) if act[1] == 'raises': - print "%s: %s $%s to $%s" %(act[0], act[1], act[2], act[3]) + print >>fh, _("%s: raises $%s to $%s" %(act[0], act[2], act[3])) # going to use pokereval to figure out hands at some point. # these functions are copied from pokergame.py diff --git a/pyfpdb/HandHistoryConverter.py b/pyfpdb/HandHistoryConverter.py index 68d00e96..256de673 100644 --- a/pyfpdb/HandHistoryConverter.py +++ b/pyfpdb/HandHistoryConverter.py @@ -30,6 +30,8 @@ import operator from xml.dom.minidom import Node from pokereval import PokerEval from time import time +import gettext + #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: @@ -65,6 +67,11 @@ letter2names = { '2': 'Deuces' } +import gettext +gettext.install('myapplication') + + + class HandHistoryConverter: eval = PokerEval() def __init__(self, config, file, sitename): @@ -124,7 +131,7 @@ class HandHistoryConverter: hand.totalPot() self.getRake(hand) - hand.printHand() + hand.writeHand(sys.stderr) #if(hand.involved == True): #self.writeHand("output file", hand) #hand.printHand() From e50fbf4f21336cf37435d752d264558e4cb880a7 Mon Sep 17 00:00:00 2001 From: eblade Date: Mon, 15 Dec 2008 22:38:04 -0500 Subject: [PATCH 16/43] timer runs only under windows, not only under unix, oops --- pyfpdb/Hud.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pyfpdb/Hud.py b/pyfpdb/Hud.py index 1de69413..3bcb3bf1 100644 --- a/pyfpdb/Hud.py +++ b/pyfpdb/Hud.py @@ -129,8 +129,6 @@ class Hud: if not win32gui.IsWindow(self.table.number): self.kill_hud() return False - else: - return False # kill the timer under Unix, and ignore it until we have a way to check the validity of the window. (x, y) = self.main_window.parentgdkhandle.get_origin() if self.table.x != x or self.table.y != y: @@ -238,7 +236,8 @@ class Hud: aux_params = config.get_aux_parameters(game_params['aux']) self.aux_windows.append(eval("%s.%s(gtk.Window(), self, config, 'fpdb')" % (aux_params['module'], aux_params['class']))) - gobject.timeout_add(500, self.update_table_position) + if os.name == "nt": + gobject.timeout_add(500, self.update_table_position) def update(self, hand, config, stat_dict): self.hand = hand # this is the last hand, so it is available later From 988a7e3eb5af3532f40c5727004730cc2b0c8356 Mon Sep 17 00:00:00 2001 From: Matt Turnbull Date: Tue, 16 Dec 2008 04:29:11 +0000 Subject: [PATCH 17/43] Added 'and is all-in' logic. Altered to read actions in correct street order. hand.streetList must be set correctly for different types of games. --- pyfpdb/EverleafToFpdb.py | 3 +-- pyfpdb/Hand.py | 40 ++++++++++++++++++++++++---------- pyfpdb/HandHistoryConverter.py | 6 +++-- 3 files changed, 33 insertions(+), 16 deletions(-) diff --git a/pyfpdb/EverleafToFpdb.py b/pyfpdb/EverleafToFpdb.py index a34f2a63..9942105d 100755 --- a/pyfpdb/EverleafToFpdb.py +++ b/pyfpdb/EverleafToFpdb.py @@ -209,11 +209,10 @@ class Everleaf(HandHistoryConverter): if __name__ == "__main__": c = Configuration.Config() - if sys.argv[0] == '': + if len(sys.argv) == 1: testfile = "regression-test-files/everleaf/Speed_Kuala_full.txt" else: testfile = sys.argv[1] - print "Converting: ", testfile e = Everleaf(c, testfile) e.processFile() print str(e) diff --git a/pyfpdb/Hand.py b/pyfpdb/Hand.py index d746f3ab..a91038f6 100644 --- a/pyfpdb/Hand.py +++ b/pyfpdb/Hand.py @@ -28,6 +28,7 @@ import codecs from decimal import Decimal import operator from time import time +from copy import deepcopy class Hand: # def __init__(self, sitename, gametype, sb, bb, string): @@ -38,7 +39,7 @@ class Hand: self.gametype = gametype self.string = string - self.streetList = ['BLINDS','PREFLOP','FLOP','TURN','RIVER'] # a list of the observed street names in order + self.streetList = ['PREFLOP','FLOP','TURN','RIVER'] # a list of the observed street names in order self.handid = 0 self.sb = gametype[3] @@ -77,6 +78,8 @@ class Hand: # dict from player names to lists of hole cards self.holecards = {} + + self.stacks = {} # dict from player names to amounts collected self.collected = {} @@ -106,6 +109,7 @@ 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.stacks[name] = Decimal(chips) self.holecards[name] = set() for street in self.streetList: self.bets[street][name] = [] @@ -176,7 +180,9 @@ Card ranks will be uppercased # if player is None, it's a missing small blind. if player is not None: self.bets['PREFLOP'][player].append(Decimal(amount)) - self.actions['PREFLOP'] += [(player, 'posts', blindtype, amount)] + self.stacks[player] -= Decimal(amount) + print "DEBUG %s stack %s" % (player, self.stacks[player]) + self.actions['PREFLOP'] += [(player, 'posts', blindtype, amount, self.stacks[player]==0)] if blindtype == 'big blind': self.lastBet['PREFLOP'] = Decimal(amount) elif blindtype == 'small & big blinds': @@ -191,7 +197,9 @@ Card ranks will be uppercased if amount is not None: self.bets[street][player].append(Decimal(amount)) #self.lastBet[street] = Decimal(amount) - self.actions[street] += [(player, 'calls', amount)] + self.stacks[player] -= Decimal(amount) + self.actions[street] += [(player, 'calls', amount, self.stacks[player]==0)] + def addRaiseTo(self, street, player, amountTo): """\ @@ -208,13 +216,19 @@ Add a raise on [street] by [player] to [amountTo] self.lastBet[street] = Decimal(amountTo) amountBy = Decimal(amountTo) - amountToCall self.bets[street][player].append(amountBy+amountToCall) - self.actions[street] += [(player, 'raises', amountBy, amountTo, amountToCall)] + self.stacks[player] -= (Decimal(amountBy)+Decimal(amountToCall)) + print "DEBUG %s stack %s" % (player, self.stacks[player]) + self.actions[street] += [(player, 'raises', amountBy, amountTo, amountToCall, self.stacks[player]==0)] + def addBet(self, street, player, amount): self.checkPlayerExists(player) self.bets[street][player].append(Decimal(amount)) - self.actions[street] += [(player, 'bets', amount)] + self.stacks[player] -= Decimal(amount) + print "DEBUG %s stack %s" % (player, self.stacks[player]) + self.actions[street] += [(player, 'bets', amount, self.stacks[player]==0)] self.lastBet[street] = Decimal(amount) + def addFold(self, street, player): self.checkPlayerExists(player) @@ -246,9 +260,10 @@ Add a raise on [street] by [player] to [amountTo] self.totalpot += reduce(operator.add, self.bets[street][player], 0) print "conventional totalpot:", self.totalpot + + self.totalpot = 0 - - print self.actions + for street in self.actions: uncalled = 0 calls = [0] @@ -278,6 +293,7 @@ Add a raise on [street] by [player] to [amountTo] self.totalpot -= (uncalled - max(calls)) print "new totalpot:", self.totalpot + if self.totalcollected is None: self.totalcollected = 0; for amount in self.collected.values(): @@ -417,15 +433,15 @@ Map the tuple self.gametype onto the pokerstars string describing it def printActionLine(self, act, fh): if act[1] == 'folds': - print >>fh, _("%s: folds" %(act[0])) + print >>fh, _("%s: folds " %(act[0])) elif act[1] == 'checks': - print >>fh, _("%s: checks" %(act[0])) + print >>fh, _("%s: checks " %(act[0])) if act[1] == 'calls': - print >>fh, _("%s: calls $%s" %(act[0], act[2])) + print >>fh, _("%s: calls $%s%s" %(act[0], act[2], ' and is all-in' if act[3] else '')) if act[1] == 'bets': - print >>fh, _("%s: bets $%s" %(act[0], act[2])) + print >>fh, _("%s: bets $%s%s" %(act[0], act[2], ' and is all-in' if act[3] else '')) if act[1] == 'raises': - print >>fh, _("%s: raises $%s to $%s" %(act[0], act[2], act[3])) + print >>fh, _("%s: raises $%s to $%s%s" %(act[0], act[2], act[3], ' and is all-in' if act[5] else '')) # going to use pokereval to figure out hands at some point. # these functions are copied from pokergame.py diff --git a/pyfpdb/HandHistoryConverter.py b/pyfpdb/HandHistoryConverter.py index 256de673..858c18b3 100644 --- a/pyfpdb/HandHistoryConverter.py +++ b/pyfpdb/HandHistoryConverter.py @@ -115,13 +115,15 @@ class HandHistoryConverter: print "\nInput:\n"+hand.string self.readHandInfo(hand) self.readPlayerStacks(hand) + print "DEBUG", hand.stacks self.markStreets(hand) self.readBlinds(hand) self.readHeroCards(hand) # want to generalise to draw games self.readCommunityCards(hand) # read community cards self.readShowdownActions(hand) - # Read action (Note: no guarantee this is in hand order. - for street in hand.streets.groupdict(): + + # Read actions in street order + for street in hand.streetList: # go through them in order if hand.streets.group(street) is not None: self.readAction(hand, street) From 1a462301766791b3ec3d741137f2e2c72ac3da5a Mon Sep 17 00:00:00 2001 From: Worros Date: Tue, 16 Dec 2008 23:45:58 +0900 Subject: [PATCH 18/43] Add first pass Full Tilt converter --- pyfpdb/FulltiltToFpdb.py | 219 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 219 insertions(+) create mode 100755 pyfpdb/FulltiltToFpdb.py diff --git a/pyfpdb/FulltiltToFpdb.py b/pyfpdb/FulltiltToFpdb.py new file mode 100755 index 00000000..a77b129e --- /dev/null +++ b/pyfpdb/FulltiltToFpdb.py @@ -0,0 +1,219 @@ +#!/usr/bin/env python +# Copyright 2008, Carl Gherardi +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# 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 General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +######################################################################## + +import sys +import Configuration +from HandHistoryConverter import * + +# FullTilt HH Format + +#Full Tilt Poker Game #9403951181: Table CR - tay - $0.05/$0.10 - No Limit Hold'em - 9:40:20 ET - 2008/12/09 +#Seat 1: rigoise ($15.95) +#Seat 2: K2dream ($6.70) +#Seat 4: ravens2216 ($10) +#Seat 5: rizkouner ($4) +#Seat 6: Sorrowful ($8.35) +#rigoise posts the small blind of $0.05 +#K2dream posts the big blind of $0.10 +#5 seconds left to act +#rizkouner posts $0.10 +#The button is in seat #6 +#*** HOLE CARDS *** +#Dealt to Sorrowful [8h Qc] +#ravens2216 folds +#rizkouner checks +#Sorrowful has 15 seconds left to act +#Sorrowful folds +#rigoise folds +#K2dream checks +#*** FLOP *** [9d Kc 5c] +#K2dream checks +#rizkouner checks +#*** TURN *** [9d Kc 5c] [5h] +#K2dream has 15 seconds left to act +#K2dream bets $0.20 +#rizkouner calls $0.20 +#*** RIVER *** [9d Kc 5c 5h] [6h] +#K2dream checks +#rizkouner has 15 seconds left to act +#rizkouner bets $0.20 +#K2dream folds +#Uncalled bet of $0.20 returned to rizkouner +#rizkouner mucks +#rizkouner wins the pot ($0.60) +#*** SUMMARY *** +#Total pot $0.65 | Rake $0.05 +#Board: [9d Kc 5c 5h 6h] +#Seat 1: rigoise (small blind) folded before the Flop +#Seat 2: K2dream (big blind) folded on the River +#Seat 4: ravens2216 didn't bet (folded) +#Seat 5: rizkouner collected ($0.60), mucked +#Seat 6: Sorrowful (button) didn't bet (folded) + + +class FullTilt(HandHistoryConverter): + def __init__(self, config, file): + print "Initialising FullTilt converter class" + HandHistoryConverter.__init__(self, config, file, sitename="FullTilt") # Call super class init. + self.sitename = "FullTilt" + self.setFileType("text", "cp1252") + self.rexx.setGameInfoRegex('.*- \$?(?P[.0-9]+)/\$?(?P[.0-9]+)') + self.rexx.setSplitHandRegex('\n\n+') + self.rexx.setHandInfoRegex('.*#(?P[0-9]+): Table (?P[- a-zA-Z]+) - \$?(?P[.0-9]+)/\$?(?P[.0-9]+) - (?P[a-zA-Z\' ]+) - (?P
[0-9]+):(?P[0-9]+):(?P[0-9]+) ET - (?P[0-9]+)/(?P[0-9]+)/(?P[0-9]+)') +# self.rexx.setHandInfoRegex('.*#(?P[0-9]+): Table (?P
[ a-zA-Z]+) - \$?(?P[.0-9]+)/\$?(?P[.0-9]+) - (?P.*) - (?P
[0-9]+):(?P[0-9]+) ET - (?P[0-9]+)/(?P[0-9]+)/(?P[0-9]+)Table (?P
[ a-zA-Z]+)\nSeat (?P
[- a-zA-Z]+) - \$?(?P[.0-9]+)/\$?(?P[.0-9]+) - (?P[a-zA-Z\' ]+) - (?P
[0-9]+):(?P[0-9]+):(?P[0-9]+) ET - (?P[0-9]+)/(?P[0-9]+)/(?P[0-9]+)') # self.rexx.setHandInfoRegex('.*#(?P[0-9]+): Table (?P
[ a-zA-Z]+) - \$?(?P[.0-9]+)/\$?(?P[.0-9]+) - (?P.*) - (?P
[0-9]+):(?P[0-9]+) ET - (?P[0-9]+)/(?P[0-9]+)/(?P[0-9]+)Table (?P
[ a-zA-Z]+)\nSeat (?P
[- a-zA-Z]+) - \$?(?P[.0-9]+)/\$?(?P[.0-9]+) - (?P[a-zA-Z\' ]+) - (?P
[0-9]+):(?P[0-9]+):(?P[0-9]+) ET - (?P[0-9]+)/(?P[0-9]+)/(?P[0-9]+)') # self.rexx.setHandInfoRegex('.*#(?P[0-9]+): Table (?P
[ a-zA-Z]+) - \$?(?P[.0-9]+)/\$?(?P[.0-9]+) - (?P.*) - (?P
[0-9]+):(?P[0-9]+) ET - (?P[0-9]+)/(?P[0-9]+)/(?P[0-9]+)Table (?P
[ a-zA-Z]+)\nSeat (?P
[- a-zA-Z]+) - \$?(?P[.0-9]+)/\$?(?P[.0-9]+) - (?P[a-zA-Z\' ]+) - (?P
[0-9]+):(?P[0-9]+):(?P[0-9]+) ET - (?P[0-9]+)/(?P[0-9]+)/(?P[0-9]+)') + self.rexx.setHandInfoRegex('.*#(?P[0-9]+): Table (?P
[- a-zA-Z]+) (\((?P.+)\) )?- \$?(?P[.0-9]+)/\$?(?P[.0-9]+) - (?P[a-zA-Z\' ]+) - (?P
[0-9]+):(?P[0-9]+):(?P[0-9]+) ET - (?P[0-9]+)/(?P[0-9]+)/(?P[0-9]+)') # self.rexx.setHandInfoRegex('.*#(?P[0-9]+): Table (?P
[ a-zA-Z]+) - \$?(?P[.0-9]+)/\$?(?P[.0-9]+) - (?P.*) - (?P
[0-9]+):(?P[0-9]+) ET - (?P[0-9]+)/(?P[0-9]+)/(?P[0-9]+)Table (?P
[ a-zA-Z]+)\nSeat (?P
[ a-zA-Z]+)\nSeat (?P
[ a-zA-Z]+)\nSeat (?P
[- a-zA-Z]+) (\((?P.+)\) )?- \$?(?P[.0-9]+)/\$?(?P[.0-9]+) - (?P[a-zA-Z\' ]+) - (?P
[0-9]+):(?P[0-9]+):(?P[0-9]+) ET - (?P[0-9]+)/(?P[0-9]+)/(?P[0-9]+)') + self.rexx.setHandInfoRegex('.*#(?P[0-9]+): Table (?P
[- a-zA-Z]+) (\((?P.+)\) )?- \$?(?P[.0-9]+)/\$?(?P[.0-9]+) - (?P[a-zA-Z\' ]+) - (?P.*)') # self.rexx.setHandInfoRegex('.*#(?P[0-9]+): Table (?P
[ a-zA-Z]+) - \$?(?P[.0-9]+)/\$?(?P[.0-9]+) - (?P.*) - (?P
[0-9]+):(?P[0-9]+) ET - (?P[0-9]+)/(?P[0-9]+)/(?P[0-9]+)Table (?P
[ a-zA-Z]+)\nSeat (?P
[ \w]+), (?P\d\d \w+ \d\d\d\d \d\d:\d\d (AM|PM))") + # SB BB HID TABLE DAY MON YEAR HR12 MIN AMPM + + self.rexx.button_re = re.compile('#SUMMARY\nDealer: (?P.*)\n') + + #Seat 1: .Lucchess ($4.17 in chips) + self.rexx.setPlayerInfoRegex('Seat (?P[0-9]+): (?P.*) \((\$(?P[.0-9]+) in chips)\)') + + #ANTES/BLINDS + #helander2222 posts blind ($0.25), lopllopl posts blind ($0.50). + self.rexx.setPostSbRegex('(?P.*) posts blind \(\$?(?P[.0-9]+)\), ') + self.rexx.setPostBbRegex('\), (?P.*) posts blind \(\$?(?P[.0-9]+)\).') + self.rexx.setPostBothRegex('.*\n(?P.*): posts small \& big blinds \[\$? (?P[.0-9]+)') + self.rexx.setHeroCardsRegex('.*\nDealt\sto\s(?P.*)\s\[ (?P.*) \]') + + #lopllopl checks, Eurolll checks, .Lucchess checks. + self.rexx.setActionStepRegex('(, )?(?P.*?)(?P bets| checks| raises| calls| folds)( \$(?P\d*\.?\d*))?[.]?') + + #Uchilka shows [ KC,JD ] + self.rexx.setShowdownActionRegex('(?P.*) shows \[ (?P.+) \]') + + # TODO: read SUMMARY correctly for collected pot stuff. + #Uchilka, bets $11.75, collects $23.04, net $11.29 + self.rexx.setCollectPotRegex('(?P.*), bets.+, collects \$(?P\d*\.?\d*), net.* ') + self.rexx.sits_out_re = re.compile('(?P.*) sits out') + self.rexx.compileRegexes() + + def readSupportedGames(self): + pass + + def determineGameType(self): + # Cheating with this regex, only support nlhe at the moment + gametype = ["ring", "hold", "nl"] + + m = self.rexx.hand_info_re.search(self.obs) + gametype = gametype + [m.group('SB')] + gametype = gametype + [m.group('BB')] + + return gametype + + def readHandInfo(self, hand): + m = self.rexx.hand_info_re.search(hand.string) + hand.handid = m.group('HID') + hand.tablename = m.group('TABLE') + #hand.buttonpos = self.rexx.button_re.search(hand.string).group('BUTTONPNAME') +# These work, but the info is already in the Hand class - should be used for tourneys though. +# m.group('SB') +# m.group('BB') +# m.group('GAMETYPE') + +# Believe Everleaf time is GMT/UTC, no transation necessary +# Stars format (Nov 10 2008): 2008/11/07 12:38:49 CET [2008/11/07 7:38:49 ET] +# or : 2008/11/07 12:38:49 ET +# Not getting it in my HH files yet, so using +# 2008/11/10 3:58:52 ET +#TODO: Do conversion from GMT to ET +#TODO: Need some date functions to convert to different timezones (Date::Manip for perl rocked for this) + + hand.starttime = time.strptime(m.group('DATETIME'), "%d %b %Y %I:%M %p") + #hand.starttime = "%d/%02d/%02d %d:%02d:%02d ET" %(int(m.group('YEAR')), int(m.group('MON')), int(m.group('DAY')), + #int(m.group('HR')), int(m.group('MIN')), int(m.group('SEC'))) + + 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 ** + # 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.*?\n\*\*)?( Dealing Flop \*\* \[ (?P\S\S), (?P\S\S), (?P\S\S) \])?(?P.*?\*\*)?( Dealing Turn \*\* \[ (?P\S\S) \])?(?P.*?\*\*)?( Dealing River \*\* \[ (?P\S\S) \])?(?P.*)', hand.string,re.DOTALL) + + m = re.search(r"PRE-FLOP(?P.+(?=FLOP)|.+(?=SHOWDOWN))" + r"(FLOP (?P\[board cards .+ \].+(?=TURN)|.+(?=SHOWDOWN)))?" + r"(TURN (?P\[board cards .+ \].+(?=RIVER)|.+(?=SHOWDOWN)))?" + r"(RIVER (?P\[board cards .+ \].+(?=SHOWDOWN)))?", hand.string,re.DOTALL) + + hand.addStreets(m) + + + def readCommunityCards(self, hand, street): + self.rexx.board_re = re.compile(r"\[board cards (?P.+) \]") + print hand.streets.group(street) + if street in ('FLOP','TURN','RIVER'): # a list of streets which get dealt community cards (i.e. all but PREFLOP) + m = self.rexx.board_re.search(hand.streets.group(street)) + hand.setCommunityCards(street, m.group('CARDS').split(',')) + + def readBlinds(self, hand): + try: + m = self.rexx.small_blind_re.search(hand.string) + hand.addBlind(m.group('PNAME'), 'small blind', m.group('SB')) + except: # no small blind + hand.addBlind(None, None, None) + for a in self.rexx.big_blind_re.finditer(hand.string): + hand.addBlind(a.group('PNAME'), 'big blind', a.group('BB')) + for a in self.rexx.both_blinds_re.finditer(hand.string): + hand.addBlind(a.group('PNAME'), 'small & big blinds', a.group('SBBB')) + + def readHeroCards(self, hand): + m = self.rexx.hero_cards_re.search(hand.string) + if(m == None): + #Not involved in hand + hand.involved = False + else: + hand.hero = m.group('PNAME') + # "2c, qh" -> set(["2c","qc"]) + # Also works with Omaha hands. + cards = m.group('CARDS') + cards = set(cards.split(',')) + hand.addHoleCards(cards, m.group('PNAME')) + + def readAction(self, hand, street): + m = self.rexx.action_re.finditer(hand.streets.group(street)) + for action in m: + if action.group('ATYPE') == ' raises': + hand.addRaiseTo( street, action.group('PNAME'), action.group('BET') ) + elif action.group('ATYPE') == ' calls': + hand.addCall( street, action.group('PNAME'), action.group('BET') ) + 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')]] + # TODO: Everleaf does not record uncalled bets. + + def readShowdownActions(self, hand): + for shows in self.rexx.showdown_action_re.finditer(hand.string): + cards = shows.group('CARDS') + cards = set(cards.split(',')) + hand.addShownCards(cards, shows.group('PNAME')) + + def readCollectPot(self,hand): + for m in self.rexx.collect_pot_re.finditer(hand.string): + hand.addCollectPot(player=m.group('PNAME'),pot=m.group('POT')) + + def readShownCards(self,hand): + return + #for m in self.rexx.collect_pot_re.finditer(hand.string): + #if m.group('CARDS') is not None: + #cards = m.group('CARDS') + #cards = set(cards.split(',')) + #hand.addShownCards(cards=None, player=m.group('PNAME'), holeandboard=cards) + + + + +if __name__ == "__main__": + c = Configuration.Config() + if len(sys.argv) == 1: + testfile = "regression-test-files/ongame/nlhe/ong NLH handhq_0.txt" + else: + testfile = sys.argv[1] + e = OnGame(c, testfile) + e.processFile() + print str(e) From a5bd7499597cc8e45383593d71f3d6feb47b7e88 Mon Sep 17 00:00:00 2001 From: Matt Turnbull Date: Wed, 17 Dec 2008 00:30:31 +0000 Subject: [PATCH 27/43] quick hack to Hand to help ongame --- pyfpdb/Hand.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyfpdb/Hand.py b/pyfpdb/Hand.py index 26a60c71..e215e99d 100644 --- a/pyfpdb/Hand.py +++ b/pyfpdb/Hand.py @@ -33,7 +33,7 @@ from copy import deepcopy class Hand: # def __init__(self, sitename, gametype, sb, bb, string): - UPS = {'a':'A', 't':'T', 'j':'J', 'q':'Q', 'k':'K'} + UPS = {'a':'A', 't':'T', 'j':'J', 'q':'Q', 'k':'K', 'S':'s', 'C':'c', 'H':'h', 'D':'d'} def __init__(self, sitename, gametype, string): self.sitename = sitename self.gametype = gametype From e5e8643557167bd341ed7764057dce511b294881 Mon Sep 17 00:00:00 2001 From: Worros Date: Wed, 17 Dec 2008 13:46:16 +0900 Subject: [PATCH 28/43] Minor update to handinfo regex for OnGame --- pyfpdb/OnGameToFpdb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyfpdb/OnGameToFpdb.py b/pyfpdb/OnGameToFpdb.py index 78ecc99c..c905905a 100755 --- a/pyfpdb/OnGameToFpdb.py +++ b/pyfpdb/OnGameToFpdb.py @@ -77,7 +77,7 @@ class OnGame(HandHistoryConverter): #Texas Hold'em $.5-$1 NL (real money), hand #P4-76915775-797 #Table Kuopio, 20 Sep 2008 11:59 PM - self.rexx.setHandInfoRegex(r"Texas Hold'em \$?(?P[.0-9]+)-\$?(?P[.0-9]+) NL \(real money\), hand #(?P[A-Z\d-]+)\nTable\ (?P
[ \w]+), (?P\d\d \w+ \d\d\d\d \d\d:\d\d (AM|PM))") + self.rexx.setHandInfoRegex(r"Texas Hold'em \$?(?P[.0-9]+)-\$?(?P[.0-9]+) NL \(real money\), hand #(?P[-A-Z\d]+)\nTable\ (?P
[\' \w]+), (?P\d\d \w+ \d\d\d\d \d\d:\d\d (AM|PM))") # SB BB HID TABLE DAY MON YEAR HR12 MIN AMPM self.rexx.button_re = re.compile('#SUMMARY\nDealer: (?P.*)\n') From dd7ede890332e123bbfe880eeb8990fc0c594b14 Mon Sep 17 00:00:00 2001 From: Worros Date: Wed, 17 Dec 2008 14:04:29 +0900 Subject: [PATCH 29/43] Adjust regex to read 'and is all-in' --- pyfpdb/OnGameToFpdb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyfpdb/OnGameToFpdb.py b/pyfpdb/OnGameToFpdb.py index c905905a..ee16eb41 100755 --- a/pyfpdb/OnGameToFpdb.py +++ b/pyfpdb/OnGameToFpdb.py @@ -93,7 +93,7 @@ class OnGame(HandHistoryConverter): self.rexx.setHeroCardsRegex('.*\nDealt\sto\s(?P.*)\s\[ (?P.*) \]') #lopllopl checks, Eurolll checks, .Lucchess checks. - self.rexx.setActionStepRegex('(, )?(?P.*?)(?P bets| checks| raises| calls| folds)( \$(?P\d*\.?\d*))?[.]?') + self.rexx.setActionStepRegex('(, )?(?P.*?)(?P bets| checks| raises| calls| folds)( \$(?P\d*\.?\d*))?( and is all-in)?') #Uchilka shows [ KC,JD ] self.rexx.setShowdownActionRegex('(?P.*) shows \[ (?P.+) \]') From 877f0771ab1695d0e763d5c636b6e93c473a373b Mon Sep 17 00:00:00 2001 From: Matt Turnbull Date: Wed, 17 Dec 2008 11:22:02 +0000 Subject: [PATCH 30/43] nongreedy matches in collect_pot_re to fix kicker being picked up instead of hand bug --- pyfpdb/EverleafToFpdb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyfpdb/EverleafToFpdb.py b/pyfpdb/EverleafToFpdb.py index 03b22995..bce92e79 100755 --- a/pyfpdb/EverleafToFpdb.py +++ b/pyfpdb/EverleafToFpdb.py @@ -79,7 +79,7 @@ class Everleaf(HandHistoryConverter): self.rexx.setHeroCardsRegex('.*\nDealt\sto\s(?P.*)\s\[ (?P.*) \]') self.rexx.setActionStepRegex('.*\n(?P.*)(?P: bets| checks| raises| calls| folds)(\s\[\$ (?P[.\d]+) USD\])?') self.rexx.setShowdownActionRegex('.*\n(?P.*) shows \[ (?P.*) \]') - self.rexx.setCollectPotRegex('.*\n(?P.*) wins \$ (?P[.\d]+) USD(.*\[ (?P.*) \])?') + self.rexx.setCollectPotRegex('.*\n(?P.*) wins \$ (?P[.\d]+) USD(.*?\[ (?P.*?) \])?') self.rexx.sits_out_re = re.compile('(?P.*) sits out') self.rexx.compileRegexes() From d16816649558f4a4cd08f3e5b513ae65619b278e Mon Sep 17 00:00:00 2001 From: Matt Turnbull Date: Wed, 17 Dec 2008 11:54:26 +0000 Subject: [PATCH 31/43] Added: addCallandRaise - when reported amount is the actual amount transfered addRaiseBy - when reported is the amount additional to the previous bet _addRaise - common helper --- pyfpdb/Hand.py | 61 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 49 insertions(+), 12 deletions(-) diff --git a/pyfpdb/Hand.py b/pyfpdb/Hand.py index e215e99d..f4a7bec7 100644 --- a/pyfpdb/Hand.py +++ b/pyfpdb/Hand.py @@ -204,25 +204,62 @@ Card ranks will be uppercased print "DEBUG %s calls %s, stack %s" % (player, amount, self.stacks[player]) self.actions[street] += [(player, 'calls', amount, self.stacks[player]==0)] - - def addRaiseTo(self, street, player, amountTo): + def addRaiseBy(self, street, player, amountBy): """\ -Add a raise on [street] by [player] to [amountTo] +Add a raise by amountBy on [street] by [player] """ - #Given only the amount raised to, the amount of the raise can be calculated by + #Given only the amount raised by, 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) + # let Bp = previous bet + # Bc = amount player has committed so far + # Rb = raise by + # then: C = Bp - Bc (amount to call) + # Rt = Bp + Rb (raise to) + # 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.stacks[player] -= (Decimal(amountBy)+Decimal(amountToCall)) - print "DEBUG %s stack %s" % (player, self.stacks[player]) - self.actions[street] += [(player, 'raises', amountBy, amountTo, amountToCall, self.stacks[player]==0)] + Rb = Decimal(amountBy) + Bp = self.lastBet[street] + Bc = reduce(operator.add, self.bets[street][player], 0) + C = Bp - Bc + Rt = Bp + Rb + + self.bets[street][player].append(C + Rb) + self.stacks[player] -= (C + Rb) + self.actions[street] += [(player, 'raises', Rb, Rt, C, self.stacks[player]==0)] + self.lastBet[street] = Rt + + def addCallandRaise(self, street, player, amount): + """\ +For sites which by "raises x" mean "calls and raises putting a total of x in the por". """ + self.checkPlayerExists(player) + CRb = Decimal(amount) + Bp = self.lastBet[street] + Bc = reduce(operator.add, self.bets[street][player], 0) + C = Bp - Bc + Rb = CRb - C + Rt = Bp + Rb + + self._addRaise(street, player, C, Rb, Rt) + + def _addRaise(self, street, player, C, Rb, Rt): + self.bets[street][player].append(C + Rb) + self.stacks[player] -= (C + Rb) + self.actions[street] += [(player, 'raises', Rb, Rt, C, self.stacks[player]==0)] + self.lastBet[street] = Rt + + def addRaiseTo(self, street, player, amountTo): + """\ +Add a raise on [street] by [player] to [amountTo] +""" + self.checkPlayerExists(player) + Bc = reduce(operator.add, self.bets[street][player], 0) + Rt = Decimal(amountTo) + C = Bp - Bc + Rb = Rt - C + self._addRaise(street, player, C, Rb, Rt) def addBet(self, street, player, amount): From fe2c8068226ce5108b8c379041c27fd960826b2d Mon Sep 17 00:00:00 2001 From: Matt Turnbull Date: Wed, 17 Dec 2008 11:57:06 +0000 Subject: [PATCH 32/43] Everleaf appears to need addCallandRaise --- pyfpdb/EverleafToFpdb.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyfpdb/EverleafToFpdb.py b/pyfpdb/EverleafToFpdb.py index bce92e79..d0885d92 100755 --- a/pyfpdb/EverleafToFpdb.py +++ b/pyfpdb/EverleafToFpdb.py @@ -80,6 +80,7 @@ class Everleaf(HandHistoryConverter): self.rexx.setActionStepRegex('.*\n(?P.*)(?P: bets| checks| raises| calls| folds)(\s\[\$ (?P[.\d]+) USD\])?') self.rexx.setShowdownActionRegex('.*\n(?P.*) shows \[ (?P.*) \]') self.rexx.setCollectPotRegex('.*\n(?P.*) wins \$ (?P[.\d]+) USD(.*?\[ (?P.*?) \])?') + #self.rexx.setCollectPotRegex('.*\n(?P.*) wins \$ (?P[.\d]+) USD(.*\[ (?P) \S\S, \S\S, \S\S, \S\S, \S\S \])?') self.rexx.sits_out_re = re.compile('(?P.*) sits out') self.rexx.compileRegexes() @@ -169,7 +170,7 @@ class Everleaf(HandHistoryConverter): m = self.rexx.action_re.finditer(hand.streets.group(street)) for action in m: if action.group('ATYPE') == ' raises': - hand.addRaiseTo( street, action.group('PNAME'), action.group('BET') ) + hand.addCallandRaise( street, action.group('PNAME'), action.group('BET') ) elif action.group('ATYPE') == ' calls': hand.addCall( street, action.group('PNAME'), action.group('BET') ) elif action.group('ATYPE') == ': bets': From 7803f523071d9b81d5e81480745cda1dc089c705 Mon Sep 17 00:00:00 2001 From: Matt Turnbull Date: Thu, 18 Dec 2008 17:49:17 +0000 Subject: [PATCH 33/43] autoimport a bit better, no? --- pyfpdb/GuiAutoImport.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/pyfpdb/GuiAutoImport.py b/pyfpdb/GuiAutoImport.py index 30fe7e67..820642e4 100644 --- a/pyfpdb/GuiAutoImport.py +++ b/pyfpdb/GuiAutoImport.py @@ -104,9 +104,12 @@ class GuiAutoImport (threading.Thread): def do_import(self): """Callback for timer to do an import iteration.""" - self.importer.runUpdated() - print "GuiAutoImport.import_dir done" - return self.doAutoImportBool + if self.doAutoImportBool: + self.importer.runUpdated() + print "GuiAutoImport.import_dir done" + return True + else: + return False def startClicked(self, widget, data): """runs when user clicks start on auto import tab""" @@ -149,12 +152,15 @@ class GuiAutoImport (threading.Thread): interval=int(self.intervalEntry.get_text()) gobject.timeout_add(interval*1000, self.do_import) else: # toggled off - self.doAutoImportBool = False # do_import will return this and stop the gobject callback timer - #TODO: other clean up, such as killing HUD - print "Stopping autoimport" - self.pipe_to_hud.communicate('\n') # waits for process to terminate - self.pipe_to_hud = None - widget.set_label(u'Start Autoimport') + self.doAutoImportBool = False # do_import will return this and stop the gobject callback timer + print "Stopping autoimport" + print >>self.pipe_to_hud.stdin, "\n" + #self.pipe_to_hud.communicate('\n') # waits for process to terminate + self.pipe_to_hud = None + self.startButton.set_label(u'Start Autoimport') + + + #end def GuiAutoImport.startClicked def get_vbox(self): From b37ddc5ace00dd1e49c0bd8f7b7f586d636edf33 Mon Sep 17 00:00:00 2001 From: Matt Turnbull Date: Fri, 19 Dec 2008 03:01:45 +0000 Subject: [PATCH 34/43] So close, yet so far. Need to calculate rake to output the side pots line correctly. --- pyfpdb/Hand.py | 152 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 148 insertions(+), 4 deletions(-) diff --git a/pyfpdb/Hand.py b/pyfpdb/Hand.py index f4a7bec7..f06a7926 100644 --- a/pyfpdb/Hand.py +++ b/pyfpdb/Hand.py @@ -307,8 +307,13 @@ Add a raise on [street] by [player] to [amountTo] self.totalpot = 0 + #print "[POT] stack list" + #print dict([(player[1], Decimal(player[2])) for player in self.players]) + players_who_act_preflop = set([x[0] for x in self.actions['PREFLOP']]) + self.pot = Pot(players_who_act_preflop) - for street in self.actions: + #for street in self.actions: + for street in [x for x in self.streetList if x in self.actions]: uncalled = 0 calls = [0] for act in self.actions[street]: @@ -317,18 +322,30 @@ Add a raise on [street] by [player] to [amountTo] uncalled = Decimal(act[2]) # only the last bet or raise can be uncalled calls = [0] print "uncalled: ", uncalled + + self.pot.addMoney(act[0], Decimal(act[2])) + elif act[1] == 'raises': # [name, 'raises', amountby, amountto, amountcalled] print "calls %s and raises %s to %s" % (act[4],act[2],act[3]) self.totalpot += Decimal(act[2]) + Decimal(act[4]) calls = [0] uncalled = Decimal(act[2]) print "uncalled: ", uncalled + + self.pot.addMoney(act[0], Decimal(act[2])+Decimal(act[4])) + elif act[1] == 'calls': # [name, 'calls', amount] self.totalpot += Decimal(act[2]) calls = calls + [Decimal(act[2])] print "calls:", calls + + self.pot.addMoney(act[0], Decimal(act[2])) + elif act[1] == 'posts': self.totalpot += Decimal(act[3]) + + self.pot.addMoney(act[0], Decimal(act[3])) + if act[2] == 'big blind': # the bb gets called by out-of-blinds posts; but sb+bb only calls bb if uncalled == Decimal(act[3]): # a bb is already posted @@ -343,14 +360,15 @@ Add a raise on [street] by [player] to [amountTo] uncalled = Decimal(act[3]) calls = [0] pass - + elif act[1] == 'folds': + self.pot.addFold(act[0]) if uncalled > 0 and max(calls+[0]) < uncalled: print "DEBUG returning some bet, calls:", calls print "DEBUG returned: %.2f from %.2f" % ((uncalled - max(calls)), self.totalpot,) self.totalpot -= (uncalled - max(calls)) print "DEBUG new totalpot:", self.totalpot - + print "DEBUG new Pot.total:", self.pot if self.totalcollected is None: self.totalcollected = 0; @@ -446,7 +464,8 @@ Map the tuple self.gametype onto the pokerstars string describing it print >>fh, "DEBUG: what do they show" print >>fh, _("*** SUMMARY ***") - print >>fh, _("Total pot $%s | Rake $%.2f" % (self.totalpot, self.rake)) # TODO: side pots + print >>fh, "%s | Rake $%.2f" % (self.pot, self.rake) + #print >>fh, _("Total pot $%s | Rake $%.2f" % (self.totalpot, self.rake)) # TODO: side pots board = [] for s in self.board.values(): @@ -544,3 +563,128 @@ Map the tuple self.gametype onto the pokerstars string describing it class FpdbParseError(Exception): pass + +class Pot(object): + + def __init__(self, contenders): + self.contenders = contenders + self.committed = dict([(player,Decimal(0)) for player in contenders]) + self.total = Decimal(0) + + def addFold(self, player): + self.contenders.remove(player) + + def addMoney(self, player, amount): + self.committed[player] += amount + + def __str__(self): + self.total = sum(self.committed.values()) + committed = sorted([ (v,k) for (k,v) in self.committed.items()]) + lastbet = committed[-1][0] - committed[-2][0] + if lastbet > 0: # uncalled + returnto = committed[-1][1] + #print "returning %f to %s" % (lastbet, returnto) + self.total -= lastbet + self.committed[returnto] -= lastbet + + + + # now: for those contenders still contending.. + commitsall = sorted([(v,k) for (k,v) in self.committed.items() if v >0]) + + pots = [] + while len(commitsall) > 0: + commitslive = [(v,k) for (v,k) in commitsall if k in self.contenders] + v1 = commitslive[0][0] + pots += [sum([min(v,v1) for (v,k) in commitsall])] + #print "all: ", commitsall + #print "live:", commitslive + commitsall = [((v-v1),k) for (v,k) in commitsall if v-v1 >0] + + + #print "[**]", pots + + # TODO: I think rake gets taken out of the pots. + # so it goes: + # total pot x. main pot y, side pot z. | rake r + # and y+z+r = x + # for example: + # Total pot $124.30 Main pot $98.90. Side pot $23.40. | Rake $2 + # so....... that's tricky. + if len(pots) == 1: + return "Main pot $%.2f" % pots[0] + elif len(pots) == 2: + return "Main pot $%.2f, side pot $%2.f" % (pots[0],pots[1]) + elif len(pots) == 3: + return "Main pot $%.2f, side pot-1 $%2.f, side pot-2 $.2f" % (pots[0],pots[1],pots[2]) + else: + return "too many pots.. fix me" + + + + + #def addMoney(self, player, amount): + #uncalled = max(self.committed.values()) - self.committed[player] + + #if self.cap: + #overflow = self.committed[player] + amount - self.cap + #if overflow > 0: + #self.total += amount - overflow + #self.committed[player] = self.cap + #self.sidepot.addMoney(player, overflow) + #else: + ## because it was capped, we can only be calling here. + #self.calls.append(min(uncalled,amount)) + #self.committed[player] += amount + #self.total += amount + #else: + ## no player is currently all-in. + + #self.committed[player] += amount + #self.total += amount + + ## is this a new uncalled bet? + #r = amount - uncalled + #if r > 0: + #self.uncalled = (player, r) + #self.calls = [0] + #else: + #self.calls.append(amount + r) + + ## is this player all-in? + #if self.committed[player] == self.stacks[player]: + #self.cap = self.stacks[player] + #contenders = self.contenders[:] + #contenders.remove(player) + #sidepotstacks = dict([(player, self.stacks[player]-self.committed[player]) for player in contenders]) + #self.sidepot = Pot(contenders, sidepotstacks, self.sidepotnum+1) + #elif self.committed[player] > self.stacks[player]: + #print "ERROR %s problem" % (player,) + #print self.committed[player], self.stacks[player] + #raise FpdbParseError + + #def returnUncalled(self): + #print "[POT]" + #print "last bet", self.uncalled + #print "calls:", self.calls + #print + #if self.uncalled: + #if max(self.calls) < self.uncalled[1]: + #self.total -= self.uncalled[1] + #print "returned", self.uncalled[0],self.uncalled[1]-max(self.calls), "from", self.uncalled[1] + + + #def __str__(self): + #total = self.total + #if self.sidepotnum == 0: + #return "Main pot $%.2f%s" %(total, self.sidepot or '' ) + #elif self.sidepotnum > 0: + #if self.sidepot: + #return ", side pot-%d $%.2f%s" % (self.sidepotnum, total, self.sidepot) + #else: + #return ", side pot $%.2f." % (total,) + + + + + From 49aa8921e39427aa0a697b281170fabe414a446a Mon Sep 17 00:00:00 2001 From: Worros Date: Fri, 19 Dec 2008 16:52:32 +0900 Subject: [PATCH 35/43] Grapher: Update to support mutiple sites and players Makes sites actually selectable via checkboxes. Removed the sitename from the graph string for the moment - How that string is generated needs a major overhaul --- pyfpdb/FpdbSQLQueries.py | 9 +++- pyfpdb/GuiGraphViewer.py | 78 ++++++++++++++++++++++++---------- pyfpdb/HUD_config.xml.example | 2 +- pyfpdb/psnlheparser-mct.tgz | Bin 26184 -> 0 bytes 4 files changed, 63 insertions(+), 26 deletions(-) delete mode 100644 pyfpdb/psnlheparser-mct.tgz diff --git a/pyfpdb/FpdbSQLQueries.py b/pyfpdb/FpdbSQLQueries.py index 07fa15bb..9afdc28f 100644 --- a/pyfpdb/FpdbSQLQueries.py +++ b/pyfpdb/FpdbSQLQueries.py @@ -635,6 +635,11 @@ class FpdbSQLQueries: elif(self.dbname == 'SQLite'): self.query['getPlayerId'] = """SELECT id from Players where name = %s""" + if(self.dbname == 'MySQL InnoDB') or (self.dbname == 'PostgreSQL'): + self.query['getSiteId'] = """SELECT id from Sites where name = %s""" + elif(self.dbname == 'SQLite'): + self.query['getSiteId'] = """SELECT id from Sites where name = %s""" + if(self.dbname == 'MySQL InnoDB') or (self.dbname == 'PostgreSQL'): self.query['getRingProfitAllHandsPlayerIdSite'] = """ SELECT hp.handId, hp.winnings, coalesce(hp.ante,0) + SUM(ha.amount) @@ -643,8 +648,8 @@ class FpdbSQLQueries: INNER JOIN Players pl ON hp.playerId = pl.id INNER JOIN Hands h ON h.id = hp.handId INNER JOIN HandsActions ha ON ha.handPlayerId = hp.id - WHERE pl.name = %s - AND pl.siteId = %s + where pl.id in + AND pl.siteId in AND hp.tourneysPlayersId IS NULL GROUP BY hp.handId, hp.winnings, h.handStart, hp.ante ORDER BY h.handStart""" diff --git a/pyfpdb/GuiGraphViewer.py b/pyfpdb/GuiGraphViewer.py index 94732295..a0a2e326 100644 --- a/pyfpdb/GuiGraphViewer.py +++ b/pyfpdb/GuiGraphViewer.py @@ -50,22 +50,27 @@ class GuiGraphViewer (threading.Thread): try: self.canvas.destroy() except AttributeError: pass - # Whaich sites are selected? - # TODO: - # What hero names for the selected site? - # TODO: + sitenos = [] + playerids = [] - name = self.heroes[self.sites] + # Which sites are selected? + for site in self.sites: + if self.sites[site] == True: + sitenos.append(self.siteid[site]) + self.cursor.execute(self.sql.query['getPlayerId'], (self.heroes[site],)) + result = self.db.cursor.fetchall() + if len(result) == 1: + playerids.append(result[0][0]) - if self.sites == "PokerStars": - site=2 - sitename="PokerStars: " - elif self.sites=="Full Tilt": - site=1 - sitename="Full Tilt: " - else: - print "invalid text in site selection in graph, defaulting to PS" - site=2 + if sitenos == []: + #Should probably pop up here. + print "No sites selected - defaulting to PokerStars" + sitenos = [2] + + + if playerids == []: + print "No player ids found" + return self.fig = Figure(figsize=(5,4), dpi=100) @@ -74,7 +79,7 @@ class GuiGraphViewer (threading.Thread): #Get graph data from DB starttime = time() - line = self.getRingProfitGraph(name, site) + line = self.getRingProfitGraph(playerids, sitenos) print "Graph generated in: %s" %(time() - starttime) self.ax.set_title("Profit graph for ring games") @@ -87,7 +92,8 @@ class GuiGraphViewer (threading.Thread): #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)) +# text = "All Hands, " + sitename + str(name) + "\nProfit: $" + str(line[-1]) + "\nTotal Hands: " + str(len(line)) + text = "All Hands, " + "\nProfit: $" + str(line[-1]) + "\nTotal Hands: " + str(len(line)) self.ax.annotate(text, xy=(10, -10), @@ -103,8 +109,26 @@ class GuiGraphViewer (threading.Thread): self.canvas.show() #end of def showClicked - def getRingProfitGraph(self, name, site): - self.cursor.execute(self.sql.query['getRingProfitAllHandsPlayerIdSite'], (name, site)) + def getRingProfitGraph(self, names, sites): + tmp = self.sql.query['getRingProfitAllHandsPlayerIdSite'] +# print "DEBUG: getRingProfitGraph" + + #Buggered if I can find a way to do this 'nicely' take a list of intergers and longs + # and turn it into a tuple readale by sql. + # [5L] into (5) not (5,) and [5L, 2829L] into (5, 2829) + nametest = str(tuple(names)) + sitetest = str(tuple(sites)) + nametest = nametest.replace("L", "") + nametest = nametest.replace(",)",")") + sitetest = sitetest.replace(",)",")") + + #Must be a nicer way to deal with tuples of size 1 ie. (2,) - which makes sql barf + tmp = tmp.replace("", nametest) + tmp = tmp.replace("", sitetest) + +# print "DEBUG: sql query:" +# print tmp + self.cursor.execute(tmp) #returns (HandId,Winnings,Costs,Profit) winnings = self.db.cursor.fetchall() @@ -125,7 +149,6 @@ class GuiGraphViewer (threading.Thread): pname.set_text(player) pname.set_width_chars(20) hbox.pack_start(pname, False, True, 0) - #TODO: Need to connect a callback here pname.connect("changed", self.__set_hero_name, site) #TODO: Look at GtkCompletion - to fill out usernames pname.show() @@ -134,7 +157,7 @@ class GuiGraphViewer (threading.Thread): def __set_hero_name(self, w, site): self.heroes[site] = w.get_text() - print "DEBUG: settings heroes[%s]: %s"%(site, self.heroes[site]) +# print "DEBUG: settings heroes[%s]: %s"%(site, self.heroes[site]) def createSiteLine(self, hbox, site): cb = gtk.CheckButton(site) @@ -144,8 +167,9 @@ class GuiGraphViewer (threading.Thread): def __set_site_select(self, w, site): # This doesn't behave as intended - self.site only allows 1 site for the moment. - self.sites = site - print "self.sites set to %s" %(self.sites) + print w.get_active() + self.sites[site] = w.get_active() + print "self.sites[%s] set to %s" %(site, self.sites[site]) def fillPlayerFrame(self, vbox): for site in self.conf.supported_sites.keys(): @@ -162,6 +186,13 @@ class GuiGraphViewer (threading.Thread): vbox.pack_start(hbox, False, True, 0) hbox.show() self.createSiteLine(hbox, site) + #Get db site id for filtering later + self.cursor.execute(self.sql.query['getSiteId'], (site)) + result = self.db.cursor.fetchall() + if len(result) == 1: + self.siteid[site] = result[0][0] + else: + print "Either 0 or more than one site matched - EEK" def fillDateFrame(self, vbox): # Hat tip to Mika Bostrom - calendar code comes from PokerStats @@ -261,7 +292,8 @@ class GuiGraphViewer (threading.Thread): self.sql=querylist self.conf = config - self.sites = "PokerStars" + self.sites = {} + self.siteid = {} self.heroes = {} # For use in date ranges. diff --git a/pyfpdb/HUD_config.xml.example b/pyfpdb/HUD_config.xml.example index 355c9d2f..bb48c482 100644 --- a/pyfpdb/HUD_config.xml.example +++ b/pyfpdb/HUD_config.xml.example @@ -49,7 +49,7 @@ - + diff --git a/pyfpdb/psnlheparser-mct.tgz b/pyfpdb/psnlheparser-mct.tgz deleted file mode 100644 index b04c80183ec34f4a740acfe9b0f9bebfbcf8e36d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26184 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 z 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 Y^1)&MzteJi9f=iZ()H#E3(k}O1NKQU4FCWD From 2d700820f42ec95edb21213d1e2bbeb65fb46ea7 Mon Sep 17 00:00:00 2001 From: Worros Date: Fri, 19 Dec 2008 16:58:24 +0900 Subject: [PATCH 36/43] Add Everleaf to DB init process Should these come from config on startup...? --- pyfpdb/fpdb_db.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pyfpdb/fpdb_db.py b/pyfpdb/fpdb_db.py index e51538ee..8c0ab66e 100644 --- a/pyfpdb/fpdb_db.py +++ b/pyfpdb/fpdb_db.py @@ -177,6 +177,7 @@ class fpdb_db: self.cursor.execute("INSERT INTO Settings VALUES (118);") self.cursor.execute("INSERT INTO Sites VALUES (DEFAULT, 'Full Tilt Poker', 'USD');") self.cursor.execute("INSERT INTO Sites VALUES (DEFAULT, 'PokerStars', 'USD');") + self.cursor.execute("INSERT INTO Sites VALUES (DEFAULT, 'Everleaf', 'USD');") self.cursor.execute("INSERT INTO TourneyTypes VALUES (DEFAULT, 1, 0, 0, 0, False);") #end def fillDefaultData From db6a8c5b31adbf665e52e95bbd71fb5fa856686e Mon Sep 17 00:00:00 2001 From: Worros Date: Fri, 19 Dec 2008 17:21:58 +0900 Subject: [PATCH 37/43] Grapher: Make date ranges work - MySQL --- pyfpdb/FpdbSQLQueries.py | 2 ++ pyfpdb/GuiGraphViewer.py | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/pyfpdb/FpdbSQLQueries.py b/pyfpdb/FpdbSQLQueries.py index 9afdc28f..4df2fa75 100644 --- a/pyfpdb/FpdbSQLQueries.py +++ b/pyfpdb/FpdbSQLQueries.py @@ -650,6 +650,8 @@ class FpdbSQLQueries: INNER JOIN HandsActions ha ON ha.handPlayerId = hp.id where pl.id in AND pl.siteId in + AND h.handStart > '' + AND h.handStart < '' AND hp.tourneysPlayersId IS NULL GROUP BY hp.handId, hp.winnings, h.handStart, hp.ante ORDER BY h.handStart""" diff --git a/pyfpdb/GuiGraphViewer.py b/pyfpdb/GuiGraphViewer.py index a0a2e326..ae8895ff 100644 --- a/pyfpdb/GuiGraphViewer.py +++ b/pyfpdb/GuiGraphViewer.py @@ -112,6 +112,12 @@ class GuiGraphViewer (threading.Thread): def getRingProfitGraph(self, names, sites): tmp = self.sql.query['getRingProfitAllHandsPlayerIdSite'] # print "DEBUG: getRingProfitGraph" + start_date, end_date = self.__get_dates() + + if start_date == '': + start_date = '1970-01-01' + if end_date == '': + end_date = '2020-12-12' #Buggered if I can find a way to do this 'nicely' take a list of intergers and longs # and turn it into a tuple readale by sql. @@ -125,6 +131,8 @@ class GuiGraphViewer (threading.Thread): #Must be a nicer way to deal with tuples of size 1 ie. (2,) - which makes sql barf tmp = tmp.replace("", nametest) tmp = tmp.replace("", sitetest) + tmp = tmp.replace("", start_date) + tmp = tmp.replace("", end_date) # print "DEBUG: sql query:" # print tmp From 659f0bb508aefd11f29a90a75b49101b6defb4fc Mon Sep 17 00:00:00 2001 From: Worros Date: Fri, 19 Dec 2008 17:27:18 +0900 Subject: [PATCH 38/43] Grapher: Fix Postgres to work again --- pyfpdb/FpdbSQLQueries.py | 6 ++++-- pyfpdb/GuiGraphViewer.py | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/pyfpdb/FpdbSQLQueries.py b/pyfpdb/FpdbSQLQueries.py index 4df2fa75..fe170c8c 100644 --- a/pyfpdb/FpdbSQLQueries.py +++ b/pyfpdb/FpdbSQLQueries.py @@ -663,8 +663,10 @@ class FpdbSQLQueries: INNER JOIN Players pl ON hp.playerId = pl.id INNER JOIN Hands h ON h.id = hp.handId INNER JOIN HandsActions ha ON ha.handPlayerId = hp.id - WHERE pl.name = %s - AND pl.siteId = %s + where pl.id in + AND pl.siteId in + AND h.handStart > '' + AND h.handStart < '' AND hp.tourneysPlayersId IS NULL GROUP BY hp.handId, hp.winnings, h.handStart ORDER BY h.handStart""" diff --git a/pyfpdb/GuiGraphViewer.py b/pyfpdb/GuiGraphViewer.py index ae8895ff..8026a2c7 100644 --- a/pyfpdb/GuiGraphViewer.py +++ b/pyfpdb/GuiGraphViewer.py @@ -195,7 +195,7 @@ class GuiGraphViewer (threading.Thread): hbox.show() self.createSiteLine(hbox, site) #Get db site id for filtering later - self.cursor.execute(self.sql.query['getSiteId'], (site)) + self.cursor.execute(self.sql.query['getSiteId'], (site,)) result = self.db.cursor.fetchall() if len(result) == 1: self.siteid[site] = result[0][0] From 16f9906d84f9c407c69ddc4339a7e714f091988e Mon Sep 17 00:00:00 2001 From: Matt Turnbull Date: Sat, 20 Dec 2008 02:22:21 +0000 Subject: [PATCH 39/43] pot total line output matches pokerstars better --- pyfpdb/Hand.py | 95 ++++++-------------------------------------------- 1 file changed, 10 insertions(+), 85 deletions(-) diff --git a/pyfpdb/Hand.py b/pyfpdb/Hand.py index f06a7926..e33d2d0c 100644 --- a/pyfpdb/Hand.py +++ b/pyfpdb/Hand.py @@ -296,22 +296,20 @@ Add a raise on [street] by [player] to [amountTo] 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) print "DEBUG conventional totalpot:", self.totalpot self.totalpot = 0 - #print "[POT] stack list" - #print dict([(player[1], Decimal(player[2])) for player in self.players]) + players_who_act_preflop = set([x[0] for x in self.actions['PREFLOP']]) self.pot = Pot(players_who_act_preflop) + + # this can now be pruned substantially if Pot is working. #for street in self.actions: for street in [x for x in self.streetList if x in self.actions]: uncalled = 0 @@ -413,9 +411,7 @@ Map the tuple self.gametype onto the pokerstars string describing it print >>fh, _("Table '%s' %d-max Seat #%s is the button" %(self.tablename, self.maxseats, self.buttonpos)) players_who_act_preflop = set([x[0] for x in self.actions['PREFLOP']]) - #print players_who_act_preflop - #print [x[1] for x in self.players] - #print [x for x in self.players if x[1] in players_who_act_preflop] + for player in [x for x in self.players if x[1] in players_who_act_preflop]: #Only print stacks of players who do something preflop print >>fh, _("Seat %s: %s ($%s)" %(player[0], player[1], player[2])) @@ -524,10 +520,6 @@ Map the tuple self.gametype onto the pokerstars string describing it 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 @@ -611,80 +603,13 @@ class Pot(object): # for example: # Total pot $124.30 Main pot $98.90. Side pot $23.40. | Rake $2 # so....... that's tricky. - if len(pots) == 1: - return "Main pot $%.2f" % pots[0] + if len(pots) == 1: # (only use Total pot) + #return "Main pot $%.2f." % pots[0] + return "Total pot $%.2f" % (self.total,) elif len(pots) == 2: - return "Main pot $%.2f, side pot $%2.f" % (pots[0],pots[1]) + return "Total pot $%.2f Main pot $%.2f. Side pot $%2.f." % (self.total, pots[0],pots[1]) elif len(pots) == 3: - return "Main pot $%.2f, side pot-1 $%2.f, side pot-2 $.2f" % (pots[0],pots[1],pots[2]) + return "Total pot $%.2f Main pot $%.2f. Side pot-1 $%2.f. Side pot-2 $.2f." % (self.total, pots[0],pots[1],pots[2]) else: - return "too many pots.. fix me" - - - - - #def addMoney(self, player, amount): - #uncalled = max(self.committed.values()) - self.committed[player] - - #if self.cap: - #overflow = self.committed[player] + amount - self.cap - #if overflow > 0: - #self.total += amount - overflow - #self.committed[player] = self.cap - #self.sidepot.addMoney(player, overflow) - #else: - ## because it was capped, we can only be calling here. - #self.calls.append(min(uncalled,amount)) - #self.committed[player] += amount - #self.total += amount - #else: - ## no player is currently all-in. + return "too many pots.. fix me.", pots - #self.committed[player] += amount - #self.total += amount - - ## is this a new uncalled bet? - #r = amount - uncalled - #if r > 0: - #self.uncalled = (player, r) - #self.calls = [0] - #else: - #self.calls.append(amount + r) - - ## is this player all-in? - #if self.committed[player] == self.stacks[player]: - #self.cap = self.stacks[player] - #contenders = self.contenders[:] - #contenders.remove(player) - #sidepotstacks = dict([(player, self.stacks[player]-self.committed[player]) for player in contenders]) - #self.sidepot = Pot(contenders, sidepotstacks, self.sidepotnum+1) - #elif self.committed[player] > self.stacks[player]: - #print "ERROR %s problem" % (player,) - #print self.committed[player], self.stacks[player] - #raise FpdbParseError - - #def returnUncalled(self): - #print "[POT]" - #print "last bet", self.uncalled - #print "calls:", self.calls - #print - #if self.uncalled: - #if max(self.calls) < self.uncalled[1]: - #self.total -= self.uncalled[1] - #print "returned", self.uncalled[0],self.uncalled[1]-max(self.calls), "from", self.uncalled[1] - - - #def __str__(self): - #total = self.total - #if self.sidepotnum == 0: - #return "Main pot $%.2f%s" %(total, self.sidepot or '' ) - #elif self.sidepotnum > 0: - #if self.sidepot: - #return ", side pot-%d $%.2f%s" % (self.sidepotnum, total, self.sidepot) - #else: - #return ", side pot $%.2f." % (total,) - - - - - From 5d909fb64898330f4f16ea623b114480f0a4fa28 Mon Sep 17 00:00:00 2001 From: Worros Date: Sat, 20 Dec 2008 12:20:18 +0900 Subject: [PATCH 40/43] Reapply stars regex changed reverted during a merge --- pyfpdb/fpdb_simple.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pyfpdb/fpdb_simple.py b/pyfpdb/fpdb_simple.py index 6636c656..e1e0f737 100644 --- a/pyfpdb/fpdb_simple.py +++ b/pyfpdb/fpdb_simple.py @@ -1170,14 +1170,16 @@ def parseHandStartTime(topline, site): tmp=topline[pos1:pos2] isUTC=True else: - tmp=topline[-30:] + tmp=topline #print "parsehandStartTime, tmp:", tmp pos = tmp.find("-")+2 tmp = tmp[pos:] #Need to match either # 2008/09/07 06:23:14 ET or - # 2008/08/17 - 01:14:43 (ET) - m = re.match('(?P[0-9]{4})\/(?P[0-9]{2})\/(?P[0-9]{2})[\- ]+(?P
[0-9]{2}):(?P[0-9]{2}):(?P[0-9]{2})',tmp) + # 2008/08/17 - 01:14:43 (ET) or + # 2008/11/12 9:33:31 CET [2008/11/12 3:33:31 ET] + rexx = '(?P[0-9]{4})\/(?P[0-9]{2})\/(?P[0-9]{2})[\- ]+(?P
[0-9]+):(?P[0-9]+):(?P[0-9]+)' + m = re.search(rexx,tmp) #print "year:", int(m.group('YEAR')), "month", int(m.group('MON')), "day", int(m.group('DAY')), "hour", int(m.group('HR')), "minute", int(m.group('MIN')), "second", int(m.group('SEC')) result = datetime.datetime(int(m.group('YEAR')), int(m.group('MON')), int(m.group('DAY')), int(m.group('HR')), int(m.group('MIN')), int(m.group('SEC'))) else: From 319ea7080d1dca04db8fb8dc24b2e4318b36f1e6 Mon Sep 17 00:00:00 2001 From: Matt Turnbull Date: Sat, 20 Dec 2008 16:48:25 +0000 Subject: [PATCH 41/43] Pot taking on much functionality committing before deleting a lot of commented out bits --- pyfpdb/Hand.py | 225 ++++++++++++++++++++++++++++--------------------- 1 file changed, 130 insertions(+), 95 deletions(-) diff --git a/pyfpdb/Hand.py b/pyfpdb/Hand.py index e33d2d0c..d2cb5300 100644 --- a/pyfpdb/Hand.py +++ b/pyfpdb/Hand.py @@ -54,7 +54,8 @@ class Hand: self.posted = [] self.involved = True - + self.pot = Pot() + # # Collections indexed by street names # @@ -111,6 +112,7 @@ If a player has None chips he won't be added.""" self.players.append([seat, name, chips]) self.stacks[name] = Decimal(chips) self.holecards[name] = set() + self.pot.addPlayer(name) for street in self.streetList: self.bets[street][name] = [] @@ -180,12 +182,24 @@ Card ranks will be uppercased def addBlind(self, player, blindtype, amount): # if player is None, it's a missing small blind. + # TODO: + # The situation we need to cover are: + # Player in small blind posts + # - this is a bet of 1 sb, as yet uncalled. + # Player in the big blind posts + # - this is a bet of 1 bb and is the new uncalled + # + # If a player posts a big & small blind + # + print "DEBUG addBlind: %s posts %s, %s" % (player, blindtype, amount) if player is not None: self.bets['PREFLOP'][player].append(Decimal(amount)) self.stacks[player] -= Decimal(amount) #print "DEBUG %s posts, stack %s" % (player, self.stacks[player]) - self.actions['PREFLOP'] += [(player, 'posts', blindtype, amount, self.stacks[player]==0)] + act = (player, 'posts', blindtype, amount, self.stacks[player]==0) + self.actions['PREFLOP'].append(act) + self.pot.addMoney(player, Decimal(amount)) if blindtype == 'big blind': self.lastBet['PREFLOP'] = Decimal(amount) elif blindtype == 'small & big blinds': @@ -202,7 +216,9 @@ Card ranks will be uppercased #self.lastBet[street] = Decimal(amount) self.stacks[player] -= Decimal(amount) print "DEBUG %s calls %s, stack %s" % (player, amount, self.stacks[player]) - self.actions[street] += [(player, 'calls', amount, self.stacks[player]==0)] + act = (player, 'calls', amount, self.stacks[player]==0) + self.actions[street].append(act) + self.pot.addMoney(player, Decimal(amount)) def addRaiseBy(self, street, player, amountBy): """\ @@ -243,13 +259,7 @@ For sites which by "raises x" mean "calls and raises putting a total of x in the Rt = Bp + Rb self._addRaise(street, player, C, Rb, Rt) - - def _addRaise(self, street, player, C, Rb, Rt): - self.bets[street][player].append(C + Rb) - self.stacks[player] -= (C + Rb) - self.actions[street] += [(player, 'raises', Rb, Rt, C, self.stacks[player]==0)] - self.lastBet[street] = Rt - + def addRaiseTo(self, street, player, amountTo): """\ Add a raise on [street] by [player] to [amountTo] @@ -260,6 +270,15 @@ Add a raise on [street] by [player] to [amountTo] C = Bp - Bc Rb = Rt - C self._addRaise(street, player, C, Rb, Rt) + + def _addRaise(self, street, player, C, Rb, Rt): + self.bets[street][player].append(C + Rb) + self.stacks[player] -= (C + Rb) + act = (player, 'raises', Rb, Rt, C, self.stacks[player]==0) + self.actions[street].append(act) + self.lastBet[street] = Rt # TODO check this is correct + self.pot.addMoney(player, C+Rb) + def addBet(self, street, player, amount): @@ -267,20 +286,24 @@ Add a raise on [street] by [player] to [amountTo] self.bets[street][player].append(Decimal(amount)) self.stacks[player] -= Decimal(amount) print "DEBUG %s bets %s, stack %s" % (player, amount, self.stacks[player]) - self.actions[street] += [(player, 'bets', amount, self.stacks[player]==0)] + act = (player, 'bets', amount, self.stacks[player]==0) + self.actions[street].append(act) self.lastBet[street] = Decimal(amount) + self.pot.addMoney(player, Decimal(amount)) def addFold(self, street, player): print "DEBUG: %s %s folded" % (street, player) self.checkPlayerExists(player) self.folded.add(player) - self.actions[street] += [(player, 'folds')] + self.pot.addFold(player) + self.actions[street].append((player, 'folds')) + def addCheck(self, street, player): print "DEBUG: %s %s checked" % (street, player) self.checkPlayerExists(player) - self.actions[street] += [(player, 'checks')] + self.actions[street].append((player, 'checks')) def addCollectPot(self,player, pot): print "DEBUG: %s collected %s" % (player, pot) @@ -294,79 +317,75 @@ Add a raise on [street] by [player] to [amountTo] def totalPot(self): """If all bets and blinds have been added, totals up the total pot size""" if self.totalpot is None: - self.totalpot = 0 - - for player in [x[1] for x in self.players]: - for street in self.streetList: - self.totalpot += reduce(operator.add, self.bets[street][player], 0) - - print "DEBUG conventional totalpot:", self.totalpot + #players_who_act_preflop = set([x[0] for x in self.actions['PREFLOP']]) + #print players_who_act_preflop + #print self.pot.contenders + #self.pot.contenders = self.pot.contenders.intersection(players_who_act_preflop).difference(self.folded) + print self.pot.contenders + self.pot.end() + self.totalpot =self.pot.total - - self.totalpot = 0 - - players_who_act_preflop = set([x[0] for x in self.actions['PREFLOP']]) - self.pot = Pot(players_who_act_preflop) + #self.pot = Pot(players_who_act_preflop) # this can now be pruned substantially if Pot is working. #for street in self.actions: - for street in [x for x in self.streetList if x in self.actions]: - uncalled = 0 - calls = [0] - for act in self.actions[street]: - if act[1] == 'bets': # [name, 'bets', amount] - self.totalpot += Decimal(act[2]) - uncalled = Decimal(act[2]) # only the last bet or raise can be uncalled - calls = [0] - print "uncalled: ", uncalled + #for street in [x for x in self.streetList if x in self.actions]: + #uncalled = 0 + #calls = [0] + #for act in self.actions[street]: + #if act[1] == 'bets': # [name, 'bets', amount] + #self.totalpot += Decimal(act[2]) + #uncalled = Decimal(act[2]) # only the last bet or raise can be uncalled + #calls = [0] + #print "uncalled: ", uncalled - self.pot.addMoney(act[0], Decimal(act[2])) - elif act[1] == 'raises': # [name, 'raises', amountby, amountto, amountcalled] - print "calls %s and raises %s to %s" % (act[4],act[2],act[3]) - self.totalpot += Decimal(act[2]) + Decimal(act[4]) - calls = [0] - uncalled = Decimal(act[2]) - print "uncalled: ", uncalled - self.pot.addMoney(act[0], Decimal(act[2])+Decimal(act[4])) + #elif act[1] == 'raises': # [name, 'raises', amountby, amountto, amountcalled] + #print "calls %s and raises %s to %s" % (act[4],act[2],act[3]) + #self.totalpot += Decimal(act[2]) + Decimal(act[4]) + #calls = [0] + #uncalled = Decimal(act[2]) + #print "uncalled: ", uncalled - elif act[1] == 'calls': # [name, 'calls', amount] - self.totalpot += Decimal(act[2]) - calls = calls + [Decimal(act[2])] - print "calls:", calls - self.pot.addMoney(act[0], Decimal(act[2])) - elif act[1] == 'posts': - self.totalpot += Decimal(act[3]) + #elif act[1] == 'calls': # [name, 'calls', amount] + #self.totalpot += Decimal(act[2]) + #calls = calls + [Decimal(act[2])] + #print "calls:", calls - self.pot.addMoney(act[0], Decimal(act[3])) - if act[2] == 'big blind': - # the bb gets called by out-of-blinds posts; but sb+bb only calls bb - if uncalled == Decimal(act[3]): # a bb is already posted - calls = calls + [Decimal(act[3])] - elif 0 < uncalled < Decimal(act[3]): # a sb is already posted, btw wow python can do a 0 and max(calls+[0]) < uncalled: + + #elif act[1] == 'posts': + #self.totalpot += Decimal(act[3]) + + + + #if act[2] == 'big blind': + ## the bb gets called by out-of-blinds posts; but sb+bb only calls bb + #if uncalled == Decimal(act[3]): # a bb is already posted + #calls = calls + [Decimal(act[3])] + #elif 0 < uncalled < Decimal(act[3]): # a sb is already posted, btw wow python can do a 0 and max(calls+[0]) < uncalled: - print "DEBUG returning some bet, calls:", calls - print "DEBUG returned: %.2f from %.2f" % ((uncalled - max(calls)), self.totalpot,) - self.totalpot -= (uncalled - max(calls)) - print "DEBUG new totalpot:", self.totalpot - print "DEBUG new Pot.total:", self.pot + #print "DEBUG returning some bet, calls:", calls + #print "DEBUG returned: %.2f from %.2f" % ((uncalled - max(calls)), self.totalpot,) + #self.totalpot -= (uncalled - max(calls)) + #print "DEBUG new totalpot:", self.totalpot + #print "DEBUG new Pot.total:", self.pot if self.totalcollected is None: self.totalcollected = 0; @@ -558,19 +577,30 @@ class FpdbParseError(Exception): pass class Pot(object): - def __init__(self, contenders): - self.contenders = contenders - self.committed = dict([(player,Decimal(0)) for player in contenders]) - self.total = Decimal(0) - + + def __init__(self): + self.contenders = set() + self.committed = {} + #self.committed = dict([(player,Decimal(0)) for player in contenders]) + self.total = None + + def addPlayer(self,player): + #self.contenders.add(player) + self.committed[player] = Decimal(0) + def addFold(self, player): - self.contenders.remove(player) + # addFold must be called when a player folds + self.contenders.discard(player) def addMoney(self, player, amount): + # addMoney must be called for any actions that put money in the pot, in the order they occur + self.contenders.add(player) self.committed[player] += amount - - def __str__(self): + + def end(self): self.total = sum(self.committed.values()) + + # Return any uncalled bet. committed = sorted([ (v,k) for (k,v) in self.committed.items()]) lastbet = committed[-1][0] - committed[-2][0] if lastbet > 0: # uncalled @@ -580,36 +610,41 @@ class Pot(object): self.committed[returnto] -= lastbet - - # now: for those contenders still contending.. + # Work out side pots + # commitsall = sorted([(v,k) for (k,v) in self.committed.items() if v >0]) - pots = [] + self.pots = [] while len(commitsall) > 0: commitslive = [(v,k) for (v,k) in commitsall if k in self.contenders] v1 = commitslive[0][0] - pots += [sum([min(v,v1) for (v,k) in commitsall])] + self.pots += [sum([min(v,v1) for (v,k) in commitsall])] #print "all: ", commitsall #print "live:", commitslive commitsall = [((v-v1),k) for (v,k) in commitsall if v-v1 >0] - - #print "[**]", pots - + # TODO: I think rake gets taken out of the pots. # so it goes: # total pot x. main pot y, side pot z. | rake r # and y+z+r = x # for example: # Total pot $124.30 Main pot $98.90. Side pot $23.40. | Rake $2 - # so....... that's tricky. - if len(pots) == 1: # (only use Total pot) - #return "Main pot $%.2f." % pots[0] + + def __str__(self): + if self.total is None: + print "call Pot.end() before printing pot total" + # NB if I'm sure end() is idempotent, call it here. + raise FpdbParseError + + + + if len(self.pots) == 1: # (only use Total pot) return "Total pot $%.2f" % (self.total,) - elif len(pots) == 2: - return "Total pot $%.2f Main pot $%.2f. Side pot $%2.f." % (self.total, pots[0],pots[1]) - elif len(pots) == 3: - return "Total pot $%.2f Main pot $%.2f. Side pot-1 $%2.f. Side pot-2 $.2f." % (self.total, pots[0],pots[1],pots[2]) + elif len(self.pots) == 2: + return "Total pot $%.2f Main pot $%.2f. Side pot $%2.f." % (self.total, self.pots[0], self.pots[1]) + elif len(self.pots) == 3: + return "Total pot $%.2f Main pot $%.2f. Side pot-1 $%2.2f. Side pot-2 $%.2f." % (self.total, self.pots[0], self.pots[1], self.pots[2]) else: - return "too many pots.. fix me.", pots + return "too many pots.. fix me.", self.pots From da41e8e2ce2bedd52f2cb6288a39ec50f89418a4 Mon Sep 17 00:00:00 2001 From: Matt Turnbull Date: Sat, 20 Dec 2008 16:57:12 +0000 Subject: [PATCH 42/43] Rid of cruft --- pyfpdb/Hand.py | 78 +++----------------------------------------------- 1 file changed, 4 insertions(+), 74 deletions(-) diff --git a/pyfpdb/Hand.py b/pyfpdb/Hand.py index d2cb5300..09f52065 100644 --- a/pyfpdb/Hand.py +++ b/pyfpdb/Hand.py @@ -316,77 +316,13 @@ Add a raise on [street] by [player] to [amountTo] def totalPot(self): """If all bets and blinds have been added, totals up the total pot size""" + + # This gives us the total amount put in the pot if self.totalpot is None: - #players_who_act_preflop = set([x[0] for x in self.actions['PREFLOP']]) - #print players_who_act_preflop - #print self.pot.contenders - #self.pot.contenders = self.pot.contenders.intersection(players_who_act_preflop).difference(self.folded) - print self.pot.contenders self.pot.end() - self.totalpot =self.pot.total - - #self.pot = Pot(players_who_act_preflop) - - - # this can now be pruned substantially if Pot is working. - #for street in self.actions: - #for street in [x for x in self.streetList if x in self.actions]: - #uncalled = 0 - #calls = [0] - #for act in self.actions[street]: - #if act[1] == 'bets': # [name, 'bets', amount] - #self.totalpot += Decimal(act[2]) - #uncalled = Decimal(act[2]) # only the last bet or raise can be uncalled - #calls = [0] - #print "uncalled: ", uncalled - - - - #elif act[1] == 'raises': # [name, 'raises', amountby, amountto, amountcalled] - #print "calls %s and raises %s to %s" % (act[4],act[2],act[3]) - #self.totalpot += Decimal(act[2]) + Decimal(act[4]) - #calls = [0] - #uncalled = Decimal(act[2]) - #print "uncalled: ", uncalled - - - - #elif act[1] == 'calls': # [name, 'calls', amount] - #self.totalpot += Decimal(act[2]) - #calls = calls + [Decimal(act[2])] - #print "calls:", calls - - - - #elif act[1] == 'posts': - #self.totalpot += Decimal(act[3]) - + self.totalpot = self.pot.total - - #if act[2] == 'big blind': - ## the bb gets called by out-of-blinds posts; but sb+bb only calls bb - #if uncalled == Decimal(act[3]): # a bb is already posted - #calls = calls + [Decimal(act[3])] - #elif 0 < uncalled < Decimal(act[3]): # a sb is already posted, btw wow python can do a 0 and max(calls+[0]) < uncalled: - - #print "DEBUG returning some bet, calls:", calls - #print "DEBUG returned: %.2f from %.2f" % ((uncalled - max(calls)), self.totalpot,) - #self.totalpot -= (uncalled - max(calls)) - #print "DEBUG new totalpot:", self.totalpot - #print "DEBUG new Pot.total:", self.pot - + # This gives us the amount collected, i.e. after rake if self.totalcollected is None: self.totalcollected = 0; for amount in self.collected.values(): @@ -581,11 +517,9 @@ class Pot(object): def __init__(self): self.contenders = set() self.committed = {} - #self.committed = dict([(player,Decimal(0)) for player in contenders]) self.total = None def addPlayer(self,player): - #self.contenders.add(player) self.committed[player] = Decimal(0) def addFold(self, player): @@ -611,7 +545,6 @@ class Pot(object): # Work out side pots - # commitsall = sorted([(v,k) for (k,v) in self.committed.items() if v >0]) self.pots = [] @@ -619,11 +552,8 @@ class Pot(object): commitslive = [(v,k) for (v,k) in commitsall if k in self.contenders] v1 = commitslive[0][0] self.pots += [sum([min(v,v1) for (v,k) in commitsall])] - #print "all: ", commitsall - #print "live:", commitslive commitsall = [((v-v1),k) for (v,k) in commitsall if v-v1 >0] - # TODO: I think rake gets taken out of the pots. # so it goes: # total pot x. main pot y, side pot z. | rake r From 0857739d83faad274fabc7f20192c2d8ececea9f Mon Sep 17 00:00:00 2001 From: Worros Date: Wed, 31 Dec 2008 01:28:28 +0900 Subject: [PATCH 43/43] =?UTF-8?q?Add=20Euro=20Symbol=20(=E2=82=AC)=20and?= =?UTF-8?q?=20EUR=20to=20regexes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyfpdb/EverleafToFpdb.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyfpdb/EverleafToFpdb.py b/pyfpdb/EverleafToFpdb.py index d0885d92..41052f66 100755 --- a/pyfpdb/EverleafToFpdb.py +++ b/pyfpdb/EverleafToFpdb.py @@ -72,14 +72,14 @@ class Everleaf(HandHistoryConverter): self.rexx.setGameInfoRegex('.*Blinds \$?(?P[.0-9]+)/\$?(?P[.0-9]+)') self.rexx.setSplitHandRegex('\n\n+') self.rexx.setHandInfoRegex('.*#(?P[0-9]+)\n.*\nBlinds \$?(?P[.0-9]+)/\$?(?P[.0-9]+) (?P.*) - (?P\d\d\d\d/\d\d/\d\d - \d\d:\d\d:\d\d)\nTable (?P
[ a-zA-Z]+)\nSeat (?P