diff --git a/packaging/debian/changelog b/packaging/debian/changelog index c09713a3..8ebf2bcd 100644 --- a/packaging/debian/changelog +++ b/packaging/debian/changelog @@ -1,3 +1,9 @@ +free-poker-tools (0.21~rc1) unstable; urgency=low + + * First 0.21 release candidate + + -- Mika Bostrom Mon, 06 Dec 2010 17:30:54 +0200 + free-poker-tools (0.20.906-1) unstable; urgency=low * New snapshot diff --git a/packaging/debian/control b/packaging/debian/control index c510e1c8..945b741e 100644 --- a/packaging/debian/control +++ b/packaging/debian/control @@ -11,9 +11,8 @@ Section: games Priority: extra Depends: ${python:Depends}, python-gtk2, python-matplotlib, python-support, python-xlib, - mysql-server | postgresql | python-pysqlite2, - python-psycopg2 | python-mysqldb -Suggests: wine + python-pysqlite2 +Suggests: wine, postgresql | mysql-server, python-psycopg2 | python-mysqldb Description: free poker database with HUD FPDB is a statistics tool for online poker. It supports most sites and several games. Most prominent feature is its heads-up display diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py index 89d3d630..b9786b16 100644 --- a/pyfpdb/Database.py +++ b/pyfpdb/Database.py @@ -60,11 +60,14 @@ import Configuration # Other library modules try: import sqlalchemy.pool as pool - use_pool = True + #use_pool = True + # Forcing to False so we can use connection.row_factory + use_pool = False except ImportError: log.info(_("Not using sqlalchemy connection pool.")) use_pool = False + try: from numpy import var use_numpy = True @@ -446,7 +449,8 @@ class Database: self.db_path = database log.info(_("Connecting to SQLite: %(database)s") % {'database':self.db_path}) if os.path.exists(database) or create: - self.connection = sqlite3.connect(self.db_path, detect_types=sqlite3.PARSE_DECLTYPES ) + self.connection = sqlite3.connect(self.db_path, detect_types=sqlite3.PARSE_DECLTYPES) + self.connection.row_factory = sqlite3.Row self.__connected = True sqlite3.register_converter("bool", lambda x: bool(int(x))) sqlite3.register_adapter(bool, lambda x: "1" if x else "0") @@ -2094,7 +2098,7 @@ class Database: if (game['type']=='ring'): line[0] = 1 # count ring hands if (game['type']=='tour'): line[1] = 1 # count tour hands if (game['type']=='ring'): line[2] = pdata[p]['totalProfit'] #sum of profit - if (game['type']=='ring'): line[3] = float(Decimal(pdata[p]['totalProfit'])/Decimal(bigBet)) #sum of big bets won + if (game['type']=='ring'): line[3] = 0 #float(Decimal(pdata[p]['totalProfit'])/Decimal(bigBet)) #sum of big bets won line[4] = startTime inserts.append(line) diff --git a/pyfpdb/GuiReplayer.py b/pyfpdb/GuiReplayer.py index ba5a985e..720a17c4 100644 --- a/pyfpdb/GuiReplayer.py +++ b/pyfpdb/GuiReplayer.py @@ -31,6 +31,9 @@ import gtk import math import gobject +import pprint +pp = pprint.PrettyPrinter(indent=4) + class GuiReplayer: def __init__(self, config, querylist, mainwin, options = None, debug=True): @@ -100,30 +103,16 @@ class GuiReplayer: self.replayBox.pack_start(self.area) - self.MyHand = self.importhand() + gobject.timeout_add(1000,self.draw_action) - self.maxseats=self.MyHand.maxseats + self.MyHand = self.importhand() + self.table = Table(self.area, self.MyHand).table if self.MyHand.gametype['currency']=="USD": #TODO: check if there are others .. self.currency="$" elif self.MyHand.gametype['currency']=="EUR": self.currency="€" - - self.table={} #create table with positions, player names, status (live/folded), stacks and chips on table - for i in range(0,self.maxseats): # radius: 200, center: 250,250 - x= int (round(250+200*math.cos(2*i*math.pi/self.maxseats))) - y= int (round(250+200*math.sin(2*i*math.pi/self.maxseats))) - try: - self.table[i]={"name":self.MyHand.players[i][1],"stack":Decimal(self.MyHand.players[i][2]),"x":x,"y":y,"chips":0,"status":"live"} #save coordinates of each player - try: - self.table[i]['holecards']=self.MyHand.holecards["PREFLOP"][self.MyHand.players[i][1]][1]+' '+self.MyHand.holecards["PREFLOP"][self.MyHand.players[i][1]][2] - print "holecards: ",self.table[i]['holecards'] - except: - self.table[i]['holecards']='' - except IndexError: #if seat is empty - print "seat ",i+1," out of ",self.maxseats," empty" - self.actions=[] #create list with all actions if isinstance(self.MyHand, HoldemOmahaHand): @@ -133,7 +122,6 @@ class GuiReplayer: self.action_number=0 self.action_level=0 self.pot=0 - gobject.timeout_add(1000,self.draw_action) def area_expose(self, area, event): @@ -249,7 +237,7 @@ class GuiReplayer: be replaced by a function to select a hand from the db in the not so distant future. This code has been shamelessly stolen from Carl """ - if True: + if False: settings = {} settings.update(self.conf.get_db_parameters()) settings.update(self.conf.get_import_parameters()) @@ -276,7 +264,7 @@ class GuiReplayer: # for the Hand.__init__ ####### Shift this section in Database.py for all to use ###### - handid = 40 + handid = 1 q = self.sql.query['get_gameinfo_from_hid'] q = q.replace('%s', self.sql.query['placeholder']) @@ -295,10 +283,74 @@ class GuiReplayer: print "DEBUG: Create stud hand here" elif gametype['base'] == 'draw': print "DEBUG: Create draw hand here" + return h def temp(self): pass +class Table: + def __init__(self, darea, hand): + self.darea = darea + self.hand = hand + self.players = [] + #self.pixmap = gtk.gdk.Pixmap(darea, width, height, depth=-1) + + # tmp var while refactoring + self.table = {} + i = 0 + for seat, name, chips in hand.players: + self.players.append(Player(hand, name, chips, seat)) + self.table[i] = self.players[i].get_hash() + i += 1 + + pp.pprint(self.table) + + def draw(self): + draw_players() + draw_pot() + draw_community_cards() + +class Player: + def __init__(self, hand, name, stack, seat): + self.status = 'live' + self.stack = Decimal(stack) + self.chips = 0 + self.seat = seat + self.name = name + self.holecards = hand.join_holecards(name) + self.x = int (round(250+200*math.cos(2*self.seat*math.pi/hand.maxseats))) + self.y = int (round(250+200*math.sin(2*self.seat*math.pi/hand.maxseats))) + + def get_hash(self): + return { 'chips': 0, + 'holecards': self.holecards, + 'name': self.name, + 'stack': self.stack, + 'status': self.status, + 'x': self.x, + 'y': self.y, + } + + def draw(self): + draw_name() + draw_stack() + draw_cards() + +class Pot: + def __init__(self, hand): + self.total = 0.0 + + def draw(self): + pass + +class CommunityCards: + def __init__(self, hand): + self.pixbuf = self.gen_pixbuf_from_file(PATH_TO_THE_FILE) + + def draw(self): + pass + + def main(argv=None): """main can also be called in the python interpreter, by supplying the command line as the argument.""" if argv is None: diff --git a/pyfpdb/GuiRingPlayerStats.py b/pyfpdb/GuiRingPlayerStats.py index d3ba1bee..86ba1aad 100644 --- a/pyfpdb/GuiRingPlayerStats.py +++ b/pyfpdb/GuiRingPlayerStats.py @@ -52,7 +52,7 @@ onlinehelp = {'Game':_('Type of Game'), 'PF3':_('% Pre Flop Re-Raise / 3Bet'), 'AggFac':_('Aggression Factor\n'), 'AggFreq':_('Aggression Frequency\nBet or Raise vs Fold'), - 'ContBet':_('Continuation Bet on the flop'), + 'ContBet':_('Continuation Bet post-flop'), 'RFI':_('% Raise First In\% Raise when first to bet'), 'Steals':_('% First to raise pre-flop\nand steal blinds'), 'Saw_F':_('% Saw Flop vs hands dealt'), diff --git a/pyfpdb/Hand.py b/pyfpdb/Hand.py index 10f15f57..405a56e5 100644 --- a/pyfpdb/Hand.py +++ b/pyfpdb/Hand.py @@ -289,104 +289,131 @@ db: a connected Database object""" hp.seatno, round(hp.winnings / 100.0,2) as winnings, p.name, - round(hp.startcash / 100.0,2) as chips, - hp.card1,hp.card2, + round(hp.startCash / 100.0,2) as chips, + hp.card1,hp.card2,hp.card3,hp.card4, hp.position FROM HandsPlayers as hp, Players as p WHERE hp.handId = %s - and p.id = hp.playerid + and p.id = hp.playerId + ORDER BY + hp.seatno """ q = q.replace('%s', db.sql.query['placeholder']) # PlayerStacks c.execute(q, (handId,)) - for (seat, winnings, name, chips, card1,card2, position) in c.fetchall(): - print "DEBUG: seat: '%s'\tname: '%s'\tchips: '%s'" % (seat, name, chips) + for (seat, winnings, name, chips, card1, card2, card3, card4, position) in c.fetchall(): + #print "DEBUG: addPlayer(%s, %s, %s)" %(seat,name,str(chips)) self.addPlayer(seat,name,str(chips)) - #if card1 and card2: - # self.addHoleCards(map(Card.valueSuitFromCard, (card1,card2)), name, dealt=True) - #if winnings > 0: - # self.addCollectPot(name, winnings) - #if position == 'B': - # self.buttonpos = seat + #print "DEBUG: card1: %s" % card1 + # map() should work, but is returning integers... FIXME later + #cardlist = map(Card.valueSuitFromCard, [card1, card2, card3, card4]) + cardlist = [Card.valueSuitFromCard(card1), Card.valueSuitFromCard(card2), Card.valueSuitFromCard(card3), Card.valueSuitFromCard(card4)] + #print "DEUBG: cardlist: '%s'" % cardlist + if cardlist[0] == '': + pass + elif self.gametype['category'] == 'holdem': + self.addHoleCards('PREFLOP', name, closed=cardlist[0:2], shown=False, mucked=False, dealt=True) + elif self.gametype['category'] == 'omaha': + self.addHoleCards('PREFLOP', name, closed=cardlist, shown=False, mucked=False, dealt=True) + if winnings > 0: + self.addCollectPot(name, str(winnings)) + if position == 'B': + self.buttonpos = seat - # HandInfo : HID, TABLE - # BUTTON - why is this treated specially in Hand? - # answer: it is written out in hand histories - # still, I think we should record all the active seat positions in a seat_order array - #c.execute("""SELECT - # h.sitehandno as hid, - # h.tablename as table, - # h.startTime as startTime - # FROM - # Hands as h - # WHERE h.id = %(handid)s - # """, {'handid':handid}) - #res = c.fetchone() - #h.handid = res[0] - #h.tablename = res[1] - #h.startTime = res[2] # automatically a datetime + # HandInfo + q = """SELECT * + FROM Hands + WHERE id = %s + """ + q = q.replace('%s', db.sql.query['placeholder']) + c.execute(q, (handId,)) + + # NOTE: This relies on row_factory = sqlite3.Row (set in connect() params) + # Need to find MySQL and Postgres equivalents + # MySQL maybe: cursorclass=MySQLdb.cursors.DictCursor + res = c.fetchone() + self.tablename = res['tableName'] + self.handid = res['siteHandNo'] + self.startTime = datetime.datetime.strptime(res['startTime'], "%Y-%m-%d %H:%M:%S+00:00") + #res['tourneyId'] + #gametypeId + #res['importTime'] # Don't really care about this + #res['seats'] + self.maxseats = res['maxSeats'] + #res['rush'] + cards = map(Card.valueSuitFromCard, [res['boardcard1'], res['boardcard2'], res['boardcard3'], res['boardcard4'], res['boardcard5']]) + #print "DEBUG: res['boardcard1']: %s" % res['boardcard1'] + #print "DEBUG: cards: %s" % cards + if cards[0]: + self.setCommunityCards('FLOP', cards[0:3]) + if cards[3]: + self.setCommunityCards('TURN', [cards[3]]) + if cards[4]: + self.setCommunityCards('RIVER', [cards[4]]) + # playersVpi | playersAtStreet1 | playersAtStreet2 | playersAtStreet3 | + # playersAtStreet4 | playersAtShowdown | street0Raises | street1Raises | + # street2Raises | street3Raises | street4Raises | street1Pot | street2Pot | + # street3Pot | street4Pot | showdownPot | comment | commentTs | texture - #cards = map(Card.valueSuitFromCard, res[11:16] ) - #if cards[0]: - # h.setCommunityCards('FLOP', cards[0:3]) - #if cards[3]: - # h.setCommunityCards('TURN', [cards[3]]) - #if cards[4]: - # h.setCommunityCards('RIVER', [cards[4]]) - #[Card.valueSuitFromCard(x) for x in cards] + # Actions + q = """SELECT + ha.actionNo, + p.name, + ha.street, + ha.actionId, + ha.allIn, + round(ha.amount / 100.0,2) as bet + FROM + HandsActions as ha, + HandsPlayers as hp, + Players as p, + Hands as h + WHERE + h.id = %s + and ha.handsPlayerId = hp.id + and hp.playerId = p.id + AND h.id = hp.handId + ORDER BY + ha.id ASC +; """ + q = q.replace('%s', db.sql.query['placeholder']) + c.execute(q, (handId,)) + for row in c.fetchall(): + name = row['name'] + street = row['street'] + act = row['actionId'] + # allin True/False if row['allIn'] == 0 + bet = row['bet'] + street = self.allStreets[int(street)+1] + #print "DEBUG: name: '%s' street: '%s' act: '%s' bet: '%s'" %(name, street, act, bet) + if act == 2: # Small Blind + print "DEBUG: addBlind(%s, 'small blind', %s" %(name, str(bet)) + self.addBlind(name, 'small blind', str(bet)) + elif act == 4: # Big Blind + self.addBlind(name, 'big blind', str(bet)) + elif act == 6: # Call + self.addCall(street, name, str(bet)) + elif act == 8: # Bet + self.addBet(street, name, str(bet)) + elif act == 10: # Fold + self.addFold(street, name) + elif act == 11: # Check + self.addCheck(street, name) + else: + print "DEBUG: unknown action: '%s'" % act - - # actions - #c.execute("""SELECT - # (ha.street,ha.actionno) as actnum, - # p.name, - # ha.street, - # ha.action, - # ha.allin, - # round(ha.amount / 100.0,2) - # FROM - # handsplayers as hp, - # handsactions as ha, - # players as p - # WHERE - # hp.handid = %(handid)s - # and ha.handsplayerid = hp.id - # and p.id = hp.playerid - # ORDER BY - # ha.street,ha.actionno - # """, {'handid':handid}) - #res = c.fetchall() - #for (actnum,player, streetnum, act, allin, amount) in res: - # act=act.strip() - # street = h.allStreets[streetnum+1] - # if act==u'blind': - # h.addBlind(player, 'big blind', amount) - # # TODO: The type of blind is not recorded in the DB. - # # TODO: preflop street name anomalies in Hand - # elif act==u'fold': - # h.addFold(street,player) - # elif act==u'call': - # h.addCall(street,player,amount) - # elif act==u'bet': - # h.addBet(street,player,amount) - # elif act==u'check': - # h.addCheck(street,player) - # elif act==u'unbet': - # pass - # else: - # print act, player, streetnum, allin, amount - # # TODO : other actions + self.totalPot() + self.rake = self.totalpot - self.totalcollected + self.writeHand() #hhc.readShowdownActions(self) #hc.readShownCards(self) - #h.totalPot() - #h.rake = h.totalpot - h.totalcollected def addPlayer(self, seat, name, chips): @@ -980,7 +1007,7 @@ class HoldemOmahaHand(Hand): log.debug(self.actions['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 in chips) " %(player[0], player[1], player[2])) + print >>fh, ("Seat %s: %s ($%.2f in chips) " %(player[0], player[1], float(player[2]))) if self.actions['BLINDSANTES']: for act in self.actions['BLINDSANTES']: diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py index 378c7288..9429b910 100644 --- a/pyfpdb/SQL.py +++ b/pyfpdb/SQL.py @@ -2246,7 +2246,7 @@ class Sql: (sum(cast(hp.street1Calls as integer))+ sum(cast(hp.street2Calls as integer))+ sum(cast(hp.street3Calls as integer))+ sum(cast(hp.street4Calls as integer))) + (sum(cast(hp.street1Aggr as integer)) + sum(cast(hp.street2Aggr as integer)) + sum(cast(hp.street3Aggr as integer)) + sum(cast(hp.street4Aggr as integer))) ) AS aggfrq - ,100.0*(sum(cast(hp.street1CBDone as integer)) + sum(cast(hp.street2CBDone as integer)) + sum(cast(hp.street2CBDone as integer)) + sum(cast(hp.street4CBDone as integer))) + ,100.0*(sum(cast(hp.street1CBDone as integer)) + sum(cast(hp.street2CBDone as integer)) + sum(cast(hp.street3CBDone as integer)) + sum(cast(hp.street4CBDone as integer))) / (sum(cast(hp.street1CBChance as integer))+ sum(cast(hp.street2CBChance as integer))+ sum(cast(hp.street3CBChance as integer))+ sum(cast(hp.street4CBChance as integer))) AS conbet ,sum(hp.totalProfit)/100.0 AS net @@ -2367,7 +2367,7 @@ class Sql: (sum(cast(hp.street1Calls as integer))+ sum(cast(hp.street2Calls as integer))+ sum(cast(hp.street3Calls as integer))+ sum(cast(hp.street4Calls as integer))) + (sum(cast(hp.street1Aggr as integer)) + sum(cast(hp.street2Aggr as integer)) + sum(cast(hp.street3Aggr as integer)) + sum(cast(hp.street4Aggr as integer))) ) AS aggfrq - ,100.0*(sum(cast(hp.street1CBDone as integer)) + sum(cast(hp.street2CBDone as integer)) + sum(cast(hp.street2CBDone as integer)) + sum(cast(hp.street4CBDone as integer))) + ,100.0*(sum(cast(hp.street1CBDone as integer)) + sum(cast(hp.street2CBDone as integer)) + sum(cast(hp.street3CBDone as integer)) + sum(cast(hp.street4CBDone as integer))) / (sum(cast(hp.street1CBChance as integer))+ sum(cast(hp.street2CBChance as integer))+ sum(cast(hp.street3CBChance as integer))+ sum(cast(hp.street4CBChance as integer))) AS conbet ,sum(hp.totalProfit)/100.0 AS net @@ -2489,7 +2489,7 @@ class Sql: (sum(cast(hp.street1Calls as integer))+ sum(cast(hp.street2Calls as integer))+ sum(cast(hp.street3Calls as integer))+ sum(cast(hp.street4Calls as integer))) + (sum(cast(hp.street1Aggr as integer)) + sum(cast(hp.street2Aggr as integer)) + sum(cast(hp.street3Aggr as integer)) + sum(cast(hp.street4Aggr as integer))) ) AS aggfrq - ,100.0*(sum(cast(hp.street1CBDone as integer)) + sum(cast(hp.street2CBDone as integer)) + sum(cast(hp.street2CBDone as integer)) + sum(cast(hp.street4CBDone as integer))) + ,100.0*(sum(cast(hp.street1CBDone as integer)) + sum(cast(hp.street2CBDone as integer)) + sum(cast(hp.street3CBDone as integer)) + sum(cast(hp.street4CBDone as integer))) / (sum(cast(hp.street1CBChance as integer))+ sum(cast(hp.street2CBChance as integer))+ sum(cast(hp.street3CBChance as integer))+ sum(cast(hp.street4CBChance as integer))) AS conbet ,sum(hp.totalProfit)/100.0 AS net diff --git a/pyfpdb/fpdb_import.py b/pyfpdb/fpdb_import.py index 15043503..ba1251ac 100644 --- a/pyfpdb/fpdb_import.py +++ b/pyfpdb/fpdb_import.py @@ -624,4 +624,4 @@ class ProgressBar: if __name__ == "__main__": - print _("CLI for fpdb_import is now available as CliFpdb.py") + print _("CLI for importing hands is GuiBulkImport.py")