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

This commit is contained in:
sqlcoder 2009-06-22 21:43:59 +01:00
commit 45c78b8a5f
19 changed files with 621 additions and 795 deletions

View File

@ -57,6 +57,21 @@ def fourStartCards(value1, suit1, value2, suit2, value3, suit3, value4, suit4):
#AAKKs #AAKKs
#AAKKr #AAKKr
# Is probably what we are looking for # Is probably what we are looking for
# mct:
# my maths says there are 4 classes of suitedness
# SSSS SSSx SSxy SSHH
# encode them as follows:
# SSSS (K, J, 6, 3)
# - 13C4 = 715 possibilities
# SSSx (K, J, 6),(3)
# - 13C3 * 13 = 3718 possibilities
# SSxy (K, J),(6),(3)
# - 13C2 * 13*13 = 13182 possibilities
# SSHH (K, J),(6, 3)
# - 13C2 * 13C2 = 6084 possibilities
# Needless to say they won't fit on a 13x13 grid.
# The actual number of hands in each class is far greater
return(0) return(0)
def cardFromValueSuit(value, suit): def cardFromValueSuit(value, suit):
@ -70,7 +85,7 @@ def cardFromValueSuit(value, suit):
def valueSuitFromCard(card): def valueSuitFromCard(card):
""" Function to convert a card stored in the database (int 0-52) into value """ Function to convert a card stored in the database (int 0-52) into value
and suit like 9s, 4c etc """ and suit like 9s, 4c etc """
if card < 0 or card > 52: if card < 0 or card > 52 or not card:
return('') return('')
else: else:
return( ['', '2h', '3h', '4h', '5h', '6h', '7h', '8h', '9h', 'Th', 'Jh', 'Qh', 'Kh', 'Ah' return( ['', '2h', '3h', '4h', '5h', '6h', '7h', '8h', '9h', 'Th', 'Jh', 'Qh', 'Kh', 'Ah'

View File

@ -250,6 +250,7 @@ class Config:
self.default_config_path = self.get_default_config_path() self.default_config_path = self.get_default_config_path()
if file != None: # configuration file path has been passed if file != None: # configuration file path has been passed
file = os.path.expanduser(file)
if not os.path.exists(file): if not os.path.exists(file):
print "Configuration file %s not found. Using defaults." % (file) print "Configuration file %s not found. Using defaults." % (file)
sys.stderr.write("Configuration file %s not found. Using defaults." % (file)) sys.stderr.write("Configuration file %s not found. Using defaults." % (file))

149
pyfpdb/Database.py Normal file → Executable file
View File

@ -33,6 +33,7 @@ import string
# FreePokerTools modules # FreePokerTools modules
import fpdb_db import fpdb_db
import fpdb_simple
import Configuration import Configuration
import SQL import SQL
import Card import Card
@ -44,8 +45,10 @@ class Database:
self.connection = self.fdb.db self.connection = self.fdb.db
db_params = c.get_db_parameters() db_params = c.get_db_parameters()
self.import_options = c.get_import_parameters()
self.type = db_params['db-type'] self.type = db_params['db-type']
self.sql = SQL.Sql(game = game, type = self.type, db_server = db_params['db-backend']) self.backend = db_params['db-backend']
self.sql = SQL.Sql(game = game, type = self.type, db_server = db_params['db-server'])
self.connection.rollback() self.connection.rollback()
# To add to config: # To add to config:
@ -76,6 +79,11 @@ class Database:
self.hand_nhands_ago = 0 # todo self.hand_nhands_ago = 0 # todo
#cur.execute(self.sql.query['get_table_name'], (hand_id, )) #cur.execute(self.sql.query['get_table_name'], (hand_id, ))
#row = cur.fetchone() #row = cur.fetchone()
self.saveActions = False if self.import_options['saveActions'] == False else True
def commit(self):
self.fdb.db.commit()
def close_connection(self): def close_connection(self):
self.connection.close() self.connection.close()
@ -116,21 +124,13 @@ class Database:
def get_cards(self, hand): def get_cards(self, hand):
"""Get and return the cards for each player in the hand.""" """Get and return the cards for each player in the hand."""
cards = {} # dict of cards, the key is the seat number example: {1: 'AcQd9hTs5d'} cards = {} # dict of cards, the key is the seat number,
# the value is a tuple of the players cards
# example: {1: (0, 0, 20, 21, 22, 0 , 0)}
c = self.connection.cursor() c = self.connection.cursor()
c.execute(self.sql.query['get_cards'], [hand]) c.execute(self.sql.query['get_cards'], [hand])
colnames = [desc[0] for desc in c.description]
cardnames = ['card1', 'card2', 'card3', 'card4', 'card5', 'card6', 'card7']
for row in c.fetchall(): for row in c.fetchall():
cs = ['', '', '', '', '', '', ''] cards[row[0]] = row[1:]
seat = -1
for col,name in enumerate(colnames):
if name in cardnames:
cs[cardnames.index(name)] = Card.valueSuitFromCard(row[col])
elif name == 'seat_number':
seat = row[col]
if seat != -1:
cards[seat] = ''.join(cs)
return cards return cards
def get_common_cards(self, hand): def get_common_cards(self, hand):
@ -272,6 +272,129 @@ class Database:
else: else:
return None return None
def get_last_insert_id(self):
return self.fdb.getLastInsertId()
#stores a stud/razz hand into the database
def ring_stud(self, config, settings, db, cursor, base, category, site_hand_no, gametype_id, hand_start_time
,names, player_ids, start_cashes, antes, card_values, card_suits, winnings, rakes
,action_types, allIns, action_amounts, actionNos, hudImportData, maxSeats, tableName
,seatNos):
fpdb_simple.fillCardArrays(len(names), base, category, card_values, card_suits)
hands_id = fpdb_simple.storeHands(self.backend, db, cursor, site_hand_no, gametype_id
,hand_start_time, names, tableName, maxSeats, hudImportData)
#print "before calling store_hands_players_stud, antes:", antes
hands_players_ids = fpdb_simple.store_hands_players_stud(self.backend, db, cursor, hands_id, player_ids
,start_cashes, antes, card_values
,card_suits, winnings, rakes, seatNos)
if 'updateHudCache' not in settings or settings['updateHudCache'] != 'drop':
fpdb_simple.storeHudCache(self.backend, cursor, base, category, gametype_id, hand_start_time, player_ids, hudImportData)
if self.saveActions:
fpdb_simple.storeActions(cursor, hands_players_ids, action_types
,allIns, action_amounts, actionNos)
return hands_id
#end def ring_stud
def ring_holdem_omaha(self, config, settings, db, cursor, base, category, site_hand_no, gametype_id
,hand_start_time, names, player_ids, start_cashes, positions, card_values
,card_suits, board_values, board_suits, winnings, rakes, action_types, allIns
,action_amounts, actionNos, hudImportData, maxSeats, tableName, seatNos):
"""stores a holdem/omaha hand into the database"""
t0 = time()
fpdb_simple.fillCardArrays(len(names), base, category, card_values, card_suits)
t1 = time()
fpdb_simple.fill_board_cards(board_values, board_suits)
t2 = time()
hands_id = fpdb_simple.storeHands(self.backend, db, cursor, site_hand_no, gametype_id
,hand_start_time, names, tableName, maxSeats,
hudImportData, board_values, board_suits)
#TEMPORARY CALL! - Just until all functions are migrated
t3 = time()
hands_players_ids = fpdb_simple.store_hands_players_holdem_omaha(
self.backend, db, cursor, category, hands_id, player_ids, start_cashes
, positions, card_values, card_suits, winnings, rakes, seatNos, hudImportData)
t4 = time()
#print "ring holdem, backend=%d" % backend
if 'updateHudCache' not in settings or settings['updateHudCache'] != 'drop':
fpdb_simple.storeHudCache(self.backend, cursor, base, category, gametype_id, hand_start_time, player_ids, hudImportData)
t5 = time()
fpdb_simple.store_board_cards(cursor, hands_id, board_values, board_suits)
t6 = time()
if self.saveActions:
fpdb_simple.storeActions(cursor, hands_players_ids, action_types, allIns, action_amounts, actionNos)
t7 = time()
#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
def tourney_holdem_omaha(self, config, settings, db, cursor, base, category, siteTourneyNo, buyin, fee, knockout
,entries, prizepool, tourney_start, payin_amounts, ranks, tourneyTypeId
,siteId #end of tourney specific params
,site_hand_no, gametype_id, hand_start_time, names, player_ids
,start_cashes, positions, card_values, card_suits, board_values
,board_suits, winnings, rakes, action_types, allIns, action_amounts
,actionNos, hudImportData, maxSeats, tableName, seatNos):
"""stores a tourney holdem/omaha hand into the database"""
fpdb_simple.fillCardArrays(len(names), base, category, card_values, card_suits)
fpdb_simple.fill_board_cards(board_values, board_suits)
tourney_id = fpdb_simple.store_tourneys(cursor, tourneyTypeId, siteTourneyNo, entries, prizepool, tourney_start)
tourneys_players_ids = fpdb_simple.store_tourneys_players(cursor, tourney_id, player_ids, payin_amounts, ranks, winnings)
hands_id = fpdb_simple.storeHands(self.backend, db, cursor, site_hand_no, gametype_id
,hand_start_time, names, tableName, maxSeats)
hands_players_ids = fpdb_simple.store_hands_players_holdem_omaha_tourney(
self.backend, db, cursor, category, hands_id, player_ids, start_cashes, positions
, card_values, card_suits, winnings, rakes, seatNos, tourneys_players_ids)
#print "tourney holdem, backend=%d" % backend
if 'updateHudCache' not in settings or settings['updateHudCache'] != 'drop':
fpdb_simple.storeHudCache(self.backend, cursor, base, category, gametype_id, hand_start_time, player_ids, hudImportData)
fpdb_simple.store_board_cards(cursor, hands_id, board_values, board_suits)
if self.saveActions:
fpdb_simple.storeActions(cursor, hands_players_ids, action_types, allIns, action_amounts, actionNos)
return hands_id
#end def tourney_holdem_omaha
def tourney_stud(self, config, settings, db, cursor, base, category, siteTourneyNo, buyin, fee, knockout, entries
,prizepool, tourneyStartTime, payin_amounts, ranks, tourneyTypeId, siteId
,siteHandNo, gametypeId, handStartTime, names, playerIds, startCashes, antes
,cardValues, cardSuits, winnings, rakes, actionTypes, allIns, actionAmounts
,actionNos, hudImportData, maxSeats, tableName, seatNos):
#stores a tourney stud/razz hand into the database
fpdb_simple.fillCardArrays(len(names), base, category, cardValues, cardSuits)
tourney_id = fpdb_simple.store_tourneys(cursor, tourneyTypeId, siteTourneyNo, entries, prizepool, tourneyStartTime)
tourneys_players_ids = fpdb_simple.store_tourneys_players(cursor, tourney_id, playerIds, payin_amounts, ranks, winnings)
hands_id = fpdb_simple.storeHands(self.backend, db, cursor, siteHandNo, gametypeId, handStartTime, names, tableName, maxSeats)
hands_players_ids = fpdb_simple.store_hands_players_stud_tourney(self.backend, db, cursor, hands_id
, playerIds, startCashes, antes, cardValues, cardSuits
, winnings, rakes, seatNos, tourneys_players_ids)
if 'updateHudCache' not in settings or settings['updateHudCache'] != 'drop':
fpdb_simple.storeHudCache(self.backend, cursor, base, category, gametypeId, hand_start_time, playerIds, hudImportData)
if self.saveActions:
fpdb_simple.storeActions(cursor, hands_players_ids, actionTypes, allIns, actionAmounts, actionNos)
return hands_id
#end def tourney_stud
if __name__=="__main__": if __name__=="__main__":
c = Configuration.Config() c = Configuration.Config()

View File

@ -1,161 +0,0 @@
# pokerstars_cash.py
# -*- coding: iso-8859-15
#
# PokerStats, an online poker statistics tracking software for Linux
# Copyright (C) 2007-2008 Mika Boström <bostik@iki.fi>
#
# 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, version 3 of the License.
#
# Modified for use in fpdb by Carl Gherardi
import re
# These are PokerStars specific;
# More importantly, they are currently valid for cash game only.
#####
# XXX: There was a weird problem with saved hand histories in PokerStars
# client 2.491; if a user was present on the table (and thus anywhere in
# the hand history), with non-standard characters in their username, the
# client would prepend a literal Ctrl-P (ASCII 16, 0x10) character to
# the hand history title line. Hence, to allow these strangely saved
# hands to be parsed and imported, there is a conditional "one extra
# character" allowed at the start of the new hand regex.
class FpdbRegex:
def __init__(self):
self.__GAME_INFO_REGEX=''
self.__SPLIT_HAND_REGEX='\n\n\n'
self.__NEW_HAND_REGEX='^.?PokerStars Game #\d+:\s+Hold\'em'
self.__HAND_INFO_REGEX='^.*#(\d+):\s+(\S+)\s([\s\S]+)\s\(\$?([.0-9]+)/\$?([.0-9]+)\)\s-\s(\S+)\s-?\s?(\S+)\s\(?(\w+)\)?'
self.__TABLE_INFO_REGEX='^\S+\s+\'.*\'\s+(\d+)-max\s+Seat\s#(\d+)'
self.__PLAYER_INFO_REGEX='^Seat\s(\d+):\s(.*)\s\(\$?([.\d]+)\s'
self.__POST_SB_REGEX='^(.*):\sposts small blind'
self.__POST_BB_REGEX='^(.*):\sposts big blind'
self.__POST_BOTH_REGEX='^(.*):\sposts small & big blinds'
self.__HAND_STAGE_REGEX='^\*{3}\s(.*)\s\*{3}'
self.__HOLE_CARD_REGEX='^\*{3}\sHOLE CARDS'
self.__FLOP_CARD_REGEX='^\*{3}\sFLOP\s\*{3}\s\[(\S{2})\s(\S{2})\s(\S{2})\]'
self.__TURN_CARD_REGEX='^\*{3}\sTURN\s\*{3}\s\[\S{2}\s\S{2}\s\S{2}\]\s\[(\S{2})\]'
self.__RIVER_CARD_REGEX='^\*{3}\sRIVER\s\*{3}\s\[\S{2}\s\S{2}\s\S{2}\s\S{2}\]\s\[(\S{2})\]'
self.__SHOWDOWN_REGEX='^\*{3}\sSHOW DOWN'
self.__SUMMARY_REGEX='^\*{3}\sSUMMARY'
self.__UNCALLED_BET_REGEX='^Uncalled bet \(\$([.\d]+)\) returned to (.*)'
self.__POT_AND_RAKE_REGEX='^Total\spot\s\$([.\d]+).*\|\sRake\s\$([.\d]+)'
self.__COLLECT_POT_REGEX='^(.*)\scollected\s\$([.\d]+)\sfrom\s((main|side)\s)?pot'
self.__HERO_CARDS_REGEX='^Dealt\sto\s(.*)\s\[(\S{2})\s(\S{2})\]'
self.__SHOWN_CARDS_REGEX='^(.*):\sshows\s\[(\S{2})\s(\S{2})\]'
self.__ACTION_STEP_REGEX='^(.*):\s(bets|checks|raises|calls|folds)((\s\$([.\d]+))?(\sto\s\$([.\d]+))?)?'
self.__SHOWDOWN_ACTION_REGEX='^(.*):\s(shows|mucks)'
self.__SUMMARY_CARDS_REGEX='^Seat\s\d+:\s(.*)\s(showed|mucked)\s\[(\S{2})\s(\S{2})\]'
self.__SUMMARY_CARDS_EXTRA_REGEX='^Seat\s\d+:\s(.*)\s(\(.*\)\s)(showed|mucked)\s\[(\S{2})\s(\S{2})\]'
def compileRegexes(self):
### Compile the regexes
self.game_info_re = re.compile(self.__GAME_INFO_REGEX)
self.split_hand_re = re.compile(self.__SPLIT_HAND_REGEX)
self.hand_start_re = re.compile(self.__NEW_HAND_REGEX)
self.hand_info_re = re.compile(self.__HAND_INFO_REGEX)
self.table_info_re = re.compile(self.__TABLE_INFO_REGEX)
self.player_info_re = re.compile(self.__PLAYER_INFO_REGEX)
self.small_blind_re = re.compile(self.__POST_SB_REGEX)
self.big_blind_re = re.compile(self.__POST_BB_REGEX)
self.both_blinds_re = re.compile(self.__POST_BOTH_REGEX)
self.hand_stage_re = re.compile(self.__HAND_STAGE_REGEX)
self.hole_cards_re = re.compile(self.__HOLE_CARD_REGEX)
self.flop_cards_re = re.compile(self.__FLOP_CARD_REGEX)
self.turn_card_re = re.compile(self.__TURN_CARD_REGEX)
self.river_card_re = re.compile(self.__RIVER_CARD_REGEX)
self.showdown_re = re.compile(self.__SHOWDOWN_REGEX)
self.summary_re = re.compile(self.__SUMMARY_REGEX)
self.uncalled_bet_re = re.compile(self.__UNCALLED_BET_REGEX)
self.collect_pot_re = re.compile(self.__COLLECT_POT_REGEX)
self.hero_cards_re = re.compile(self.__HERO_CARDS_REGEX)
self.cards_shown_re = re.compile(self.__SHOWN_CARDS_REGEX)
self.summary_cards_re = re.compile(self.__SUMMARY_CARDS_REGEX)
self.summary_cards_extra_re = re.compile(self.__SUMMARY_CARDS_EXTRA_REGEX)
self.action_re = re.compile(self.__ACTION_STEP_REGEX)
self.rake_re = re.compile(self.__POT_AND_RAKE_REGEX)
self.showdown_action_re = re.compile(self.__SHOWDOWN_ACTION_REGEX)
# Set methods for plugins to override
def setGameInfoRegex(self, string):
self.__GAME_INFO_REGEX = string
def setSplitHandRegex(self, string):
self.__SPLIT_HAND_REGEX = string
def setNewHandRegex(self, string):
self.__NEW_HAND_REGEX = string
def setHandInfoRegex(self, string):
self.__HAND_INFO_REGEX = string
def setTableInfoRegex(self, string):
self.__TABLE_INFO_REGEX = string
def setPlayerInfoRegex(self, string):
self.__PLAYER_INFO_REGEX = string
def setPostSbRegex(self, string):
self.__POST_SB_REGEX = string
def setPostBbRegex(self, string):
self.__POST_BB_REGEX = string
def setPostBothRegex(self, string):
self.__POST_BOTH_REGEX = string
def setHandStageRegex(self, string):
self.__HAND_STAGE_REGEX = string
def setHoleCardRegex(self, string):
self.__HOLE_CARD_REGEX = string
def setFlopCardRegex(self, string):
self.__FLOP_CARD_REGEX = string
def setTurnCardRegex(self, string):
self.__TURN_CARD_REGEX = string
def setRiverCardRegex(self, string):
self.__RIVER_CARD_REGEX = string
def setShowdownRegex(self, string):
self.__SHOWDOWN_REGEX = string
def setSummaryRegex(self, string):
self.__SUMMARY_REGEX = string
def setUncalledBetRegex(self, string):
self.__UNCALLED_BET_REGEX = string
def setCollectPotRegex(self, string):
self.__COLLECT_POT_REGEX = string
def setHeroCardsRegex(self, string):
self.__HERO_CARDS_REGEX = string
def setShownCardsRegex(self, string):
self.__SHOWN_CARDS_REGEX = string
def setSummaryCardsRegex(self, string):
self.__SUMMARY_CARDS_REGEX = string
def setSummaryCardsExtraRegex(self, string):
self.__SUMMARY_CARDS_EXTRA_REGEX = string
def setActionStepRegex(self, string):
self.__ACTION_STEP_REGEX = string
def setPotAndRakeRegex(self, string):
self.__POT_AND_RAKE_REGEX = string
def setShowdownActionRegex(self, string):
self.__SHOWDOWN_ACTION_REGEX = string

View File

@ -28,6 +28,7 @@ import time
import fpdb_import import fpdb_import
from optparse import OptionParser from optparse import OptionParser
import Configuration import Configuration
import string
class GuiAutoImport (threading.Thread): class GuiAutoImport (threading.Thread):
def __init__(self, settings, config): def __init__(self, settings, config):
@ -157,21 +158,15 @@ class GuiAutoImport (threading.Thread):
widget.set_label(u' _Stop Autoimport ') widget.set_label(u' _Stop Autoimport ')
if self.pipe_to_hud is None: if self.pipe_to_hud is None:
if os.name == 'nt': if os.name == 'nt':
command = "python HUD_main.py" + " %s" % (self.database) command = "python HUD_main.py" + " " + self.settings['cl_options']
bs = 0 # windows is not happy with line buffing here bs = 0 # windows is not happy with line buffing here
self.pipe_to_hud = subprocess.Popen(command, bufsize = bs, stdin = subprocess.PIPE, self.pipe_to_hud = subprocess.Popen(command, bufsize = bs, stdin = subprocess.PIPE,
universal_newlines=True) universal_newlines=True)
else: else:
command = os.path.join(sys.path[0], 'HUD_main.py') command = os.path.join(sys.path[0], 'HUD_main.py')
#command = self.config.execution_path('HUD_main.py') # Hi Ray. Sorry about this, kludging. cl = [command, ] + string.split(self.settings['cl_options'])
bs = 1 self.pipe_to_hud = subprocess.Popen(cl, bufsize = 1, stdin = subprocess.PIPE,
self.pipe_to_hud = subprocess.Popen((command, self.database), bufsize = bs, stdin = subprocess.PIPE,
universal_newlines=True) 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. # Add directories to importer object.
for site in self.input_settings: for site in self.input_settings:

View File

@ -29,12 +29,16 @@ Main for FreePokerTools HUD.
# Standard Library modules # Standard Library modules
import sys import sys
# redirect the stderr
errorfile = open('HUD-error.txt', 'w', 0)
sys.stderr = errorfile
import os import os
import Options
(options, sys.argv) = Options.fpdb_options()
if not options.errorsToConsole:
print "Note: error output is being diverted to fpdb-error-log.txt and HUD-error.txt. Any major error will be reported there _only_."
errorFile = open('fpdb-error-log.txt', 'w', 0)
sys.stderr = errorFile
import thread import thread
import time import time
import string import string
@ -59,7 +63,7 @@ class HUD_main(object):
def __init__(self, db_name = 'fpdb'): def __init__(self, db_name = 'fpdb'):
self.db_name = db_name self.db_name = db_name
self.config = Configuration.Config() self.config = Configuration.Config(file=options.config, dbname=options.dbname)
self.hud_dict = {} self.hud_dict = {}
# a thread to read stdin # a thread to read stdin
@ -198,17 +202,12 @@ class HUD_main(object):
self.db_connection.connection.rollback() self.db_connection.connection.rollback()
if __name__== "__main__": if __name__== "__main__":
sys.stderr.write("HUD_main starting\n")
# database name can be passed on command line sys.stderr.write("HUD_main starting\n")
try: sys.stderr.write("Using db name = %s\n" % (options.dbname))
db_name = sys.argv[1]
except:
db_name = 'fpdb'
sys.stderr.write("Using db name = %s\n" % (db_name))
# start the HUD_main object # start the HUD_main object
hm = HUD_main(db_name = db_name) hm = HUD_main(db_name = options.dbname)
# start the event loop # start the event loop
gtk.main() gtk.main()

View File

@ -39,15 +39,14 @@ class Hand:
self.starttime = 0 self.starttime = 0
self.handText = handText self.handText = handText
self.handid = 0 self.handid = 0
self.tablename = "Slartibartfast" self.tablename = ""
self.hero = "Hiro" self.hero = ""
self.maxseats = 10 self.maxseats = 10
self.counted_seats = 0 self.counted_seats = 0
self.buttonpos = 0 self.buttonpos = 0
self.seating = [] self.seating = []
self.players = [] self.players = []
self.posted = [] self.posted = []
self.involved = True
# Collections indexed by street names # Collections indexed by street names
self.bets = {} self.bets = {}
@ -55,23 +54,26 @@ class Hand:
self.streets = {} self.streets = {}
self.actions = {} # [['mct','bets','$10'],['mika','folds'],['carlg','raises','$20']] self.actions = {} # [['mct','bets','$10'],['mika','folds'],['carlg','raises','$20']]
self.board = {} # dict from street names to community cards self.board = {} # dict from street names to community cards
for street in self.streetList: self.holecards = {}
for street in self.allStreets:
self.streets[street] = "" # portions of the handText, filled by markStreets() self.streets[street] = "" # portions of the handText, filled by markStreets()
self.bets[street] = {} self.bets[street] = {}
self.lastBet[street] = 0 self.lastBet[street] = 0
self.actions[street] = [] self.actions[street] = []
self.board[street] = [] self.board[street] = []
self.holecards[street] = {} # dict from player names to holecards
# Collections indexed by player names # Collections indexed by player names
self.holecards = {} # dict from player names to dicts by street ... of tuples ... of holecards # self.holecards = {} # dict from player names to dicts by street ... of tuples ... of holecards
self.discards = {} # dict from player names to dicts by street ... of tuples ... of discarded holecards self.discards = {} # dict from player names to dicts by street ... of tuples ... of discarded holecards
self.stacks = {} self.stacks = {}
self.collected = [] #list of ? self.collected = [] #list of ?
self.collectees = {} # dict from player names to amounts collected (?) self.collectees = {} # dict from player names to amounts collected (?)
# Sets of players # Sets of players
self.shown = set()
self.folded = set() self.folded = set()
self.dealt = set() # 'dealt to' line to be printed
self.shown = set() # cards were shown
# self.action = [] # self.action = []
# Things to do with money # Things to do with money
@ -80,6 +82,10 @@ class Hand:
self.totalcollected = None self.totalcollected = None
self.rake = None self.rake = None
def __str__(self):
str = ''
str = str + "Hand Object for %s at %s" % (self.handid, self.sitename)
return str
def insert(self, db): def insert(self, db):
""" Function to insert Hand into database """ Function to insert Hand into database
@ -90,7 +96,7 @@ db: a connected fpdb_db object"""
sqlids = db.getSqlPlayerIDs([p[1] for p in self.players], self.siteId) sqlids = db.getSqlPlayerIDs([p[1] for p in self.players], self.siteId)
# HudCache data to come from DerivedStats class # HudCache data to come from DerivedStats class
# HandsActions - all actions for all players for all streets - self.actions # HandsActions - all actions for all players for all streets - self.actions
# BoardCards - Skip - no longer necessary? # BoardCards - Skip - no longer necessary
# Hands - Summary information of hand indexed by handId - gameinfo # Hands - Summary information of hand indexed by handId - gameinfo
#hh['siteHandNo'] = self.handid #hh['siteHandNo'] = self.handid
# gametypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id), # gametypeId SMALLINT UNSIGNED NOT NULL, FOREIGN KEY (gametypeId) REFERENCES Gametypes(id),
@ -105,6 +111,14 @@ db: a connected fpdb_db object"""
# boardcard3 smallint, # boardcard3 smallint,
# boardcard4 smallint, # boardcard4 smallint,
# boardcard5 smallint, # boardcard5 smallint,
# Flop turn and river may all be empty - add (likely) too many elements and trim with range
# boardcards = board['FLOP'] + board['TURN'] + board['RIVER'] + [u'0x', u'0x', u'0x', u'0x', u'0x']
# cards = [Card.cardFromValueSuit(v,s) for v,s in boardcards[0:4]]
# hh['boardcard1'] = cards[0]
# hh['boardcard2'] = cards[1]
# hh['boardcard3'] = cards[2]
# hh['boardcard4'] = cards[3]
# hh['boardcard5'] = cards[4]
# texture smallint, # texture smallint,
# playersVpi SMALLINT NOT NULL, /* num of players vpi */ # playersVpi SMALLINT NOT NULL, /* num of players vpi */
# Needs to be recorded # Needs to be recorded
@ -163,12 +177,10 @@ If a player has None chips he won't be added."""
if chips is not None: if chips is not None:
self.players.append([seat, name, chips]) self.players.append([seat, name, chips])
self.stacks[name] = Decimal(chips) self.stacks[name] = Decimal(chips)
self.holecards[name] = []
self.discards[name] = []
self.pot.addPlayer(name) self.pot.addPlayer(name)
for street in self.streetList: for street in self.allStreets:
self.bets[street][name] = [] self.bets[street][name] = []
self.holecards[name] = {} # dict from street names. #self.holecards[name] = {} # dict from street names.
self.discards[name] = {} # dict from street names. self.discards[name] = {} # dict from street names.
@ -190,6 +202,7 @@ If a player has None chips he won't be added."""
def setCommunityCards(self, street, cards): def setCommunityCards(self, street, cards):
logging.debug("setCommunityCards %s %s" %(street, cards)) logging.debug("setCommunityCards %s %s" %(street, cards))
self.board[street] = [self.card(c) for c in cards] self.board[street] = [self.card(c) for c in cards]
print "DEBUG: self.board: %s" % self.board
def card(self,c): def card(self,c):
"""upper case the ranks but not suits, 'atjqk' => 'ATJQK'""" """upper case the ranks but not suits, 'atjqk' => 'ATJQK'"""
@ -382,7 +395,7 @@ Map the tuple self.gametype onto the pokerstars string describing it
"omahahilo" : "Omaha Hi/Lo", "omahahilo" : "Omaha Hi/Lo",
"razz" : "Razz", "razz" : "Razz",
"studhi" : "7 Card Stud", "studhi" : "7 Card Stud",
"studhilo" : "FIXME", "studhilo" : "7 Card Stud Hi/Lo",
"fivedraw" : "5 Card Draw", "fivedraw" : "5 Card Draw",
"27_1draw" : "FIXME", "27_1draw" : "FIXME",
"27_3draw" : "Triple Draw 2-7 Lowball", "27_3draw" : "Triple Draw 2-7 Lowball",
@ -407,32 +420,32 @@ Map the tuple self.gametype onto the pokerstars string describing it
def printHand(self): def printHand(self):
self.writeHand(sys.stdout) self.writeHand(sys.stdout)
def printActionLine(self, act, fh): def actionString(self, act):
if act[1] == 'folds': if act[1] == 'folds':
print >>fh, ("%s: folds " %(act[0])) return ("%s: folds " %(act[0]))
elif act[1] == 'checks': elif act[1] == 'checks':
print >>fh, ("%s: checks " %(act[0])) return ("%s: checks " %(act[0]))
elif act[1] == 'calls': elif act[1] == 'calls':
print >>fh, ("%s: calls $%s%s" %(act[0], act[2], ' and is all-in' if act[3] else '')) return ("%s: calls $%s%s" %(act[0], act[2], ' and is all-in' if act[3] else ''))
elif act[1] == 'bets': elif act[1] == 'bets':
print >>fh, ("%s: bets $%s%s" %(act[0], act[2], ' and is all-in' if act[3] else '')) return ("%s: bets $%s%s" %(act[0], act[2], ' and is all-in' if act[3] else ''))
elif act[1] == 'raises': elif act[1] == 'raises':
print >>fh, ("%s: raises $%s to $%s%s" %(act[0], act[2], act[3], ' and is all-in' if act[5] else '')) return ("%s: raises $%s to $%s%s" %(act[0], act[2], act[3], ' and is all-in' if act[5] else ''))
elif act[1] == 'completea': elif act[1] == 'completea':
print >>fh, ("%s: completes to $%s%s" %(act[0], act[2], ' and is all-in' if act[3] else '')) return ("%s: completes to $%s%s" %(act[0], act[2], ' and is all-in' if act[3] else ''))
elif act[1] == 'posts': elif act[1] == 'posts':
if(act[2] == "small blind"): if(act[2] == "small blind"):
print >>fh, ("%s: posts small blind $%s%s" %(act[0], act[3], ' and is all-in' if act[4] else '')) return ("%s: posts small blind $%s%s" %(act[0], act[3], ' and is all-in' if act[4] else ''))
elif(act[2] == "big blind"): elif(act[2] == "big blind"):
print >>fh, ("%s: posts big blind $%s%s" %(act[0], act[3], ' and is all-in' if act[4] else '')) return ("%s: posts big blind $%s%s" %(act[0], act[3], ' and is all-in' if act[4] else ''))
elif(act[2] == "both"): elif(act[2] == "both"):
print >>fh, ("%s: posts small & big blinds $%s%s" %(act[0], act[3], ' and is all-in' if act[4] else '')) return ("%s: posts small & big blinds $%s%s" %(act[0], act[3], ' and is all-in' if act[4] else ''))
elif act[1] == 'bringin': elif act[1] == 'bringin':
print >>fh, ("%s: brings in for $%s%s" %(act[0], act[2], ' and is all-in' if act[3] else '')) return ("%s: brings in for $%s%s" %(act[0], act[2], ' and is all-in' if act[3] else ''))
elif act[1] == 'discards': elif act[1] == 'discards':
print >>fh, ("%s: discards %s %s%s" %(act[0], act[2], 'card' if act[2] == 1 else 'cards' , " [" + " ".join(self.discards[act[0]]['DRAWONE']) + "]" if self.hero == act[0] else '')) return ("%s: discards %s %s%s" %(act[0], act[2], 'card' if act[2] == 1 else 'cards' , " [" + " ".join(self.discards[act[0]]['DRAWONE']) + "]" if self.hero == act[0] else ''))
elif act[1] == 'stands pat': elif act[1] == 'stands pat':
print >>fh, ("%s: stands pat" %(act[0])) return ("%s: stands pat" %(act[0]))
class HoldemOmahaHand(Hand): class HoldemOmahaHand(Hand):
@ -440,9 +453,10 @@ class HoldemOmahaHand(Hand):
if gametype['base'] != 'hold': if gametype['base'] != 'hold':
pass # or indeed don't pass and complain instead pass # or indeed don't pass and complain instead
logging.debug("HoldemOmahaHand") logging.debug("HoldemOmahaHand")
self.streetList = ['BLINDSANTES', 'DEAL', 'PREFLOP','FLOP','TURN','RIVER'] # a list of the observed street names in order self.allStreets = ['BLINDSANTES', 'PREFLOP','FLOP','TURN','RIVER']
self.holeStreets = ['PREFLOP']
self.communityStreets = ['FLOP', 'TURN', 'RIVER'] self.communityStreets = ['FLOP', 'TURN', 'RIVER']
self.actionStreets = ['PREFLOP','FLOP','TURN','RIVER'] self.actionStreets = ['BLINDSANTES','PREFLOP','FLOP','TURN','RIVER']
Hand.__init__(self, sitename, gametype, handText, builtFrom = "HHC") Hand.__init__(self, sitename, gametype, handText, builtFrom = "HHC")
self.sb = gametype['sb'] self.sb = gametype['sb']
self.bb = gametype['bb'] self.bb = gametype['bb']
@ -480,24 +494,32 @@ class HoldemOmahaHand(Hand):
pass pass
def addHoleCards(self, cards, player, shown=False): def addHoleCards(self, cards, player, shown=False, dealt=False):
"""\ """\
Assigns observed holecards to a player. Assigns observed holecards to a player.
cards list of card bigrams e.g. ['2h','Jc'] cards list of card bigrams e.g. ['2h','Jc']
player (string) name of player player (string) name of player
shown whether they were revealed at showdown
dealt whether they were seen in a 'dealt to' line
""" """
logging.debug("addHoleCards %s %s" % (cards, player)) logging.debug("addHoleCards %s %s" % (cards, player))
try: try:
self.checkPlayerExists(player) self.checkPlayerExists(player)
cardset = set(self.card(c) for c in cards)
if shown and len(cardset) > 0:
self.shown.add(player)
if 'PREFLOP' in self.holecards[player]:
self.holecards[player]['PREFLOP'].update(cardset)
else:
self.holecards[player]['PREFLOP'] = cardset
except FpdbParseError, e: except FpdbParseError, e:
print "[ERROR] Tried to add holecards for unknown player: %s" % (player,) print "[ERROR] Tried to add holecards for unknown player: %s" % (player,)
return
cardset = set((self.card(c) for c in cards))
if len(cardset) == 0:
return
if dealt:
self.dealt.add(player)
if shown:
self.shown.add(player)
if player in self.holecards['PREFLOP']:
self.holecards['PREFLOP'][player].update(cardset)
else:
self.holecards['PREFLOP'][player] = cardset
def addShownCards(self, cards, player, holeandboard=None): def addShownCards(self, cards, player, holeandboard=None):
"""\ """\
@ -506,14 +528,108 @@ Card ranks will be uppercased
""" """
logging.debug("addShownCards %s hole=%s all=%s" % (player, cards, holeandboard)) logging.debug("addShownCards %s hole=%s all=%s" % (player, cards, holeandboard))
if cards is not None: if cards is not None:
self.shown.add(player) self.addHoleCards(cards,player,shown=True)
self.addHoleCards(cards,player)
elif holeandboard is not None: elif holeandboard is not None:
holeandboard = set([self.card(c) for c in holeandboard]) holeandboard = set([self.card(c) for c in holeandboard])
board = set([c for s in self.board.values() for c in s]) board = set([c for s in self.board.values() for c in s])
self.addHoleCards(holeandboard.difference(board),player,shown=True) self.addHoleCards(holeandboard.difference(board),player,shown=True)
def writeHTMLHand(self, fh=sys.__stdout__):
from nevow import tags as T
from nevow import flat
players_who_act_preflop = (([x[0] for x in self.actions['PREFLOP']]+[x[0] for x in self.actions['BLINDSANTES']]))
players_stacks = [x for x in self.players if x[1] in players_who_act_preflop]
action_streets = [x for x in self.actionStreets if len(self.actions[x]) > 0]
def render_stack(context,data):
pat = context.tag.patternGenerator('list_item')
for player in data:
x = "Seat %s: %s ($%s in chips) " %(player[0], player[1],
player[2])
context.tag[ pat().fillSlots('playerStack', x)]
return context.tag
def render_street(context,data):
pat = context.tag.patternGenerator('list_item')
for street in data:
lines = []
if street in self.holeStreets and self.holecards[street]:
lines.append(
T.ol(class_='dealclosed', data=street,
render=render_deal) [
T.li(pattern='list_item')[ T.slot(name='deal') ]
]
)
if street in self.communityStreets and self.board[street]:
lines.append(
T.ol(class_='community', data=street,
render=render_deal_community)[
T.li(pattern='list_item')[ T.slot(name='deal') ]
]
)
if street in self.actionStreets and self.actions[street]:
lines.append(
T.ol(class_='actions', data=self.actions[street], render=render_action) [
T.li(pattern='list_item')[ T.slot(name='action') ]
]
)
if lines:
context.tag[ pat().fillSlots('street', [ T.h3[ street ] ]+lines)]
return context.tag
def render_deal(context,data):
# data is streetname
# we can have open+closed, or just open, or just closed.
if self.holecards[data]:
for player in self.holecards[data]:
somestuff = 'dealt to %s %s' % (player, self.holecards[data][player])
pat = context.tag.patternGenerator('list_item')
context.tag[ pat().fillSlots('deal', somestuff)]
return context.tag
def render_deal_community(context,data):
# data is streetname
if self.board[data]:
somestuff = '[' + ' '.join(self.board[data]) + ']'
pat = context.tag.patternGenerator('list_item')
context.tag[ pat().fillSlots('deal', somestuff)]
return context.tag
def render_action(context,data):
pat = context.tag.patternGenerator('list_item')
for act in data:
x = self.actionString(act)
context.tag[ pat().fillSlots('action', x)]
return context.tag
s = T.p[
T.h1[
T.span(class_='site')["%s Game #%s]" % ('PokerStars', self.handid)],
T.span(class_='type_limit')[ "%s ($%s/$%s)" %(self.getGameTypeAsString(), self.sb, self.bb) ],
T.span(class_='date')[ datetime.datetime.strftime(self.starttime,'%Y/%m/%d - %H:%M:%S ET') ]
],
T.h2[ "Table '%s' %d-max Seat #%s is the button" %(self.tablename,
self.maxseats, self.buttonpos)],
T.ol(class_='stacks', data = players_stacks, render=render_stack)[
T.li(pattern='list_item')[ T.slot(name='playerStack') ]
],
T.ol(class_='streets', data = self.allStreets,
render=render_street)[
T.li(pattern='list_item')[ T.slot(name='street')]
]
]
import tidy
options = dict(input_xml=True,
output_xhtml=True,
add_xml_decl=False,
doctype='omit',
indent='auto',
tidy_mark=False)
return str(tidy.parseString(flat.flatten(s), **options))
def writeHand(self, fh=sys.__stdout__): def writeHand(self, fh=sys.__stdout__):
# PokerStars format. # PokerStars format.
print >>fh, ("%s Game #%s: %s ($%s/$%s) - %s" %("PokerStars", self.handid, self.getGameTypeAsString(), self.sb, self.bb, datetime.datetime.strftime(self.starttime,'%Y/%m/%d - %H:%M:%S ET'))) print >>fh, ("%s Game #%s: %s ($%s/$%s) - %s" %("PokerStars", self.handid, self.getGameTypeAsString(), self.sb, self.bb, datetime.datetime.strftime(self.starttime,'%Y/%m/%d - %H:%M:%S ET')))
@ -527,33 +643,36 @@ Card ranks will be uppercased
if self.actions['BLINDSANTES']: if self.actions['BLINDSANTES']:
for act in self.actions['BLINDSANTES']: for act in self.actions['BLINDSANTES']:
self.printActionLine(act, fh) print >>fh, self.actionString(act)
print >>fh, ("*** HOLE CARDS ***") print >>fh, ("*** HOLE CARDS ***")
if self.involved: for player in self.dealt:
print >>fh, ("Dealt to %s [%s]" %(self.hero , " ".join(self.holecards[self.hero]['PREFLOP']))) print >>fh, ("Dealt to %s [%s]" %(player, " ".join(self.holecards['PREFLOP'][player])))
if self.hero == "":
for player in self.shown.difference(self.dealt):
print >>fh, ("Dealt to %s [%s]" %(player, " ".join(self.holecards['PREFLOP'][player])))
if self.actions['PREFLOP']: if self.actions['PREFLOP']:
for act in self.actions['PREFLOP']: for act in self.actions['PREFLOP']:
self.printActionLine(act, fh) print >>fh, self.actionString(act)
if self.board['FLOP']: if self.board['FLOP']:
print >>fh, ("*** FLOP *** [%s]" %( " ".join(self.board['FLOP']))) print >>fh, ("*** FLOP *** [%s]" %( " ".join(self.board['FLOP'])))
if self.actions['FLOP']: if self.actions['FLOP']:
for act in self.actions['FLOP']: for act in self.actions['FLOP']:
self.printActionLine(act, fh) print >>fh, self.actionString(act)
if self.board['TURN']: if self.board['TURN']:
print >>fh, ("*** TURN *** [%s] [%s]" %( " ".join(self.board['FLOP']), " ".join(self.board['TURN']))) print >>fh, ("*** TURN *** [%s] [%s]" %( " ".join(self.board['FLOP']), " ".join(self.board['TURN'])))
if self.actions['TURN']: if self.actions['TURN']:
for act in self.actions['TURN']: for act in self.actions['TURN']:
self.printActionLine(act, fh) print >>fh, self.actionString(act)
if self.board['RIVER']: if self.board['RIVER']:
print >>fh, ("*** 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']) ))
if self.actions['RIVER']: if self.actions['RIVER']:
for act in self.actions['RIVER']: for act in self.actions['RIVER']:
self.printActionLine(act, fh) print >>fh, self.actionString(act)
#Some sites don't have a showdown section so we have to figure out if there should be one #Some sites don't have a showdown section so we have to figure out if there should be one
@ -569,8 +688,8 @@ Card ranks will be uppercased
numOfHoleCardsNeeded = 4 numOfHoleCardsNeeded = 4
elif self.gametype['category'] in ('holdem'): elif self.gametype['category'] in ('holdem'):
numOfHoleCardsNeeded = 2 numOfHoleCardsNeeded = 2
if len(self.holecards[name]['PREFLOP']) == numOfHoleCardsNeeded: if len(self.holecards['PREFLOP'][name]) == numOfHoleCardsNeeded:
print >>fh, ("%s shows [%s] (a hand...)" % (name, " ".join(self.holecards[name]['PREFLOP']))) print >>fh, ("%s shows [%s] (a hand...)" % (name, " ".join(self.holecards['PREFLOP'][name])))
# Current PS format has the lines: # Current PS format has the lines:
# Uncalled bet ($111.25) returned to s0rrow # Uncalled bet ($111.25) returned to s0rrow
@ -597,7 +716,7 @@ Card ranks will be uppercased
seatnum = player[0] seatnum = player[0]
name = player[1] name = player[1]
if name in self.collectees and name in self.shown: if name in self.collectees and name in self.shown:
print >>fh, ("Seat %d: %s showed [%s] and won ($%s)" % (seatnum, name, " ".join(self.holecards[name]['PREFLOP']), self.collectees[name])) print >>fh, ("Seat %d: %s showed [%s] and won ($%s)" % (seatnum, name, " ".join(self.holecards['PREFLOP'][name]), self.collectees[name]))
elif name in self.collectees: elif name in self.collectees:
print >>fh, ("Seat %d: %s collected ($%s)" % (seatnum, name, self.collectees[name])) print >>fh, ("Seat %d: %s collected ($%s)" % (seatnum, name, self.collectees[name]))
#~ elif name in self.shown: #~ elif name in self.shown:
@ -606,7 +725,7 @@ Card ranks will be uppercased
print >>fh, ("Seat %d: %s folded" % (seatnum, name)) print >>fh, ("Seat %d: %s folded" % (seatnum, name))
else: else:
if name in self.shown: if name in self.shown:
print >>fh, ("Seat %d: %s showed [%s] and lost with..." % (seatnum, name, " ".join(self.holecards[name]['PREFLOP']))) print >>fh, ("Seat %d: %s showed [%s] and lost with..." % (seatnum, name, " ".join(self.holecards['PREFLOP'][name])))
else: else:
print >>fh, ("Seat %d: %s mucked" % (seatnum, name)) print >>fh, ("Seat %d: %s mucked" % (seatnum, name))
@ -739,12 +858,12 @@ Card ranks will be uppercased
(nc,oc) = self.holecards[player]['DEAL'] (nc,oc) = self.holecards[player]['DEAL']
print >>fh, _("Dealt to %s: [%s]") % (player, " ".join(nc)) print >>fh, _("Dealt to %s: [%s]") % (player, " ".join(nc))
for act in self.actions['DEAL']: for act in self.actions['DEAL']:
self.printActionLine(act, fh) print >>fh, self.actionString(act)
if 'DRAWONE' in self.actions: if 'DRAWONE' in self.actions:
print >>fh, _("*** FIRST DRAW ***") print >>fh, _("*** FIRST DRAW ***")
for act in self.actions['DRAWONE']: for act in self.actions['DRAWONE']:
self.printActionLine(act, fh) print >>fh, self.actionString(act)
if act[0] == self.hero and act[1] == 'discards': if act[0] == self.hero and act[1] == 'discards':
(nc,oc) = self.holecards[act[0]]['DRAWONE'] (nc,oc) = self.holecards[act[0]]['DRAWONE']
dc = self.discards[act[0]]['DRAWONE'] dc = self.discards[act[0]]['DRAWONE']
@ -754,7 +873,7 @@ Card ranks will be uppercased
if 'DRAWTWO' in self.actions: if 'DRAWTWO' in self.actions:
print >>fh, _("*** SECOND DRAW ***") print >>fh, _("*** SECOND DRAW ***")
for act in self.actions['DRAWTWO']: for act in self.actions['DRAWTWO']:
self.printActionLine(act, fh) print >>fh, self.actionString(act)
if act[0] == self.hero and act[1] == 'discards': if act[0] == self.hero and act[1] == 'discards':
(nc,oc) = self.holecards[act[0]]['DRAWTWO'] (nc,oc) = self.holecards[act[0]]['DRAWTWO']
dc = self.discards[act[0]]['DRAWTWO'] dc = self.discards[act[0]]['DRAWTWO']
@ -764,7 +883,7 @@ Card ranks will be uppercased
if 'DRAWTHREE' in self.actions: if 'DRAWTHREE' in self.actions:
print >>fh, _("*** THIRD DRAW ***") print >>fh, _("*** THIRD DRAW ***")
for act in self.actions['DRAWTHREE']: for act in self.actions['DRAWTHREE']:
self.printActionLine(act, fh) print >>fh, self.actionString(act)
if act[0] == self.hero and act[1] == 'discards': if act[0] == self.hero and act[1] == 'discards':
(nc,oc) = self.holecards[act[0]]['DRAWTHREE'] (nc,oc) = self.holecards[act[0]]['DRAWTHREE']
dc = self.discards[act[0]]['DRAWTHREE'] dc = self.discards[act[0]]['DRAWTHREE']
@ -878,7 +997,8 @@ Add a complete on [street] by [player] to [amountTo]
def writeHand(self, fh=sys.__stdout__): def writeHand(self, fh=sys.__stdout__):
# PokerStars format. # PokerStars format.
print >>fh, _("%s Game #%s: %s ($%s/$%s) - %s" %("PokerStars", self.handid, self.getGameTypeAsString(), self.sb, self.bb, time.strftime('%Y/%m/%d - %H:%M:%S (ET)', self.starttime))) # print >>fh, _("%s Game #%s: %s ($%s/$%s) - %s" %("PokerStars", self.handid, self.getGameTypeAsString(), self.sb, self.bb, time.strftime('%Y/%m/%d - %H:%M:%S (ET)', self.starttime)))
print >>fh, _("%s Game #%s: %s ($%s/$%s) - %s" %("PokerStars", self.handid, self.getGameTypeAsString(), self.sb, self.bb, datetime.datetime.strftime(self.starttime,'%Y/%m/%d - %H:%M:%S ET')))
print >>fh, _("Table '%s' %d-max Seat #%s is the button" %(self.tablename, self.maxseats, self.buttonpos)) print >>fh, _("Table '%s' %d-max Seat #%s is the button" %(self.tablename, self.maxseats, self.buttonpos))
players_who_post_antes = set([x[0] for x in self.actions['ANTES']]) players_who_post_antes = set([x[0] for x in self.actions['ANTES']])
@ -903,7 +1023,7 @@ Add a complete on [street] by [player] to [amountTo]
print >>fh, _("Dealt to %s:%s%s") % (player, " [" + " ".join(closed) + "] " if closed else " ", "[" + " ".join(open) + "]" if open else "") print >>fh, _("Dealt to %s:%s%s") % (player, " [" + " ".join(closed) + "] " if closed else " ", "[" + " ".join(open) + "]" if open else "")
for act in self.actions['THIRD']: for act in self.actions['THIRD']:
#FIXME: Need some logic here for bringin vs completes #FIXME: Need some logic here for bringin vs completes
self.printActionLine(act, fh) print >>fh, self.actionString(act)
if 'FOURTH' in self.actions: if 'FOURTH' in self.actions:
dealt = 0 dealt = 0
@ -920,7 +1040,7 @@ Add a complete on [street] by [player] to [amountTo]
print >>fh, _("*** 4TH STREET ***") print >>fh, _("*** 4TH STREET ***")
print >>fh, _("Dealt to %s:%s%s") % (player, " [" + " ".join(old) + "] " if old else " ", "[" + " ".join(new) + "]" if new else "") print >>fh, _("Dealt to %s:%s%s") % (player, " [" + " ".join(old) + "] " if old else " ", "[" + " ".join(new) + "]" if new else "")
for act in self.actions['FOURTH']: for act in self.actions['FOURTH']:
self.printActionLine(act, fh) print >>fh, self.actionString(act)
if 'FIFTH' in self.actions: if 'FIFTH' in self.actions:
dealt = 0 dealt = 0
@ -938,7 +1058,7 @@ Add a complete on [street] by [player] to [amountTo]
print >>fh, _("*** 5TH STREET ***") print >>fh, _("*** 5TH STREET ***")
print >>fh, _("Dealt to %s:%s%s") % (player, " [" + " ".join(old) + "] " if old else " ", "[" + " ".join(new) + "]" if new else "") print >>fh, _("Dealt to %s:%s%s") % (player, " [" + " ".join(old) + "] " if old else " ", "[" + " ".join(new) + "]" if new else "")
for act in self.actions['FIFTH']: for act in self.actions['FIFTH']:
self.printActionLine(act, fh) print >>fh, self.actionString(act)
if 'SIXTH' in self.actions: if 'SIXTH' in self.actions:
dealt = 0 dealt = 0
@ -956,7 +1076,7 @@ Add a complete on [street] by [player] to [amountTo]
print >>fh, _("*** 6TH STREET ***") print >>fh, _("*** 6TH STREET ***")
print >>fh, _("Dealt to %s:%s%s") % (player, " [" + " ".join(old) + "] " if old else " ", "[" + " ".join(new) + "]" if new else "") print >>fh, _("Dealt to %s:%s%s") % (player, " [" + " ".join(old) + "] " if old else " ", "[" + " ".join(new) + "]" if new else "")
for act in self.actions['SIXTH']: for act in self.actions['SIXTH']:
self.printActionLine(act, fh) print >>fh, self.actionString(act)
if 'SEVENTH' in self.actions: if 'SEVENTH' in self.actions:
# OK. It's possible that they're all in at an earlier street, but only closed cards are dealt. # OK. It's possible that they're all in at an earlier street, but only closed cards are dealt.
@ -975,7 +1095,7 @@ Add a complete on [street] by [player] to [amountTo]
if new: if new:
print >>fh, _("Dealt to %s:%s%s") % (player, " [" + " ".join(old) + "] " if old else " ", "[" + " ".join(new) + "]" if new else "") print >>fh, _("Dealt to %s:%s%s") % (player, " [" + " ".join(old) + "] " if old else " ", "[" + " ".join(new) + "]" if new else "")
for act in self.actions['SEVENTH']: for act in self.actions['SEVENTH']:
self.printActionLine(act, fh) print >>fh, self.actionString(act)
#Some sites don't have a showdown section so we have to figure out if there should be one #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 # The logic for a showdown is: at the end of river action there are at least two players in the hand
@ -1106,7 +1226,8 @@ class Pot(object):
def assemble(cnxn, handid): def assemble(cnxn, handid):
c = cnxn.cursor() c = cnxn.cursor()
# We need the following for the Hand.__init__ # We need at least sitename, gametype, handid
# for the Hand.__init__
c.execute(""" c.execute("""
select select
s.name, s.name,
@ -1115,27 +1236,24 @@ select
g.type, g.type,
g.limitType, g.limitType,
g.hilo, g.hilo,
g.smallBlind / 100.0, round(g.smallBlind / 100.0,2),
g.bigBlind / 100.0 , round(g.bigBlind / 100.0,2),
g.smallBet / 100.0, round(g.smallBet / 100.0,2),
g.bigBet / 100.0, round(g.bigBet / 100.0,2),
s.currency, s.currency,
bc.card1value, h.boardcard1,
bc.card1suit, h.boardcard2,
bc.card2value,bc.card2suit, h.boardcard3,
bc.card3value,bc.card3suit, h.boardcard4,
bc.card4value,bc.card4suit, h.boardcard5
bc.card5value,bc.card5suit
from from
hands as h, hands as h,
boardcards as bc,
sites as s, sites as s,
gametypes as g, gametypes as g,
handsplayers as hp, handsplayers as hp,
players as p players as p
where where
h.id = %(handid)s h.id = %(handid)s
and bc.handid = h.id
and g.id = h.gametypeid and g.id = h.gametypeid
and hp.handid = h.id and hp.handid = h.id
and p.id = hp.playerid and p.id = hp.playerid
@ -1145,20 +1263,15 @@ limit 1""", {'handid':handid})
res = c.fetchone() res = c.fetchone()
gametype = {'category':res[1],'base':res[2],'type':res[3],'limitType':res[4],'hilo':res[5],'sb':res[6],'bb':res[7], 'currency':res[10]} gametype = {'category':res[1],'base':res[2],'type':res[3],'limitType':res[4],'hilo':res[5],'sb':res[6],'bb':res[7], 'currency':res[10]}
h = HoldemOmahaHand(hhc = None, sitename=res[0], gametype = gametype, handText=None, builtFrom = "DB", handid=handid) h = HoldemOmahaHand(hhc = None, sitename=res[0], gametype = gametype, handText=None, builtFrom = "DB", handid=handid)
cards = map("".join, zip(map(str,res[11:21:2]), res[12:21:2])) cards = map(Card.valueSuitFromCard, res[11:16] )
if cards[0]:
if cards[0] != "0x":
h.setCommunityCards('FLOP', cards[0:3]) h.setCommunityCards('FLOP', cards[0:3])
if cards[3] != "0x": if cards[3]:
h.setCommunityCards('TURN', cards[3]) h.setCommunityCards('TURN', [cards[3]])
if cards[4] != "0x": if cards[4]:
h.setCommunityCards('RIVER', cards[4]) h.setCommunityCards('RIVER', [cards[4]])
#[Card.valueSuitFromCard(x) for x in cards] #[Card.valueSuitFromCard(x) for x in cards]
#TODO : doesn't look like this is in the database; don't like the way Hand requires it
h.hero = 'mcturnbull'
# HandInfo : HID, TABLE # HandInfo : HID, TABLE
# BUTTON - why is this treated specially in Hand? # BUTTON - why is this treated specially in Hand?
# answer: it is written out in hand histories # answer: it is written out in hand histories
@ -1181,9 +1294,11 @@ WHERE h.id = %(handid)s
c.execute(""" c.execute("""
SELECT SELECT
hp.seatno, hp.seatno,
round(hp.winnings / 100.0,2) as winnings,
p.name, p.name,
round(hp.startcash / 100.0,2) as chips, round(hp.startcash / 100.0,2) as chips,
(hp.card1,hp.card2) as hole hp.card1,hp.card2,
hp.position
FROM FROM
handsplayers as hp, handsplayers as hp,
players as p players as p
@ -1191,10 +1306,16 @@ WHERE
hp.handid = %(handid)s hp.handid = %(handid)s
and p.id = hp.playerid and p.id = hp.playerid
""", {'handid':handid}) """, {'handid':handid})
for (seat, name, chips, cards) in c.fetchall(): for (seat, winnings, name, chips, card1,card2, position) in c.fetchall():
h.addPlayer(seat,name,chips) h.addPlayer(seat,name,chips)
h.addHoleCards([Card.valueSuitFromCard(x) for x in cards],name) if card1 and card2:
h.addHoleCards(map(Card.valueSuitFromCard, (card1,card2)), name, dealt=True)
if winnings > 0:
h.addCollectPot(name, winnings)
if position == 'B':
h.buttonpos = seat
# actions # actions
c.execute(""" c.execute("""
SELECT SELECT
@ -1203,7 +1324,7 @@ SELECT
ha.street, ha.street,
ha.action, ha.action,
ha.allin, ha.allin,
ha.amount / 100.0 round(ha.amount / 100.0,2)
FROM FROM
handsplayers as hp, handsplayers as hp,
handsactions as ha, handsactions as ha,
@ -1218,7 +1339,7 @@ ORDER BY
res = c.fetchall() res = c.fetchall()
for (actnum,player, streetnum, act, allin, amount) in res: for (actnum,player, streetnum, act, allin, amount) in res:
act=act.strip() act=act.strip()
street = h.streetList[streetnum+2] street = h.allStreets[streetnum+1]
if act==u'blind': if act==u'blind':
h.addBlind(player, 'big blind', amount) h.addBlind(player, 'big blind', amount)
# TODO: The type of blind is not recorded in the DB. # TODO: The type of blind is not recorded in the DB.
@ -1236,7 +1357,7 @@ ORDER BY
else: else:
print act, player, streetnum, allin, amount print act, player, streetnum, allin, amount
# TODO : other actions # TODO : other actions
#hhc.readCollectPot(self)
#hhc.readShowdownActions(self) #hhc.readShowdownActions(self)
#hc.readShownCards(self) #hc.readShownCards(self)
h.totalPot() h.totalPot()

View File

@ -43,7 +43,7 @@ import Configuration
import Stats import Stats
import Mucked import Mucked
import Database import Database
import HUD_main #import HUD_main
def importName(module_name, name): def importName(module_name, name):
"""Import a named object 'name' from module 'module_name'.""" """Import a named object 'name' from module 'module_name'."""

View File

@ -35,6 +35,7 @@ import gobject
# FreePokerTools modules # FreePokerTools modules
import Configuration import Configuration
import Database import Database
import Card
class Aux_Window: class Aux_Window:
def __init__(self, hud, params, config): def __init__(self, hud, params, config):
@ -67,19 +68,27 @@ class Aux_Window:
# Some utility routines useful for Aux_Windows # Some utility routines useful for Aux_Windows
# #
def get_card_images(self): def get_card_images(self):
card_images = {}
suits = ('S', 'H', 'D', 'C') card_images = 53 * [0]
ranks = ('A', 'K', 'Q', 'J', 'T', '9', '8', '7', '6', '5', '4', '3', '2', 'B') suits = ('s', 'h', 'd', 'c')
ranks = (14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2)
pb = gtk.gdk.pixbuf_new_from_file(self.config.execution_path(self.params['deck'])) pb = gtk.gdk.pixbuf_new_from_file(self.config.execution_path(self.params['deck']))
for j in range(0, 14): for j in range(0, 13):
for i in range(0, 4): for i in range(0, 4):
temp_pb = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, pb.get_has_alpha(), pb.get_bits_per_sample(), 30, 42) card_images[Card.cardFromValueSuit(ranks[j], suits[i])] = self.cropper(pb, i, j)
pb.copy_area(30*j, 42*i, 30, 42, temp_pb, 0, 0) temp_pb = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, pb.get_has_alpha(), pb.get_bits_per_sample(), 30, 42)
card_images[(ranks[j], suits[i])] = temp_pb # also pick out a card back and store in [0]
card_images[0] = self.cropper(pb, 2, 13)
return(card_images) return(card_images)
# cards are 30 wide x 42 high # cards are 30 wide x 42 high
def cropper(self, pb, i, j):
"""Crop out a card image given an FTP deck and the i, j position."""
temp_pb = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, pb.get_has_alpha(), pb.get_bits_per_sample(), 30, 42)
pb.copy_area(30*j, 42*i, 30, 42, temp_pb, 0, 0)
return temp_pb
def split_cards(self, card): def split_cards(self, card):
if card == 'xx': return ('B', 'S') if card == 'xx': return ('B', 'S')
return (card[0], card[1].upper()) return (card[0], card[1].upper())
@ -108,9 +117,10 @@ class Stud_mucked(Aux_Window):
def create(self): def create(self):
self.container =gtk.Window() self.container = gtk.Window()
self.vbox = gtk.VBox() self.vbox = gtk.VBox()
self.container.add(self.vbox) self.container.add(self.vbox)
self.container.set_title(self.hud.table.name)
self.mucked_list.create(self.vbox) self.mucked_list.create(self.vbox)
self.mucked_cards.create(self.vbox) self.mucked_cards.create(self.vbox)
@ -207,7 +217,9 @@ class Stud_list:
# find the hero's seat from the stat_dict # find the hero's seat from the stat_dict
for stat in self.parent.hud.stat_dict.itervalues(): for stat in self.parent.hud.stat_dict.itervalues():
if stat['screen_name'] == hero: if stat['screen_name'] == hero:
return self.parent.hud.cards[stat['seat']][0:6] return Card.valueSuitFromCard(self.parent.hud.cards[stat['seat']][0]) +\
Card.valueSuitFromCard(self.parent.hud.cards[stat['seat']][1]) +\
Card.valueSuitFromCard(self.parent.hud.cards[stat['seat']][2])
return "xxxxxx" return "xxxxxx"
def update_gui(self, new_hand_id): def update_gui(self, new_hand_id):
@ -240,7 +252,7 @@ class Stud_cards:
for r in range(0, self.rows): for r in range(0, self.rows):
for c in range(0, self.cols): for c in range(0, self.cols):
self.seen_cards[(c, r)] = gtk.image_new_from_pixbuf(self.card_images[('B', 'S')]) self.seen_cards[(c, r)] = gtk.image_new_from_pixbuf(self.card_images[(0)])
self.eb[(c, r)]= gtk.EventBox() self.eb[(c, r)]= gtk.EventBox()
# set up the contents for the cells # set up the contents for the cells
@ -287,11 +299,11 @@ class Stud_cards:
self.clear() self.clear()
for c, cards in self.parent.hud.cards.iteritems(): for c, cards in self.parent.hud.cards.iteritems():
self.grid_contents[(1, c - 1)].set_text(self.get_screen_name(c)) self.grid_contents[(1, c - 1)].set_text(self.get_screen_name(c))
for i in ((0, cards[0:2]), (1, cards[2:4]), (2, cards[4:6]), (3, cards[6:8]), for i in ((0, cards[0]), (1, cards[1]), (2, cards[2]), (3, cards[3]),
(4, cards[8:10]), (5, cards[10:12]), (6, cards[12:14])): (4, cards[4]), (5, cards[5]), (6, cards[6])):
if not i[1] == "xx": if not i[1] == 0:
self.seen_cards[(i[0], c - 1)]. \ self.seen_cards[(i[0], c - 1)]. \
set_from_pixbuf(self.card_images[self.parent.split_cards(i[1])]) set_from_pixbuf(self.card_images[i[1]])
## action in tool tips for 3rd street cards ## action in tool tips for 3rd street cards
for c in (0, 1, 2): for c in (0, 1, 2):
for r in range(0, self.rows): for r in range(0, self.rows):
@ -314,7 +326,7 @@ class Stud_cards:
for r in range(0, self.rows): for r in range(0, self.rows):
self.grid_contents[(1, r)].set_text(" ") self.grid_contents[(1, r)].set_text(" ")
for c in range(0, 7): for c in range(0, 7):
self.seen_cards[(c, r)].set_from_pixbuf(self.card_images[('B', 'S')]) self.seen_cards[(c, r)].set_from_pixbuf(self.card_images[0])
self.eb[(c, r)].set_tooltip_text('') self.eb[(c, r)].set_tooltip_text('')
class Flop_Mucked(Aux_Window): class Flop_Mucked(Aux_Window):
@ -459,29 +471,31 @@ class Flop_Mucked(Aux_Window):
new_locs[i] = (pos[0] - self.hud.table.x, pos[1] - self.hud.table.y) new_locs[i] = (pos[0] - self.hud.table.x, pos[1] - self.hud.table.y)
self.config.edit_aux_layout(self.params['name'], self.hud.max, locations = new_locs) self.config.edit_aux_layout(self.params['name'], self.hud.max, locations = new_locs)
if __name__== "__main__": # This test program doesn't work
def destroy(*args): # call back for terminating the main eventloop
gtk.main_quit() # used only for testing
def process_new_hand(source, condition, db_connection): #callback from stdin watch -- testing only #if __name__== "__main__":
# there is a new hand_id to be processed #
# just read it and pass it to update # def destroy(*args): # call back for terminating the main eventloop
new_hand_id = sys.stdin.readline() # gtk.main_quit() # used only for testing
new_hand_id = new_hand_id.rstrip() # remove trailing whitespace #
m.update_data(new_hand_id, db_connection) # def process_new_hand(source, condition, db_connection): #callback from stdin watch -- testing only
m.update_gui(new_hand_id) ## there is a new hand_id to be processed
return(True) ## just read it and pass it to update
# new_hand_id = sys.stdin.readline()
config = Configuration.Config() # new_hand_id = new_hand_id.rstrip() # remove trailing whitespace
db_connection = Database.Database(config, 'fpdb', '') # m.update_data(new_hand_id, db_connection)
main_window = gtk.Window() # m.update_gui(new_hand_id)
main_window.set_keep_above(True) # return(True)
main_window.connect("destroy", destroy) #
# config = Configuration.Config()
aux_to_call = "stud_mucked" # db_connection = Database.Database(config, 'fpdbTEST', '')
aux_params = config.get_aux_parameters(aux_to_call) # main_window = gtk.Window()
m = eval("%s(main_window, None, config, aux_params)" % aux_params['class']) # main_window.set_keep_above(True)
# main_window.connect("destroy", destroy)
s_id = gobject.io_add_watch(sys.stdin, gobject.IO_IN, process_new_hand, db_connection) #
gtk.main() # aux_to_call = "stud_mucked"
# aux_params = config.get_aux_parameters(aux_to_call)
# m = eval("%s(None, config, aux_params)" % aux_params['class'])
#
# s_id = gobject.io_add_watch(sys.stdin, gobject.IO_IN, process_new_hand, db_connection)
# gtk.main()

45
pyfpdb/Options.py Normal file
View File

@ -0,0 +1,45 @@
#!/usr/bin/python
#Copyright 2008 Ray E. Barker
#This program is free software: you can redistribute it and/or modify
#it under the terms of the GNU Affero General Public License as published by
#the Free Software Foundation, version 3 of the License.
#
#This program is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
#GNU General Public License for more details.
#
#You should have received a copy of the GNU Affero General Public License
#along with this program. If not, see <http://www.gnu.org/licenses/>.
#In the "official" distribution you can find the license in
#agpl-3.0.txt in the docs folder of the package.
import os
import sys
from optparse import OptionParser
def fpdb_options():
"""Process command line options for fpdb and HUD_main."""
parser = OptionParser()
parser.add_option("-x", "--errorsToConsole",
action="store_true",
help="If passed error output will go to the console rather than .")
parser.add_option("-d", "--databaseName",
dest="dbname", default="fpdb",
help="Overrides the default database name")
parser.add_option("-c", "--configFile",
dest="config", default=None,
help="Specifies a configuration file.")
(options, sys.argv) = parser.parse_args()
return (options, sys.argv)
if __name__== "__main__":
(options, sys.argv) = fpdb_options()
print "errorsToConsole =", options.errorsToConsole
print "database name =", options.dbname
print "config file =", options.config
print "press enter to end"
sys.stdin.readline()

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright 2008, Carl Gherardi # Copyright 2008, Carl Gherardi
@ -26,7 +26,7 @@ from HandHistoryConverter import *
class PokerStars(HandHistoryConverter): class PokerStars(HandHistoryConverter):
# Static regexes # Static regexes
re_GameInfo = re.compile("PokerStars Game #(?P<HID>[0-9]+):\s+(HORSE)? \(?(?P<GAME>Hold\'em|Razz|7 Card Stud|Omaha|Omaha Hi/Lo|Badugi) (?P<LIMIT>No Limit|Limit|Pot Limit),? \(?(?P<CURRENCY>\$|)?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+)\) - (?P<DATETIME>.*$)", re.MULTILINE) re_GameInfo = re.compile("PokerStars Game #(?P<HID>[0-9]+):\s+(HORSE)? \(?(?P<GAME>Hold\'em|Razz|7 Card Stud|7 Card Stud Hi/Lo|Omaha|Omaha Hi/Lo|Badugi) (?P<LIMIT>No Limit|Limit|Pot Limit),? \(?(?P<CURRENCY>\$|)?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+)\) - (?P<DATETIME>.*$)", re.MULTILINE)
re_SplitHands = re.compile('\n\n+') re_SplitHands = re.compile('\n\n+')
re_TailSplitHands = re.compile('(\n\n\n+)') re_TailSplitHands = re.compile('(\n\n\n+)')
re_HandInfo = re.compile("^Table \'(?P<TABLE>[- a-zA-Z]+)\'(?P<TABLEATTRIBUTES>.+?$)?", re.MULTILINE) re_HandInfo = re.compile("^Table \'(?P<TABLE>[- a-zA-Z]+)\'(?P<TABLEATTRIBUTES>.+?$)?", re.MULTILINE)
@ -95,6 +95,7 @@ follow : whether to tail -f the input"""
'Omaha Hi/Lo' : ('hold','omahahilo'), 'Omaha Hi/Lo' : ('hold','omahahilo'),
'Razz' : ('stud','razz'), 'Razz' : ('stud','razz'),
'7 Card Stud' : ('stud','studhi'), '7 Card Stud' : ('stud','studhi'),
'7 Card Stud Hi/Lo' : ('stud','studhilo'),
'Badugi' : ('draw','badugi') 'Badugi' : ('draw','badugi')
} }
currencies = { u'':'EUR', '$':'USD', '':'T$' } currencies = { u'':'EUR', '$':'USD', '':'T$' }
@ -208,7 +209,7 @@ follow : whether to tail -f the input"""
for a in self.re_PostBB.finditer(hand.handText): for a in self.re_PostBB.finditer(hand.handText):
hand.addBlind(a.group('PNAME'), 'big blind', a.group('BB')) hand.addBlind(a.group('PNAME'), 'big blind', a.group('BB'))
for a in self.re_PostBoth.finditer(hand.handText): for a in self.re_PostBoth.finditer(hand.handText):
hand.addBlind(a.group('PNAME'), 'small & big blinds', a.group('SBBB')) hand.addBlind(a.group('PNAME'), 'both', a.group('SBBB'))
def readHeroCards(self, hand): def readHeroCards(self, hand):
m = self.re_HeroCards.search(hand.handText) m = self.re_HeroCards.search(hand.handText)

View File

@ -528,7 +528,6 @@ class Sql:
self.query['get_cards'] = """ self.query['get_cards'] = """
select select
seatNo AS seat_number, seatNo AS seat_number,
name AS screen_name,
card1, /*card1Value, card1Suit, */ card1, /*card1Value, card1Suit, */
card2, /*card2Value, card2Suit, */ card2, /*card2Value, card2Suit, */
card3, /*card3Value, card3Suit, */ card3, /*card3Value, card3Suit, */

View File

@ -62,7 +62,7 @@ class Table(Table_Window):
return None return None
(x, y, width, height) = win32gui.GetWindowRect(hwnd) (x, y, width, height) = win32gui.GetWindowRect(hwnd)
print "x = %s y = %s width = %s height = %s" % (x, y, width, height) print "x = %s y = %s width = %s height = %s" % (x, y, width, height)
self.x = int(x) + b_width self.x = int(x) + b_width
self.y = int(y) + tb_height self.y = int(y) + tb_height
self.height = int(height) - b_width - tb_height self.height = int(height) - b_width - tb_height
@ -72,7 +72,8 @@ class Table(Table_Window):
self.title = titles[hwnd] self.title = titles[hwnd]
self.site = "" self.site = ""
self.hud = None self.hud = None
self.number = gtk.gdk.window_foreign_new(long(self.window)) self.number = hwnd
self.gdkhandle = gtk.gdk.window_foreign_new(long(self.window))
def get_geometry(self): def get_geometry(self):
@ -112,32 +113,31 @@ class Table(Table_Window):
win32api.CloseHandle(hToken) win32api.CloseHandle(hToken)
return exename return exename
def topify(self, hud):
"""Set the specified gtk window to stayontop in MS Windows."""
def windowEnumerationHandler(hwnd, resultList):
'''Callback for win32gui.EnumWindows() to generate list of window handles.'''
resultList.append((hwnd, win32gui.GetWindowText(hwnd)))
unique_name = 'unique name for finding this window'
real_name = hud.main_window.get_title()
hud.main_window.set_title(unique_name)
tl_windows = []
win32gui.EnumWindows(windowEnumerationHandler, tl_windows)
for w in tl_windows:
if w[1] == unique_name:
hud.main_window.gdkhandle = gtk.gdk.window_foreign_new(w[0])
hud.main_window.gdkhandle.set_transient_for(self.gdkhandle)
#
# style = win32gui.GetWindowLong(self.number, win32con.GWL_EXSTYLE)
# style |= win32con.WS_CLIPCHILDREN
# win32gui.SetWindowLong(self.number, win32con.GWL_EXSTYLE, style)
break
hud.main_window.set_title(real_name)
def win_enum_handler(hwnd, titles): def win_enum_handler(hwnd, titles):
titles[hwnd] = win32gui.GetWindowText(hwnd) titles[hwnd] = win32gui.GetWindowText(hwnd)
def topify_window(hud, window):
"""Set the specified gtk window to stayontop in MS Windows."""
def windowEnumerationHandler(hwnd, resultList):
'''Callback for win32gui.EnumWindows() to generate list of window handles.'''
resultList.append((hwnd, win32gui.GetWindowText(hwnd)))
unique_name = 'unique name for finding this window'
real_name = window.get_title()
window.set_title(unique_name)
tl_windows = []
win32gui.EnumWindows(windowEnumerationHandler, tl_windows)
for w in tl_windows:
if w[1] == unique_name:
hud.main_window.parentgdkhandle = gtk.gdk.window_foreign_new(long(hud.table.number))
hud.main_window.gdkhandle = gtk.gdk.window_foreign_new(w[0])
hud.main_window.gdkhandle.set_transient_for(hud.main_window.parentgdkhandle)
style = win32gui.GetWindowLong(self.table.number, win32con.GWL_EXSTYLE)
style |= win32con.WS_CLIPCHILDREN
win32gui.SetWindowLong(hud.table.number, win32con.GWL_EXSTYLE, style)
break
window.set_title(real_name)

View File

@ -17,15 +17,11 @@
import os import os
import sys import sys
from optparse import OptionParser import Options
import string
cl_options = string.join(sys.argv[1:])
parser = OptionParser() (options, sys.argv) = Options.fpdb_options()
parser.add_option("-x", "--errorsToConsole", action="store_true",
help="If passed error output will go to the console rather than .")
parser.add_option("-d", "--databaseName", dest="dbname", default="fpdb",
help="Overrides the default database name")
(options, sys.argv) = parser.parse_args()
if not options.errorsToConsole: if not options.errorsToConsole:
print "Note: error output is being diverted to fpdb-error-log.txt and HUD-error.txt. Any major error will be reported there _only_." print "Note: error output is being diverted to fpdb-error-log.txt and HUD-error.txt. Any major error will be reported there _only_."
@ -356,6 +352,7 @@ class fpdb:
else: else:
self.settings['os']="windows" self.settings['os']="windows"
self.settings.update({'cl_options': cl_options})
self.settings.update(self.config.get_db_parameters()) self.settings.update(self.config.get_db_parameters())
self.settings.update(self.config.get_tv_parameters()) self.settings.update(self.config.get_tv_parameters())
self.settings.update(self.config.get_import_parameters()) self.settings.update(self.config.get_import_parameters())
@ -486,7 +483,7 @@ This program is licensed under the AGPL3, see docs"""+os.sep+"agpl-3.0.txt")
def __init__(self): def __init__(self):
self.threads=[] self.threads=[]
self.db=None self.db=None
self.config = Configuration.Config(dbname=options.dbname) self.config = Configuration.Config(file=options.config, dbname=options.dbname)
self.load_profile() self.load_profile()
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)

View File

@ -578,7 +578,7 @@ class fpdb_db:
self.cursor.execute( "lock tables Hands write" ) self.cursor.execute( "lock tables Hands write" )
except: except:
# Table 'fpdb.hands' doesn't exist # Table 'fpdb.hands' doesn't exist
if str(sys.exc_value).find(".hands' doesn't exist") >= 0: if str(sys.exc_value).find(".Hands' doesn't exist") >= 0:
return(2) return(2)
print "Error! failed to obtain global lock. Close all programs accessing " \ print "Error! failed to obtain global lock. Close all programs accessing " \
+ "database (including fpdb) and try again (%s)." \ + "database (including fpdb) and try again (%s)." \
@ -596,12 +596,41 @@ class fpdb_db:
+ "database (including fpdb) and try again (%s)." \ + "database (including fpdb) and try again (%s)." \
% ( str(sys.exc_value).rstrip('\n'), ) % ( str(sys.exc_value).rstrip('\n'), )
return(1) return(1)
return(0) return(0)
def getLastInsertId(self):
if self.backend == self.MYSQL_INNODB:
ret = self.db.insert_id()
if ret < 1 or ret > 999999999:
print "getLastInsertId(): problem fetching insert_id? ret=", ret
ret = -1
elif self.backend == self.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)
self.cursor.execute ("SELECT lastval()")
row = self.cursor.fetchone()
if not row:
print "getLastInsertId(%s): problem fetching lastval? row=" % seq, row
ret = -1
else:
ret = row[0]
elif self.backend == self.SQLITE:
# don't know how to do this in sqlite
print "getLastInsertId(): not coded for sqlite yet"
ret = -1
else:
print "getLastInsertId(): unknown backend ", self.backend
ret = -1
return ret
def storeHand(self, p): def storeHand(self, p):
#stores into table hands: #stores into table hands:
self.cursor.execute ("""INSERT INTO Hands self.cursor.execute ("""INSERT INTO Hands
(siteHandNo, gametypeId, handStart, seats, tableName, importTime, maxSeats (siteHandNo, gametypeId, handStart, seats, tableName, importTime, maxSeats
,boardcard1, boardcard2, boardcard3, boardcard4, boardcard5
,playersVpi, playersAtStreet1, playersAtStreet2 ,playersVpi, playersAtStreet1, playersAtStreet2
,playersAtStreet3, playersAtStreet4, playersAtShowdown ,playersAtStreet3, playersAtStreet4, playersAtShowdown
,street0Raises, street1Raises, street2Raises ,street0Raises, street1Raises, street2Raises
@ -612,6 +641,7 @@ class fpdb_db:
VALUES VALUES
(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""" (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)"""
,(p['siteHandNo'], gametype_id, p['handStart'], len(names), p['tableName'], datetime.datetime.today(), p['maxSeats'] ,(p['siteHandNo'], gametype_id, p['handStart'], len(names), p['tableName'], datetime.datetime.today(), p['maxSeats']
,p['boardcard1'], ['boardcard2'], p['boardcard3'], ['boardcard4'], ['boardcard5']
,hudCache['playersVpi'], hudCache['playersAtStreet1'], hudCache['playersAtStreet2'] ,hudCache['playersVpi'], hudCache['playersAtStreet1'], hudCache['playersAtStreet2']
,hudCache['playersAtStreet3'], hudCache['playersAtStreet4'], hudCache['playersAtShowdown'] ,hudCache['playersAtStreet3'], hudCache['playersAtStreet4'], hudCache['playersAtShowdown']
,hudCache['street0Raises'], hudCache['street1Raises'], hudCache['street2Raises'] ,hudCache['street0Raises'], hudCache['street1Raises'], hudCache['street2Raises']

View File

@ -288,36 +288,34 @@ class Importer:
if os.path.isdir(file): if os.path.isdir(file):
self.addToDirList[file] = [site] + [filter] self.addToDirList[file] = [site] + [filter]
return return
if filter == "passthrough" or filter == "":
(stored, duplicates, partial, errors, ttime) = self.import_fpdb_file(file, site) conv = None
else: # Load filter, process file, pass returned filename to import_fpdb_file
conv = None
# Load filter, process file, pass returned filename to import_fpdb_file
print "\nConverting %s" % file print "\nConverting %s" % file
hhbase = self.config.get_import_parameters().get("hhArchiveBase") hhbase = self.config.get_import_parameters().get("hhArchiveBase")
hhbase = os.path.expanduser(hhbase) hhbase = os.path.expanduser(hhbase)
hhdir = os.path.join(hhbase,site) hhdir = os.path.join(hhbase,site)
try: try:
out_path = os.path.join(hhdir, file.split(os.path.sep)[-2]+"-"+os.path.basename(file)) out_path = os.path.join(hhdir, file.split(os.path.sep)[-2]+"-"+os.path.basename(file))
except: except:
out_path = os.path.join(hhdir, "x"+strftime("%d-%m-%y")+os.path.basename(file)) out_path = os.path.join(hhdir, "x"+strftime("%d-%m-%y")+os.path.basename(file))
filter_name = filter.replace("ToFpdb", "") filter_name = filter.replace("ToFpdb", "")
mod = __import__(filter) mod = __import__(filter)
obj = getattr(mod, filter_name, None) obj = getattr(mod, filter_name, None)
if callable(obj): if callable(obj):
conv = obj(in_path = file, out_path = out_path) conv = obj(in_path = file, out_path = out_path)
if(conv.getStatus()): if(conv.getStatus()):
(stored, duplicates, partial, errors, ttime) = self.import_fpdb_file(out_path, site) (stored, duplicates, partial, errors, ttime) = self.import_fpdb_file(out_path, site)
else:
# conversion didn't work
# TODO: appropriate response?
return (0, 0, 0, 1, 0)
else: else:
print "Unknown filter filter_name:'%s' in filter:'%s'" %(filter_name, filter) # conversion didn't work
return # TODO: appropriate response?
return (0, 0, 0, 1, 0)
else:
print "Unknown filter filter_name:'%s' in filter:'%s'" %(filter_name, filter)
return
#This will barf if conv.getStatus != True #This will barf if conv.getStatus != True
return (stored, duplicates, partial, errors, ttime) return (stored, duplicates, partial, errors, ttime)
@ -394,8 +392,8 @@ class Importer:
self.hand=hand self.hand=hand
try: try:
handsId = fpdb_parse_logic.mainParser(self.settings, self.fdb.db handsId = fpdb_parse_logic.mainParser(self.settings, self.fdb
,self.fdb.cursor, self.siteIds[site], category, hand, self.config) , self.siteIds[site], category, hand, self.config)
self.fdb.db.commit() self.fdb.db.commit()
stored += 1 stored += 1

View File

@ -18,11 +18,13 @@
#methods that are specific to holdem but not trivial #methods that are specific to holdem but not trivial
import fpdb_simple import fpdb_simple
import fpdb_save_to_db import Database
#parses a holdem hand #parses a holdem hand
def mainParser(settings, db, cursor, siteID, category, hand, config): def mainParser(settings, fdb, siteID, category, hand, config):
backend = settings['db-backend'] backend = settings['db-backend']
#This is redundant - hopefully fdb will be a Database object in an interation soon
db = Database.Database(config, 'fpdb', '')
category = fpdb_simple.recogniseCategory(hand[0]) category = fpdb_simple.recogniseCategory(hand[0])
base = "hold" if category == "holdem" or category == "omahahi" or category == "omahahilo" else "stud" base = "hold" if category == "holdem" or category == "omahahi" or category == "omahahilo" else "stud"
@ -47,7 +49,7 @@ def mainParser(settings, db, cursor, siteID, category, hand, config):
break break
#print "small blind line:",smallBlindLine #print "small blind line:",smallBlindLine
gametypeID = fpdb_simple.recogniseGametypeID(backend, db, cursor, hand[0], hand[smallBlindLine], siteID, category, isTourney) gametypeID = fpdb_simple.recogniseGametypeID(backend, fdb.db, fdb.cursor, hand[0], hand[smallBlindLine], siteID, category, isTourney)
if isTourney: if isTourney:
siteTourneyNo = fpdb_simple.parseTourneyNo(hand[0]) siteTourneyNo = fpdb_simple.parseTourneyNo(hand[0])
buyin = fpdb_simple.parseBuyin(hand[0]) buyin = fpdb_simple.parseBuyin(hand[0])
@ -58,9 +60,9 @@ def mainParser(settings, db, cursor, siteID, category, hand, config):
tourneyStartTime= handStartTime #todo: read tourney start time tourneyStartTime= handStartTime #todo: read tourney start time
rebuyOrAddon = fpdb_simple.isRebuyOrAddon(hand[0]) rebuyOrAddon = fpdb_simple.isRebuyOrAddon(hand[0])
tourneyTypeId = fpdb_simple.recogniseTourneyTypeId(cursor, siteID, buyin, fee, knockout, rebuyOrAddon) tourneyTypeId = fpdb_simple.recogniseTourneyTypeId(fdb.cursor, siteID, buyin, fee, knockout, rebuyOrAddon)
fpdb_simple.isAlreadyInDB(cursor, gametypeID, siteHandNo) fpdb_simple.isAlreadyInDB(fdb.cursor, gametypeID, siteHandNo)
hand = fpdb_simple.filterCrap(hand, isTourney) hand = fpdb_simple.filterCrap(hand, isTourney)
@ -74,7 +76,7 @@ def mainParser(settings, db, cursor, siteID, category, hand, config):
seatLines.append(line) seatLines.append(line)
names = fpdb_simple.parseNames(seatLines) names = fpdb_simple.parseNames(seatLines)
playerIDs = fpdb_simple.recognisePlayerIDs(cursor, names, siteID) playerIDs = fpdb_simple.recognisePlayerIDs(fdb.cursor, names, siteID)
tmp = fpdb_simple.parseCashesAndSeatNos(seatLines) tmp = fpdb_simple.parseCashesAndSeatNos(seatLines)
startCashes = tmp['startCashes'] startCashes = tmp['startCashes']
seatNos = tmp['seatNos'] seatNos = tmp['seatNos']
@ -113,7 +115,7 @@ def mainParser(settings, db, cursor, siteID, category, hand, config):
tableName = tableResult['tableName'] tableName = tableResult['tableName']
#print "before part5, antes:", antes #print "before part5, antes:", antes
#part 5: final preparations, then call fpdb_save_to_db.* with #part 5: final preparations, then call Database.* with
# the arrays as they are - that file will fill them. # the arrays as they are - that file will fill them.
fpdb_simple.convertCardValues(cardValues) fpdb_simple.convertCardValues(cardValues)
if base == "hold": if base == "hold":
@ -121,8 +123,8 @@ def mainParser(settings, db, cursor, siteID, category, hand, config):
fpdb_simple.convertBlindBet(actionTypes, actionAmounts) fpdb_simple.convertBlindBet(actionTypes, actionAmounts)
fpdb_simple.checkPositions(positions) fpdb_simple.checkPositions(positions)
cursor.execute("SELECT limitType FROM Gametypes WHERE id=%s",(gametypeID, )) fdb.cursor.execute("SELECT limitType FROM Gametypes WHERE id=%s",(gametypeID, ))
limit_type = cursor.fetchone()[0] limit_type = fdb.cursor.fetchone()[0]
fpdb_simple.convert3B4B(category, limit_type, actionTypes, actionAmounts) fpdb_simple.convert3B4B(category, limit_type, actionTypes, actionAmounts)
totalWinnings = sum(winnings) totalWinnings = sum(winnings)
@ -143,8 +145,8 @@ def mainParser(settings, db, cursor, siteID, category, hand, config):
payin_amounts = fpdb_simple.calcPayin(len(names), buyin, fee) payin_amounts = fpdb_simple.calcPayin(len(names), buyin, fee)
if base == "hold": if base == "hold":
result = fpdb_save_to_db.tourney_holdem_omaha( result = db.tourney_holdem_omaha(
config, settings, db, cursor, base, category, siteTourneyNo, buyin config, settings, fdb.db, fdb.cursor, base, category, siteTourneyNo, buyin
, fee, knockout, entries, prizepool, tourneyStartTime , fee, knockout, entries, prizepool, tourneyStartTime
, payin_amounts, ranks, tourneyTypeId, siteID, siteHandNo , payin_amounts, ranks, tourneyTypeId, siteID, siteHandNo
, gametypeID, handStartTime, names, playerIDs, startCashes , gametypeID, handStartTime, names, playerIDs, startCashes
@ -152,8 +154,8 @@ def mainParser(settings, db, cursor, siteID, category, hand, config):
, winnings, rakes, actionTypes, allIns, actionAmounts , winnings, rakes, actionTypes, allIns, actionAmounts
, actionNos, hudImportData, maxSeats, tableName, seatNos) , actionNos, hudImportData, maxSeats, tableName, seatNos)
elif base == "stud": elif base == "stud":
result = fpdb_save_to_db.tourney_stud( result = db.tourney_stud(
config, settings, db, cursor, base, category, siteTourneyNo config, settings, fdb.db, fdb.cursor, base, category, siteTourneyNo
, buyin, fee, knockout, entries, prizepool, tourneyStartTime , buyin, fee, knockout, entries, prizepool, tourneyStartTime
, payin_amounts, ranks, tourneyTypeId, siteID, siteHandNo , payin_amounts, ranks, tourneyTypeId, siteID, siteHandNo
, gametypeID, handStartTime, names, playerIDs, startCashes , gametypeID, handStartTime, names, playerIDs, startCashes
@ -164,23 +166,23 @@ def mainParser(settings, db, cursor, siteID, category, hand, config):
raise fpdb_simple.FpdbError("unrecognised category") raise fpdb_simple.FpdbError("unrecognised category")
else: else:
if base == "hold": if base == "hold":
result = fpdb_save_to_db.ring_holdem_omaha( result = db.ring_holdem_omaha(
config, settings, db, cursor, base, category, siteHandNo config, settings, fdb.db, fdb.cursor, base, category, siteHandNo
, gametypeID, handStartTime, names, playerIDs , gametypeID, handStartTime, names, playerIDs
, startCashes, positions, cardValues, cardSuits , startCashes, positions, cardValues, cardSuits
, boardValues, boardSuits, winnings, rakes , boardValues, boardSuits, winnings, rakes
, actionTypes, allIns, actionAmounts, actionNos , actionTypes, allIns, actionAmounts, actionNos
, hudImportData, maxSeats, tableName, seatNos) , hudImportData, maxSeats, tableName, seatNos)
elif base == "stud": elif base == "stud":
result = fpdb_save_to_db.ring_stud( result = db.ring_stud(
config, settings, db, cursor, base, category, siteHandNo, gametypeID config, settings, fdb.db, fdb.cursor, base, category, siteHandNo, gametypeID
, handStartTime, names, playerIDs, startCashes, antes , handStartTime, names, playerIDs, startCashes, antes
, cardValues, cardSuits, winnings, rakes, actionTypes, allIns , cardValues, cardSuits, winnings, rakes, actionTypes, allIns
, actionAmounts, actionNos, hudImportData, maxSeats, tableName , actionAmounts, actionNos, hudImportData, maxSeats, tableName
, seatNos) , seatNos)
else: else:
raise fpdb_simple.FpdbError ("unrecognised category") raise fpdb_simple.FpdbError ("unrecognised category")
db.commit() fdb.db.commit()
return result return result
#end def mainParser #end def mainParser

View File

@ -1,182 +0,0 @@
#!/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 <http://www.gnu.org/licenses/>.
#In the "official" distribution you can find the license in
#agpl-3.0.txt in the docs folder of the package.
#This file contains methods to store hands into the db. decides to move this
#into a seperate file since its ugly, fairly long and just generally in the way.
from time import time
import fpdb_simple
MYSQL_INNODB = 2
PGSQL = 3
SQLITE = 4
#fastStoreHudCache = False # set this to True to test the new storeHudCache routine
#
#saveActions = True # set this to False to avoid storing action data
# # Pros: speeds up imports
# # Cons: no action data is saved, so you need to keep the hand histories
# # variance not available on stats page
# # no graphs
#stores a stud/razz hand into the database
def ring_stud(config, settings, db, cursor, base, category, site_hand_no, gametype_id, hand_start_time
,names, player_ids, start_cashes, antes, card_values, card_suits, winnings, rakes
,action_types, allIns, action_amounts, actionNos, hudImportData, maxSeats, tableName
,seatNos):
backend = settings['db-backend']
import_options = config.get_import_parameters()
saveActions = False if import_options['saveActions'] == False else True
fastStoreHudCache = True if import_options['fastStoreHudCache'] == True else False
fpdb_simple.fillCardArrays(len(names), base, category, card_values, card_suits)
hands_id = fpdb_simple.storeHands(backend, db, cursor, site_hand_no, gametype_id
,hand_start_time, names, tableName, maxSeats)
#print "before calling store_hands_players_stud, antes:", antes
hands_players_ids = fpdb_simple.store_hands_players_stud(backend, db, cursor, hands_id, player_ids
,start_cashes, antes, card_values
,card_suits, winnings, rakes, seatNos)
if 'updateHudCache' not in settings or settings['updateHudCache'] != 'drop':
fpdb_simple.storeHudCache(backend, cursor, base, category, gametype_id, hand_start_time, player_ids, hudImportData)
if saveActions:
fpdb_simple.storeActions(cursor, hands_players_ids, action_types
,allIns, action_amounts, actionNos)
return hands_id
#end def ring_stud
def ring_holdem_omaha(config, settings, db, cursor, base, category, site_hand_no, gametype_id
,hand_start_time, names, player_ids, start_cashes, positions, card_values
,card_suits, board_values, board_suits, winnings, rakes, action_types, allIns
,action_amounts, actionNos, hudImportData, maxSeats, tableName, seatNos):
"""stores a holdem/omaha hand into the database"""
backend = settings['db-backend']
import_options = config.get_import_parameters()
saveActions = False if import_options['saveActions'] == False else True
fastStoreHudCache = True if import_options['fastStoreHudCache'] == True else False
# print "DEBUG: saveActions = '%s' fastStoreHudCache = '%s'"%(saveActions, fastStoreHudCache)
# print "DEBUG: import_options = ", import_options
t0 = time()
fpdb_simple.fillCardArrays(len(names), base, category, card_values, card_suits)
t1 = time()
fpdb_simple.fill_board_cards(board_values, board_suits)
t2 = time()
hands_id = fpdb_simple.storeHands(backend, db, cursor, site_hand_no, gametype_id
,hand_start_time, names, tableName, maxSeats, hudImportData)
t3 = time()
hands_players_ids = fpdb_simple.store_hands_players_holdem_omaha(
backend, db, cursor, category, hands_id, player_ids, start_cashes
, positions, card_values, card_suits, winnings, rakes, seatNos, hudImportData)
t4 = time()
#print "ring holdem, backend=%d" % backend
if 'updateHudCache' not in settings or settings['updateHudCache'] != 'drop':
if fastStoreHudCache:
fpdb_simple.storeHudCache2(backend, cursor, base, category, gametype_id, hand_start_time, player_ids, hudImportData)
else:
fpdb_simple.storeHudCache(backend, cursor, base, category, gametype_id, hand_start_time, player_ids, hudImportData)
t5 = time()
fpdb_simple.store_board_cards(cursor, hands_id, board_values, board_suits)
t6 = time()
if saveActions:
fpdb_simple.storeActions(cursor, hands_players_ids, action_types, allIns, action_amounts, actionNos)
t7 = time()
#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
def tourney_holdem_omaha(config, settings, db, cursor, base, category, siteTourneyNo, buyin, fee, knockout
,entries, prizepool, tourney_start, payin_amounts, ranks, tourneyTypeId
,siteId #end of tourney specific params
,site_hand_no, gametype_id, hand_start_time, names, player_ids
,start_cashes, positions, card_values, card_suits, board_values
,board_suits, winnings, rakes, action_types, allIns, action_amounts
,actionNos, hudImportData, maxSeats, tableName, seatNos):
"""stores a tourney holdem/omaha hand into the database"""
backend = settings['db-backend']
import_options = config.get_import_parameters()
saveActions = True if import_options['saveActions'] == True else False
fastStoreHudCache = True if import_options['fastStoreHudCache'] == True else False
fpdb_simple.fillCardArrays(len(names), base, category, card_values, card_suits)
fpdb_simple.fill_board_cards(board_values, board_suits)
tourney_id = fpdb_simple.store_tourneys(cursor, tourneyTypeId, siteTourneyNo, entries, prizepool, tourney_start)
tourneys_players_ids = fpdb_simple.store_tourneys_players(cursor, tourney_id, player_ids, payin_amounts, ranks, winnings)
hands_id = fpdb_simple.storeHands(backend, db, cursor, site_hand_no, gametype_id
,hand_start_time, names, tableName, maxSeats)
hands_players_ids = fpdb_simple.store_hands_players_holdem_omaha_tourney(
backend, db, cursor, category, hands_id, player_ids, start_cashes, positions
, card_values, card_suits, winnings, rakes, seatNos, tourneys_players_ids)
#print "tourney holdem, backend=%d" % backend
if 'updateHudCache' not in settings or settings['updateHudCache'] != 'drop':
if fastStoreHudCache:
fpdb_simple.storeHudCache2(backend, cursor, base, category, gametype_id, hand_start_time, player_ids, hudImportData)
else:
fpdb_simple.storeHudCache(backend, cursor, base, category, gametype_id, hand_start_time, player_ids, hudImportData)
fpdb_simple.store_board_cards(cursor, hands_id, board_values, board_suits)
if saveActions:
fpdb_simple.storeActions(cursor, hands_players_ids, action_types, allIns, action_amounts, actionNos)
return hands_id
#end def tourney_holdem_omaha
def tourney_stud(config, settings, db, cursor, base, category, siteTourneyNo, buyin, fee, knockout, entries
,prizepool, tourneyStartTime, payin_amounts, ranks, tourneyTypeId, siteId
,siteHandNo, gametypeId, handStartTime, names, playerIds, startCashes, antes
,cardValues, cardSuits, winnings, rakes, actionTypes, allIns, actionAmounts
,actionNos, hudImportData, maxSeats, tableName, seatNos):
#stores a tourney stud/razz hand into the database
backend = settings['db-backend']
import_options = config.get_import_parameters()
saveActions = True if import_options['saveActions'] == True else False
fastStoreHudCache = True if import_options['fastStoreHudCache'] == True else False
fpdb_simple.fillCardArrays(len(names), base, category, cardValues, cardSuits)
tourney_id = fpdb_simple.store_tourneys(cursor, tourneyTypeId, siteTourneyNo, entries, prizepool, tourneyStartTime)
tourneys_players_ids = fpdb_simple.store_tourneys_players(cursor, tourney_id, playerIds, payin_amounts, ranks, winnings)
hands_id = fpdb_simple.storeHands(backend, db, cursor, siteHandNo, gametypeId, handStartTime, names, tableName, maxSeats)
hands_players_ids = fpdb_simple.store_hands_players_stud_tourney(backend, db, cursor, hands_id
, playerIds, startCashes, antes, cardValues, cardSuits
, winnings, rakes, seatNos, tourneys_players_ids)
if 'updateHudCache' not in settings or settings['updateHudCache'] != 'drop':
fpdb_simple.storeHudCache(backend, cursor, base, category, gametypeId, hand_start_time, playerIds, hudImportData)
if saveActions:
fpdb_simple.storeActions(cursor, hands_players_ids, actionTypes, allIns, actionAmounts, actionNos)
return hands_id
#end def tourney_stud

View File

@ -1147,10 +1147,13 @@ card5Value, card5Suit) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""",
#end def store_board_cards #end def store_board_cards
def storeHands(backend, conn, cursor, site_hand_no, gametype_id def storeHands(backend, conn, cursor, site_hand_no, gametype_id
,hand_start_time, names, tableName, maxSeats, hudCache): ,hand_start_time, names, tableName, maxSeats, hudCache,
board_values, board_suits):
cards = [Card.cardFromValueSuit(v,s) for v,s in zip(board_values,board_suits)]
#stores into table hands: #stores into table hands:
cursor.execute ("""INSERT INTO Hands cursor.execute ("""INSERT INTO Hands
(siteHandNo, gametypeId, handStart, seats, tableName, importTime, maxSeats (siteHandNo, gametypeId, handStart, seats, tableName, importTime, maxSeats
,boardcard1,boardcard2,boardcard3,boardcard4,boardcard5
,playersVpi, playersAtStreet1, playersAtStreet2 ,playersVpi, playersAtStreet1, playersAtStreet2
,playersAtStreet3, playersAtStreet4, playersAtShowdown ,playersAtStreet3, playersAtStreet4, playersAtShowdown
,street0Raises, street1Raises, street2Raises ,street0Raises, street1Raises, street2Raises
@ -1159,9 +1162,11 @@ def storeHands(backend, conn, cursor, site_hand_no, gametype_id
,showdownPot ,showdownPot
) )
VALUES VALUES
(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s,
%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
""" """
, (site_hand_no, gametype_id, hand_start_time, len(names), tableName, datetime.datetime.today(), maxSeats , (site_hand_no, gametype_id, hand_start_time, len(names), tableName, datetime.datetime.today(), maxSeats
,cards[0], cards[1], cards[2], cards[3], cards[4]
,hudCache['playersVpi'], hudCache['playersAtStreet1'], hudCache['playersAtStreet2'] ,hudCache['playersVpi'], hudCache['playersAtStreet1'], hudCache['playersAtStreet2']
,hudCache['playersAtStreet3'], hudCache['playersAtStreet4'], hudCache['playersAtShowdown'] ,hudCache['playersAtStreet3'], hudCache['playersAtStreet4'], hudCache['playersAtShowdown']
,hudCache['street0Raises'], hudCache['street1Raises'], hudCache['street2Raises'] ,hudCache['street0Raises'], hudCache['street1Raises'], hudCache['street2Raises']
@ -1318,19 +1323,26 @@ def store_hands_players_stud(backend, conn, cursor, hands_id, player_ids, start_
result=[] result=[]
#print "before inserts in store_hands_players_stud, antes:", antes #print "before inserts in store_hands_players_stud, antes:", antes
for i in xrange(len(player_ids)): for i in xrange(len(player_ids)):
card1 = Card.cardFromValueSuit(card_values[i][0], card_suits[i][0])
card2 = Card.cardFromValueSuit(card_values[i][1], card_suits[i][1])
card3 = Card.cardFromValueSuit(card_values[i][2], card_suits[i][2])
card4 = Card.cardFromValueSuit(card_values[i][3], card_suits[i][3])
card5 = Card.cardFromValueSuit(card_values[i][4], card_suits[i][4])
card6 = Card.cardFromValueSuit(card_values[i][5], card_suits[i][5])
card7 = Card.cardFromValueSuit(card_values[i][6], card_suits[i][6])
cursor.execute ("""INSERT INTO HandsPlayers cursor.execute ("""INSERT INTO HandsPlayers
(handId, playerId, startCash, ante, (handId, playerId, startCash, ante, tourneyTypeId,
card1Value, card1Suit, card2Value, card2Suit, card1, card2,
card3Value, card3Suit, card4Value, card4Suit, card3, card4,
card5Value, card5Suit, card6Value, card6Suit, card5, card6,
card7Value, card7Suit, winnings, rake, seatNo) card7, winnings, rake, seatNo)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, VALUES (%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], 1,
(hands_id, player_ids[i], start_cashes[i], antes[i], card1, card2,
card_values[i][0], card_suits[i][0], card_values[i][1], card_suits[i][1], card3, card4,
card_values[i][2], card_suits[i][2], card_values[i][3], card_suits[i][3], card5, card6,
card_values[i][4], card_suits[i][4], card_values[i][5], card_suits[i][5], card7, winnings[i], rakes[i], seatNos[i]))
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])) #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(cursor.fetchall()[0][0])
result.append( getLastInsertId(backend, conn, cursor) ) result.append( getLastInsertId(backend, conn, cursor) )
@ -2114,189 +2126,6 @@ def storeHudCache(backend, cursor, base, category, gametypeId, hand_start_time,
# hard-code styleKey as 'A000000' (all-time cache, no key) for now # hard-code styleKey as 'A000000' (all-time cache, no key) for now
styleKey = 'A000000' styleKey = 'A000000'
#print "storeHudCache, len(playerIds)=", len(playerIds), " len(vpip)=" \
#, len(hudImportData['street0VPI']), " len(totprof)=", len(hudImportData['totalProfit'])
for player in xrange(len(playerIds)):
# Set up a clean row
row=[]
row.append(0)#blank for id
row.append(gametypeId)
row.append(playerIds[player])
row.append(len(playerIds))#seats
for i in xrange(len(hudImportData)+2):
row.append(0)
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_3BChance'][player]: row[9]+=1
if hudImportData['street0_3BDone'][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
# Try to do the update first:
num = cursor.execute("""UPDATE HudCache
SET HDs=HDs+%s, street0VPI=street0VPI+%s, street0Aggr=street0Aggr+%s,
street0_3BChance=street0_3BChance+%s, street0_3BDone=street0_3BDone+%s,
street1Seen=street1Seen+%s, street2Seen=street2Seen+%s, street3Seen=street3Seen+%s,
street4Seen=street4Seen+%s, sawShowdown=sawShowdown+%s,
street1Aggr=street1Aggr+%s, street2Aggr=street2Aggr+%s, street3Aggr=street3Aggr+%s,
street4Aggr=street4Aggr+%s, otherRaisedStreet1=otherRaisedStreet1+%s,
otherRaisedStreet2=otherRaisedStreet2+%s, otherRaisedStreet3=otherRaisedStreet3+%s,
otherRaisedStreet4=otherRaisedStreet4+%s,
foldToOtherRaisedStreet1=foldToOtherRaisedStreet1+%s, foldToOtherRaisedStreet2=foldToOtherRaisedStreet2+%s,
foldToOtherRaisedStreet3=foldToOtherRaisedStreet3+%s, foldToOtherRaisedStreet4=foldToOtherRaisedStreet4+%s,
wonWhenSeenStreet1=wonWhenSeenStreet1+%s, wonAtSD=wonAtSD+%s, stealAttemptChance=stealAttemptChance+%s,
stealAttempted=stealAttempted+%s, foldBbToStealChance=foldBbToStealChance+%s,
foldedBbToSteal=foldedBbToSteal+%s,
foldSbToStealChance=foldSbToStealChance+%s, foldedSbToSteal=foldedSbToSteal+%s,
street1CBChance=street1CBChance+%s, street1CBDone=street1CBDone+%s, street2CBChance=street2CBChance+%s,
street2CBDone=street2CBDone+%s, street3CBChance=street3CBChance+%s,
street3CBDone=street3CBDone+%s, street4CBChance=street4CBChance+%s, street4CBDone=street4CBDone+%s,
foldToStreet1CBChance=foldToStreet1CBChance+%s, foldToStreet1CBDone=foldToStreet1CBDone+%s,
foldToStreet2CBChance=foldToStreet2CBChance+%s, foldToStreet2CBDone=foldToStreet2CBDone+%s,
foldToStreet3CBChance=foldToStreet3CBChance+%s,
foldToStreet3CBDone=foldToStreet3CBDone+%s, foldToStreet4CBChance=foldToStreet4CBChance+%s,
foldToStreet4CBDone=foldToStreet4CBDone+%s, totalProfit=totalProfit+%s,
street1CheckCallRaiseChance=street1CheckCallRaiseChance+%s,
street1CheckCallRaiseDone=street1CheckCallRaiseDone+%s, street2CheckCallRaiseChance=street2CheckCallRaiseChance+%s,
street2CheckCallRaiseDone=street2CheckCallRaiseDone+%s, street3CheckCallRaiseChance=street3CheckCallRaiseChance+%s,
street3CheckCallRaiseDone=street3CheckCallRaiseDone+%s, street4CheckCallRaiseChance=street4CheckCallRaiseChance+%s,
street4CheckCallRaiseDone=street4CheckCallRaiseDone+%s
WHERE gametypeId+0=%s
AND playerId=%s
AND activeSeats=%s
AND position=%s
AND tourneyTypeId+0=%s
AND styleKey=%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], styleKey))
# Test statusmessage to see if update worked, do insert if not
#print "storehud2, upd num =", num
if ( (backend == PGSQL and cursor.statusmessage != "UPDATE 1")
or (backend == MYSQL_INNODB and num == 0) ):
#print "playerid before insert:",row[2]," num = ", num
cursor.execute("""INSERT INTO HudCache
(gametypeId, playerId, activeSeats, position, tourneyTypeId, styleKey,
HDs, street0VPI, street0Aggr, street0_3BChance, street0_3BDone,
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, %s)"""
, (row[1], row[2], row[3], row[4], row[5], styleKey, 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]) )
#print "hopefully inserted hud data line: ", cursor.statusmessage
# message seems to be "INSERT 0 1"
else:
#print "updated(2) hud data line"
pass
# else:
# print "todo: implement storeHudCache for stud base"
#end def storeHudCache
def storeHudCache2(backend, cursor, base, category, gametypeId, hand_start_time, playerIds, hudImportData):
"""Modified version aiming for more speed ..."""
# if (category=="holdem" or category=="omahahi" or category=="omahahilo"):
if use_date_in_hudcache:
#print "key =", "d%02d%02d%02d " % (hand_start_time.year-2000, hand_start_time.month, hand_start_time.day)
styleKey = "d%02d%02d%02d" % (hand_start_time.year-2000, hand_start_time.month, hand_start_time.day)
else:
# hard-code styleKey as 'A000000' (all-time cache, no key) for now
styleKey = 'A000000'
#print "storeHudCache2, len(playerIds)=", len(playerIds), " len(vpip)=" \ #print "storeHudCache2, len(playerIds)=", len(playerIds), " len(vpip)=" \
#, len(hudImportData['street0VPI']), " len(totprof)=", len(hudImportData['totalProfit']) #, len(hudImportData['street0VPI']), " len(totprof)=", len(hudImportData['totalProfit'])
for player in xrange(len(playerIds)): for player in xrange(len(playerIds)):