Coverage for Stats.py: 0%

784 statements  

« prev     ^ index     » next       coverage.py v7.6.3, created at 2024-10-15 19:33 +0000

1#!/usr/bin/env python 

2# -*- coding: utf-8 -*- 

3 

4"""Manage collecting and formatting of stats and tooltips.""" 

5# Copyright 2008-2011, Ray E. Barker 

6 

7# 

8# This program is free software; you can redistribute it and/or modify 

9# it under the terms of the GNU General Public License as published by 

10# the Free Software Foundation; either version 2 of the License, or 

11# (at your option) any later version. 

12# 

13# This program is distributed in the hope that it will be useful, 

14# but WITHOUT ANY WARRANTY; without even the implied warranty of 

15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

16# GNU General Public License for more details. 

17# 

18# You should have received a copy of the GNU General Public License 

19# along with this program; if not, write to the Free Software 

20# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 

21 

22######################################################################## 

23 

24# How to write a new stat: 

25# 0 Do not use a name like "xyz_2". Names ending in _ and a single digit are 

26# used to indicate the number of decimal places the user wants to see in the Hud. 

27# 1 You can see a listing of all the raw stats (e.g., from the HudCache table) 

28# by running Database.py as a stand along program. You need to combine 

29# those raw stats to get stats to present to the HUD. If you need more 

30# information than is in the HudCache table, then you have to write SQL. 

31# 2 The raw stats seen when you run Database.py are available in the Stats.py 

32# in the stat_dict dict. For example the number of vpips would be 

33# stat_dict[player]['vpip']. So the % vpip is 

34# float(stat_dict[player]['vpip'])/float(stat_dict[player]['n']). You can see how the 

35# keys of stat_dict relate to the column names in HudCache by inspecting 

36# the proper section of the SQL.py module. 

37# The stat_dict keys should be in lower case, i.e. vpip not VPIP, since 

38# postgres returns the column names in lower case. 

39# 3 You have to write a small function for each stat you want to add. See 

40# the vpip() function for example. This function has to be protected from 

41# exceptions, using something like the try:/except: paragraphs in vpip. 

42# 4 The name of the function has to be the same as the of the stat used 

43# in the config file. 

44# 5 The stat functions have a peculiar return value, which is outlined in 

45# the do_stat function. This format is useful for tool tips and maybe 

46# other stuff. 

47# 6 All stats receive two params (stat_dict and player) - if these parameters contain 

48# "None", the stat must return its description in tuple [5] and must not traceback 

49# 7 Stats needing values from the hand instance can find these in _global_hand_instance.foo 

50# attribute 

51 

52import L10n 

53 

54 

55# Standard Library modules 

56import sys 

57 

58 

59import re 

60 

61# FreePokerTools modules 

62import Configuration 

63import Database 

64 

65# import Charset 

66import Card 

67import Hand 

68 

69# String manipulation 

70import codecs 

71 

72 

73import logging 

74 

75if __name__ == "__main__": 

76 Configuration.set_logfile("fpdb-log.txt") 

77# logging has been set up in fpdb.py or HUD_main.py, use their settings: 

78log = logging.getLogger("db") 

79 

80re_Places = re.compile("_[0-9]$") 

81 

82encoder = codecs.lookup(Configuration.LOCALE_ENCODING) 

83_ = L10n.get_translation() 

84 

85 

86# Since tuples are immutable, we have to create a new one when 

87# overriding any decimal placements. Copy old ones and recreate the 

88# second value in tuple to specified format- 

89def __stat_override(decimals, stat_vals): 

90 """ 

91 Returns a tuple with the first element of `stat_vals` as a float, the second element as a string 

92 with `decimals` number of decimal places, and the remaining elements of `stat_vals`. 

93 

94 Parameters: 

95 - decimals (int): The number of decimal places to round the first element of `stat_vals`. 

96 - stat_vals (tuple): A tuple of values. 

97 

98 Returns: 

99 - tuple: A tuple with the first element of `stat_vals` as a float, the second element as a string 

100 with `decimals` number of decimal places, and the remaining elements of `stat_vals`. 

101 """ 

102 s = "%.*f" % (decimals, 100.0 * stat_vals[0]) 

103 return stat_vals[0], s, stat_vals[2], stat_vals[3], stat_vals[4], stat_vals[5] 

104 

105 

106def do_tip(widget, tip): 

107 """ 

108 Sets the tooltip of the given widget to the UTF-8 encoded version of the tip. 

109 

110 Parameters: 

111 - widget: The widget to set the tooltip for. 

112 - tip: The tip to encode and set as the tooltip. 

113 

114 Returns: 

115 - None 

116 """ 

117 _tip = str(tip) 

118 widget.setToolTip(_tip) 

119 

120 

121def do_stat(stat_dict, player=24, stat="vpip", hand_instance=None): 

122 """ 

123 Calculates a specific statistic for a given player in a hand. 

124 

125 Args: 

126 stat_dict (dict): A dictionary containing statistics for all players in the hand. 

127 player (int, optional): The player for whom to calculate the statistic. Defaults to 24. 

128 stat (str, optional): The statistic to calculate. Defaults to 'vpip'. 

129 hand_instance (object, optional): An instance of the hand. Defaults to None. 

130 

131 Returns: 

132 The calculated statistic for the player, or None if the statistic is not in the list of available statistics. 

133 

134 Note: 

135 The hand instance is not needed for many stat functions, so it is stored in a global variable to avoid having to conditionally pass the extra value. 

136 If the statistic name ends with an underscore followed by a number, it is overridden with the specified number of decimal places. 

137 The decimal place override assumes the raw result is a fraction (x/100), and manual decimal places only make sense for percentage values. 

138 The profit/100 hands (bb/BB) already default to three decimal places anyhow, so they are unlikely override candidates. 

139 """ 

140 # hand instance is not needed for many stat functions 

141 # so this optional parameter will be stored in a global 

142 # to avoid having to conditionally pass the extra value 

143 global _global_hand_instance 

144 _global_hand_instance = hand_instance 

145 

146 statname = stat 

147 match = re_Places.search(stat) 

148 if match: # override if necessary 

149 statname = stat[0:-2] 

150 

151 if statname not in STATLIST: 

152 return None 

153 

154 result = eval("%(stat)s(stat_dict, %(player)d)" % {"stat": statname, "player": player}) 

155 

156 # If decimal places have been defined, override result[1] 

157 # NOTE: decimal place override ALWAYS assumes the raw result is a 

158 # fraction (x/100); manual decimal places really only make sense for 

159 # percentage values. Also, profit/100 hands (bb/BB) already default 

160 # to three decimal places anyhow, so they are unlikely override 

161 # candidates. 

162 if match: 

163 places = int(stat[-1:]) 

164 result = __stat_override(places, result) 

165 return result 

166 

167 

168# OK, for reference the tuple returned by the stat is: 

169# 0 - The stat, raw, no formating, eg 0.33333333 

170# 1 - formatted stat with appropriate precision, eg. 33; shown in HUD 

171# 2 - formatted stat with appropriate precision, punctuation and a hint, eg v=33% 

172# 3 - same as #2 except name of stat instead of hint, eg vpip=33% 

173# 4 - the calculation that got the stat, eg 9/27 

174# 5 - the name of the stat, useful for a tooltip, eg vpip 

175 

176########################################### 

177# functions that return individual stats 

178 

179 

180def totalprofit(stat_dict, player): 

181 """ 

182 Calculates the total profit for a given player. 

183 

184 Args: 

185 stat_dict (dict): A dictionary containing player statistics. 

186 player (int): The player for whom to calculate the total profit. 

187 

188 Returns: 

189 tuple: A tuple containing the following values: 

190 - stat (float): The total profit divided by 100. 

191 - formatted_stat (str): The formatted total profit with two decimal places. 

192 - tp_formatted_stat (str): The formatted total profit with two decimal places and a hint. 

193 - tot_prof_formatted_stat (str): The formatted total profit with two decimal places and a hint. 

194 - str_stat (str): The total profit as a string. 

195 - stat_name (str): The name of the statistic. 

196 

197 If the 'net' key is not present in the stat_dict for the given player, or if the value cannot be converted to a float, 

198 the function returns a tuple with default values: 

199 - ('0', '$0.00', 'tp=0', 'totalprofit=0', '0', 'Total Profit') 

200 """ 

201 try: 

202 stat = float(stat_dict[player]["net"]) / 100 

203 return (stat / 100.0, "$%.2f" % stat, "tp=$%.2f" % stat, "tot_prof=$%.2f" % stat, str(stat), "Total Profit") 

204 except (KeyError, ValueError, TypeError): 

205 return ("0", "$0.00", "tp=0", "totalprofit=0", "0", "Total Profit") 

206 

207 

208def playername(stat_dict, player): 

209 """ 

210 Retrieves the player's screen name from the stat dictionary. 

211 

212 Args: 

213 stat_dict (dict): A dictionary containing player statistics. 

214 player (int): The player for whom to retrieve the screen name. 

215 

216 Returns: 

217 tuple: A tuple containing the player's screen name repeated five times and a constant 'Player name' at the end. If the player's screen name is not found, it returns an empty string five times followed by 'Player name'. 

218 """ 

219 try: 

220 return ( 

221 stat_dict[player]["screen_name"], 

222 stat_dict[player]["screen_name"], 

223 stat_dict[player]["screen_name"], 

224 stat_dict[player]["screen_name"], 

225 stat_dict[player]["screen_name"], 

226 "Player name", 

227 ) 

228 except (KeyError, ValueError, TypeError): 

229 return ("", "", "", "", "", "Player name") 

230 

231 

232def _calculate_end_stack(stat_dict, player, hand_instance): 

233 """ 

234 Calculate the end stack size for a given player in a hand instance. 

235 

236 Args: 

237 stat_dict (dict): A dictionary containing player statistics. 

238 player (int): The player for whom to calculate the end stack size. 

239 hand_instance (Hand): An instance of the Hand class representing the current hand. 

240 

241 Returns: 

242 float: The end stack size for the given player. 

243 

244 Note: 

245 This function is currently located in Stats.py but it should be moved to Hands.py since it belongs there. 

246 

247 Todo: 

248 - Find a better way to calculate the end stack size from the hand_instance. 

249 - Add a hand_instance "end_of_hand_stack" attribute. 

250 """ 

251 # fixme - move this code into Hands.py - it really belongs there 

252 

253 # To reflect the end-of-hand position, we need a end-stack calculation 

254 # fixme, is there an easier way to do this from the hand_instance??? 

255 # can't seem to find a hand_instance "end_of_hand_stack" attribute 

256 

257 # First, find player stack size at the start of the hand 

258 stack = 0.0 

259 for item in hand_instance.players: 

260 if item[1] == stat_dict[player]["screen_name"]: 

261 stack = float(item[2]) 

262 

263 # Next, deduct all action from this player 

264 for street in hand_instance.bets: 

265 for item in hand_instance.bets[street]: 

266 if item == stat_dict[player]["screen_name"]: 

267 for amount in hand_instance.bets[street][stat_dict[player]["screen_name"]]: 

268 stack -= float(amount) 

269 

270 # Next, add back any money returned 

271 for p in hand_instance.pot.returned: 

272 if p == stat_dict[player]["screen_name"]: 

273 stack += float(hand_instance.pot.returned[p]) 

274 

275 # Finally, add back any winnings 

276 for item in hand_instance.collectees: 

277 if item == stat_dict[player]["screen_name"]: 

278 stack += float(hand_instance.collectees[item]) 

279 return stack 

280 

281 

282def m_ratio(stat_dict, player): 

283 """ 

284 Calculate the M-ratio for a player in a tournament. 

285 

286 Args: 

287 stat_dict (dict): A dictionary containing player statistics. 

288 player (int): The player for whom to calculate the M-ratio. 

289 

290 Returns: 

291 tuple: A tuple containing the M-ratio value and formatted strings. 

292 

293 Note: 

294 This function calculates the M-ratio using the end-of-hand stack count versus the hand's antes/blinds. 

295 """ 

296 # Tournament M-ratio calculation 

297 # Using the end-of-hand stack count vs. that hand's antes/blinds 

298 

299 # sum all blinds/antes 

300 stat = 0.0 

301 compulsory_bets = 0.0 

302 hand_instance = _global_hand_instance 

303 

304 if not hand_instance: 

305 return ( 

306 (stat / 100.0), 

307 "%d" % (int(stat)), 

308 "M=%d" % (int(stat)), 

309 "M=%d" % (int(stat)), 

310 "(%d)" % (int(stat)), 

311 "M ratio", 

312 ) 

313 

314 for p in hand_instance.bets["BLINDSANTES"]: 

315 for i in hand_instance.bets["BLINDSANTES"][p]: 

316 compulsory_bets += float(i) 

317 compulsory_bets += float(hand_instance.gametype.get("sb", 0)) # Ensure "sb" key exists 

318 compulsory_bets += float(hand_instance.gametype.get("bb", 0)) # Ensure "bb" key exists 

319 

320 stack = _calculate_end_stack(stat_dict, player, hand_instance) 

321 

322 if compulsory_bets != 0: # Check if compulsory_bets is non-zero to avoid division by zero 

323 stat = stack / compulsory_bets 

324 else: 

325 stat = 0 # Default to 0 if compulsory_bets is zero 

326 

327 return ( 

328 (int(stat)), 

329 "%d" % (int(stat)), 

330 "M=%d" % (int(stat)), 

331 "M=%d" % (int(stat)), 

332 "(%d)" % (int(stat)), 

333 "M ratio", 

334 ) 

335 

336 

337def bbstack(stat_dict, player): 

338 """ 

339 Calculate the tournament stack size in Big Blinds. 

340 

341 Args: 

342 stat_dict (dict): A dictionary containing player statistics. 

343 player (int): The player for whom to calculate the stack size. 

344 

345 Returns: 

346 tuple: A tuple containing the stack size in Big Blinds and related information. 

347 

348 Note: 

349 This function calculates the stack size in Big Blinds based on the end of hand stack count and the current Big Blind limit. 

350 """ 

351 # Tournament Stack calculation in Big Blinds 

352 # Result is end of hand stack count / Current Big Blind limit 

353 stat = 0.0 

354 hand_instance = _global_hand_instance 

355 if not (hand_instance): 

356 return (stat, "NA", "v=NA", "vpip=NA", "(0/0)", "bb stack") 

357 

358 # current big blind limit 

359 

360 current_bigblindlimit = 0 

361 current_bigblindlimit += float(hand_instance.gametype["bb"]) 

362 

363 stack = _calculate_end_stack(stat_dict, player, hand_instance) 

364 

365 if current_bigblindlimit != 0: 

366 stat = stack / current_bigblindlimit 

367 else: 

368 stat = 0 

369 

370 return ( 

371 (stat / 100.0), 

372 "%d" % (int(stat)), 

373 "bb's=%d" % (int(stat)), 

374 "#bb's=%d" % (int(stat)), 

375 "(%d)" % (int(stat)), 

376 "bb stack", 

377 ) 

378 

379 

380def playershort(stat_dict, player): 

381 """ 

382 Retrieves the shortened screen name of a player from the given stat_dict. 

383 

384 Args: 

385 stat_dict (dict): A dictionary containing player statistics. 

386 player (int): The player for whom to retrieve the shortened screen name. 

387 

388 Returns: 

389 tuple: A tuple containing the shortened screen name and related information. 

390 

391 Raises: 

392 KeyError: If the player's screen name is not found in the stat_dict. 

393 

394 Note: 

395 If the length of the screen name is greater than 6, it is truncated to 5 characters and a dot is appended. 

396 The returned tuple contains the shortened screen name repeated 5 times and the player's full screen name. 

397 """ 

398 try: 

399 r = stat_dict[player]["screen_name"] 

400 except (KeyError, ValueError, TypeError): 

401 return ("", "", "", "", "", ("Player Name") + " 1-5") 

402 

403 if len(r) > 6: 

404 r = r[:5] + "." 

405 return (r, r, r, r, stat_dict[player]["screen_name"], ("Player Name") + " 1-5") 

406 

407 

408def vpip(stat_dict, player): 

409 """ 

410 A function to calculate and return VPIP (Voluntarily Put In Pot) percentage. 

411 

412 Args: 

413 stat_dict (dict): A dictionary containing player statistics. 

414 player (str): The player for whom to calculate the VPIP. 

415 

416 Returns: 

417 tuple: A tuple containing: 

418 - VPIP percentage (float) 

419 - VPIP percentage formatted as a string 

420 - 'v=' followed by VPIP percentage formatted as a percentage string 

421 - 'vpip=' followed by VPIP percentage formatted as a percentage string 

422 - '(x/y)' where x is the VPIP and y is VPIP opportunities 

423 - 'Voluntarily put in preflop/3rd street %' 

424 

425 If an error occurs, returns: 

426 - 0.0 

427 - 'NA' 

428 - 'v=NA' 

429 - 'vpip=NA' 

430 - '(0/0)' 

431 - 'Voluntarily put in preflop/3rd street %' 

432 """ 

433 stat = 0.0 

434 try: 

435 vpip_opp = float(stat_dict[player].get("vpip_opp", 0)) # Ensure key exists 

436 if vpip_opp != 0: # Check if vpip_opp is non-zero to avoid division by zero 

437 stat = float(stat_dict[player]["vpip"]) / vpip_opp 

438 else: 

439 stat = 0 # Default to 0 if vpip_opp is zero 

440 

441 return ( 

442 stat, 

443 "%3.1f" % (100.0 * stat), 

444 "v=%3.1f%%" % (100.0 * stat), 

445 "vpip=%3.1f%%" % (100.0 * stat), 

446 "(%d/%d)" % (stat_dict[player]["vpip"], vpip_opp), 

447 "Voluntarily put in preflop/3rd street %", 

448 ) 

449 except (KeyError, ValueError, TypeError): 

450 return (stat, "NA", "v=NA", "vpip=NA", "(0/0)", "Voluntarily put in preflop/3rd street %") 

451 

452 

453def pfr(stat_dict, player): 

454 """ 

455 Calculate and return the preflop raise percentage (pfr) for a player. 

456 

457 Args: 

458 stat_dict (dict): A dictionary containing player statistics. 

459 player (int): The player for whom the pfr is calculated. 

460 

461 Returns: 

462 tuple: A tuple containing the pfr value, formatted pfr percentages, and related information. 

463 """ 

464 stat = 0.0 

465 try: 

466 pfr_opp = float(stat_dict[player].get("pfr_opp", 0)) # Ensure key exists 

467 if pfr_opp != 0: # Check if pfr_opp is non-zero to avoid division by zero 

468 stat = float(stat_dict[player]["pfr"]) / pfr_opp 

469 else: 

470 stat = 0 # Default to 0 if pfr_opp is zero 

471 

472 return ( 

473 stat, 

474 "%3.1f" % (100.0 * stat), 

475 "p=%3.1f%%" % (100.0 * stat), 

476 "pfr=%3.1f%%" % (100.0 * stat), 

477 "(%d/%d)" % (stat_dict[player]["pfr"], pfr_opp), 

478 "Preflop/3rd street raise %", 

479 ) 

480 except (KeyError, ValueError, TypeError): 

481 return (stat, "NA", "p=NA", "pfr=NA", "(0/0)", "Preflop/3rd street raise %") 

482 

483 

484def wtsd(stat_dict, player): 

485 """ 

486 Calculate and return the percentage of hands where a player went to showdown when seen flop/4th street. 

487 

488 Args: 

489 stat_dict (dict): A dictionary containing player statistics. 

490 player (int): The player for whom the percentage is calculated. 

491 

492 Returns: 

493 tuple: A tuple containing the percentage value, formatted percentage percentages, and related information. 

494 """ 

495 stat = 0.0 

496 try: 

497 saw_f = float(stat_dict[player].get("saw_f", 0)) # Ensure key exists 

498 if saw_f != 0: # Check if saw_f is non-zero to avoid division by zero 

499 stat = float(stat_dict[player]["sd"]) / saw_f 

500 else: 

501 stat = 0 # Default to 0 if saw_f is zero 

502 

503 return ( 

504 stat, 

505 "%3.1f" % (100.0 * stat), 

506 "w=%3.1f%%" % (100.0 * stat), 

507 "wtsd=%3.1f%%" % (100.0 * stat), 

508 "(%d/%d)" % (stat_dict[player]["sd"], saw_f), 

509 "% went to showdown when seen flop/4th street", 

510 ) 

511 except (KeyError, ValueError, TypeError): 

512 return (stat, "NA", "w=NA", "wtsd=NA", "(0/0)", "% went to showdown when seen flop/4th street") 

513 

514 

515def wmsd(stat_dict, player): 

516 """ 

517 Calculate and return the win money at showdown (wmsd) statistics for a player. 

518 

519 Args: 

520 stat_dict (dict): A dictionary containing player statistics. 

521 player (int): The player for whom the wmsd is calculated. 

522 

523 Returns: 

524 tuple: A tuple containing the wmsd value, formatted wmsd percentages, and related information. 

525 """ 

526 stat = 0.0 

527 try: 

528 sd = float(stat_dict[player].get("sd", 0)) # Ensure key exists 

529 if sd != 0: # Check if sd is non-zero to avoid division by zero 

530 stat = float(stat_dict[player]["wmsd"]) / sd 

531 else: 

532 stat = 0 # Default to 0 if sd is zero 

533 

534 return ( 

535 stat, 

536 "%3.1f" % (100.0 * stat), 

537 "w=%3.1f%%" % (100.0 * stat), 

538 "wmsd=%3.1f%%" % (100.0 * stat), 

539 "(%5.1f/%d)" % (float(stat_dict[player]["wmsd"]), sd), 

540 "% won some money at showdown", 

541 ) 

542 except (KeyError, ValueError, TypeError): 

543 return (stat, "NA", "w=NA", "wmsd=NA", "(0/0)", "% won some money at showdown") 

544 

545 

546# Money is stored as pennies, so there is an implicit 100-multiplier 

547# already in place 

548def profit100(stat_dict, player): 

549 """ 

550 Calculate the profit per 100 hands for a given player. 

551 

552 Args: 

553 stat_dict (dict): A dictionary containing player statistics. 

554 player (int): The player for whom the profit per 100 hands is calculated. 

555 

556 Returns: 

557 tuple: A tuple containing the profit per 100 hands value, formatted profit percentages, and related information. 

558 

559 Notes: 

560 - The profit per 100 hands is calculated by dividing the net winnings by the number of hands played. 

561 - If an exception occurs during the calculation, the function returns a tuple with default values. 

562 """ 

563 stat = 0.0 

564 try: 

565 n = float(stat_dict[player].get("n", 0)) # Ensure key exists 

566 if n != 0: # Check if 'n' (number of hands) is non-zero 

567 stat = float(stat_dict[player]["net"]) / n 

568 else: 

569 stat = 0 # Default to 0 if 'n' is zero 

570 

571 return ( 

572 stat / 100.0, 

573 "%.2f" % (stat), 

574 "p=%.2f" % (stat), 

575 "p/100=%.2f" % (stat), 

576 "%d/%d" % (stat_dict[player]["net"], n), 

577 "Profit per 100 hands", 

578 ) 

579 

580 except (KeyError, ValueError, TypeError): 

581 if stat_dict: 

582 log.error( 

583 "exception calculating profit100: 100 * %d / %d" % (stat_dict[player]["net"], stat_dict[player]["n"]) 

584 ) 

585 return (stat, "NA", "p=NA", "p/100=NA", "(0/0)", "Profit per 100 hands") 

586 

587 

588def bbper100(stat_dict, player): 

589 """ 

590 Calculate the number of big blinds won per 100 hands for a given player. 

591 

592 Args: 

593 stat_dict (dict): A dictionary containing player statistics. 

594 player (int): The player for whom the number of big blinds won per 100 hands is calculated. 

595 

596 Returns: 

597 tuple: A tuple containing the number of big blinds won per 100 hands value, formatted values, and related information. 

598 """ 

599 stat = 0.0 

600 try: 

601 bigblind = float(stat_dict[player].get("bigblind", 0)) # Ensure key exists 

602 if bigblind != 0: # Check if 'bigblind' is non-zero 

603 stat = 100.0 * float(stat_dict[player]["net"]) / bigblind 

604 else: 

605 stat = 0 # Default to 0 if 'bigblind' is zero 

606 

607 return ( 

608 stat / 100.0, 

609 "%5.3f" % (stat), 

610 "bb100=%5.3f" % (stat), 

611 "bb100=%5.3f" % (stat), 

612 "(%d,%d)" % (100 * stat_dict[player]["net"], bigblind), 

613 "Big blinds won per 100 hands", 

614 ) 

615 

616 except (KeyError, ValueError, TypeError): 

617 if stat_dict: 

618 log.info( 

619 "exception calculating bbper100: 100 * %d / %d" 

620 % (stat_dict[player]["net"], stat_dict[player]["bigblind"]) 

621 ) 

622 return (stat, "NA", "bb100=NA", "bb100=NA", "(--)", "Big blinds won per 100 hands") 

623 

624 

625def BBper100(stat_dict, player): 

626 """ 

627 Calculate the number of big bets won per 100 hands for a given player. 

628 

629 Args: 

630 stat_dict (dict): A dictionary containing player statistics. 

631 player (int): The player for whom the number of big bets won per 100 hands is calculated. 

632 

633 Returns: 

634 tuple: A tuple containing the number of big bets won per 100 hands value, formatted values, and related information. 

635 

636 Notes: 

637 - The number of big bets won per 100 hands is calculated by dividing the net winnings by the big blind value, multiplied by 50. 

638 - If an exception occurs during the calculation, the function returns a tuple with default values. 

639 """ 

640 stat = 0.0 

641 try: 

642 bigblind = float(stat_dict[player].get("bigblind", 0)) # Ensure key exists 

643 if bigblind != 0: # Check if 'bigblind' is non-zero 

644 stat = 50 * float(stat_dict[player]["net"]) / bigblind 

645 else: 

646 stat = 0 # Default to 0 if 'bigblind' is zero 

647 

648 return ( 

649 stat / 100.0, 

650 "%5.3f" % (stat), 

651 "BB100=%5.3f" % (stat), 

652 "BB100=%5.3f" % (stat), 

653 "(%d,%d)" % (100 * stat_dict[player]["net"], 2 * bigblind), 

654 "Big bets won per 100 hands", 

655 ) 

656 

657 except (KeyError, ValueError, TypeError): 

658 if stat_dict: 

659 log.info("exception calculating BBper100: " + str(stat_dict[player])) 

660 return (stat, "NA", "BB100=NA", "BB100=NA", "(--)", "Big bets won per 100 hands") 

661 

662 

663def saw_f(stat_dict, player): 

664 """Calculate the saw flop percentage for a given player. 

665 

666 Args: 

667 stat_dict (dict): A dictionary containing player statistics. 

668 player (int): The player for whom the saw flop percentage is calculated. 

669 

670 Returns: 

671 tuple: A tuple containing the saw flop percentage in various formats. 

672 - The saw flop percentage as a float. 

673 - The saw flop percentage formatted to one decimal place. 

674 - The saw flop percentage with a label. 

675 - The saw flop percentage with a different label. 

676 - The count of times saw flop divided by total count. 

677 - A description of the statistic. 

678 

679 If an error occurs during calculation, default values are returned. 

680 """ 

681 try: 

682 num = float(stat_dict[player]["saw_f"]) 

683 den = float(stat_dict[player]["n"]) 

684 stat = num / den 

685 return ( 

686 stat, 

687 "%3.1f" % (100.0 * stat), 

688 "sf=%3.1f%%" % (100.0 * stat), 

689 "saw_f=%3.1f%%" % (100.0 * stat), 

690 "(%d/%d)" % (stat_dict[player]["saw_f"], stat_dict[player]["n"]), 

691 "Flop/4th street seen %", 

692 ) 

693 

694 except (KeyError, ValueError, TypeError): 

695 stat = 0.0 

696 return (stat, "NA", "sf=NA", "saw_f=NA", "(0/0)", "Flop/4th street seen %") 

697 

698 

699def n(stat_dict, player): 

700 """ 

701 Calculate and format the number of hands seen for a player. 

702 

703 Args: 

704 stat_dict (dict): A dictionary containing player statistics. 

705 player (int): The player for whom the number of hands seen is calculated. 

706 

707 Returns: 

708 tuple: A tuple containing formatted strings representing the number of hands seen in different ways. 

709 """ 

710 try: 

711 # If sample is large enough, use X.Yk notation instead 

712 _n = stat_dict[player]["n"] 

713 fmt = "%d" % _n 

714 if _n >= 10000: 

715 k = _n / 1000 

716 c = _n % 1000 

717 _c = float(c) / 100.0 

718 d = int(round(_c)) 

719 if d == 10: 

720 k += 1 

721 d = 0 

722 fmt = "%d.%dk" % (k, d) 

723 return ( 

724 stat_dict[player]["n"], 

725 "%s" % fmt, 

726 "n=%d" % (stat_dict[player]["n"]), 

727 "n=%d" % (stat_dict[player]["n"]), 

728 "(%d)" % (stat_dict[player]["n"]), 

729 "Number of hands seen", 

730 ) 

731 

732 except (KeyError, ValueError, TypeError): 

733 # Number of hands shouldn't ever be "NA"; zeroes are better here 

734 return (0, "%d" % (0), "n=%d" % (0), "n=%d" % (0), "(%d)" % (0), "Number of hands seen") 

735 

736 

737def steal(stat_dict, player): 

738 """ 

739 Calculate and format the steal percentage for a player. 

740 

741 Args: 

742 stat_dict (dict): A dictionary containing player statistics. 

743 player (int): The player for whom the steal percentage is calculated. 

744 

745 Returns: 

746 tuple: A tuple containing formatted strings representing the steal percentage in different ways. 

747 - stat (float): The steal percentage. 

748 - '%3.1f' (str): The steal percentage formatted as a string with 3 decimal places. 

749 - 'st=%3.1f%%' (str): The steal percentage formatted as a string with 3 decimal places and a percentage sign. 

750 - 'steal=%3.1f%%' (str): The steal percentage formatted as a string with 3 decimal places and a percentage sign. 

751 - '(%d/%d)' (str): The steal count and steal opponent count formatted as a string. 

752 - '% steal attempted' (str): The description of the steal percentage. 

753 

754 Raises: 

755 None 

756 

757 Notes: 

758 - The steal percentage is calculated by dividing the steal count by the steal opponent count. 

759 - If any of the required statistics are missing, the function returns default values. 

760 """ 

761 stat = 0.0 

762 try: 

763 steal_opp = float(stat_dict[player].get("steal_opp", 0)) # Ensure key exists 

764 if steal_opp != 0: # Check if steal_opp is non-zero to avoid division by zero 

765 stat = float(stat_dict[player]["steal"]) / steal_opp 

766 else: 

767 stat = 0 # Default to 0 if steal_opp is zero 

768 

769 return ( 

770 stat, 

771 "%3.1f" % (100.0 * stat), 

772 "st=%3.1f%%" % (100.0 * stat), 

773 "steal=%3.1f%%" % (100.0 * stat), 

774 "(%d/%d)" % (stat_dict[player]["steal"], steal_opp), 

775 "% steal attempted", 

776 ) 

777 

778 except (KeyError, ValueError, TypeError): 

779 return (stat, "NA", "st=NA", "steal=NA", "(0/0)", "% steal attempted") 

780 

781 

782def s_steal(stat_dict, player): 

783 """ 

784 Calculate and format the steal success percentage for a player. 

785 

786 Args: 

787 stat_dict (dict): A dictionary containing player statistics. 

788 player (int): The player for whom the steal success percentage is calculated. 

789 

790 Returns: 

791 tuple: A tuple containing formatted strings representing the steal success percentage in different ways. 

792 - stat (float): The steal success percentage. 

793 - '%3.1f' (str): The steal success percentage formatted as a string with 3 decimal places. 

794 - 's_st=%3.1f%%' (str): The steal success percentage formatted with a specific label. 

795 - 's_steal=%3.1f%%' (str): The steal success percentage formatted with a specific label. 

796 - '(%d/%d)' (str): The steal success count and total steal count formatted as a string. 

797 - '% steal success' (str): The description of the steal success percentage. 

798 

799 Raises: 

800 None 

801 """ 

802 stat = 0.0 

803 try: 

804 if float(stat_dict[player]["steal"]) != 0: 

805 stat = float(stat_dict[player]["suc_st"]) / float(stat_dict[player]["steal"]) 

806 return ( 

807 stat, 

808 "%3.1f" % (100.0 * stat), 

809 "s_st=%3.1f%%" % (100.0 * stat), 

810 "s_steal=%3.1f%%" % (100.0 * stat), 

811 "(%d/%d)" % (stat_dict[player]["suc_st"], stat_dict[player]["steal"]), 

812 "% steal success", 

813 ) 

814 

815 except (KeyError, ValueError, TypeError): 

816 return (stat, "NA", "s_st=NA", "s_steal=NA", "(0/0)", "% steal success") 

817 

818 

819def f_SB_steal(stat_dict, player): 

820 """ 

821 Calculate the folded Small Blind (SB) to steal statistics for a player. 

822 

823 Args: 

824 stat_dict (dict): A dictionary containing player statistics. 

825 player (int): The player for whom the statistics are calculated. 

826 

827 Returns: 

828 tuple: A tuple containing the folded SB to steal statistics. 

829 - stat (float): The folded SB to steal percentage. 

830 - '%3.1f' (str): The folded SB to steal percentage formatted as a string with 3 decimal places. 

831 - 'fSB=%3.1f%%' (str): The folded SB to steal percentage formatted with a specific label. 

832 - 'fSB_s=%3.1f%%' (str): The folded SB to steal percentage formatted with a specific label. 

833 - '(%d/%d)' (str): The number of folded SB to steal and the total number of folded SB formatted as a string. 

834 - '% folded SB to steal' (str): The description of the folded SB to steal percentage. 

835 

836 Raises: 

837 None 

838 """ 

839 stat = 0.0 

840 try: 

841 if float(stat_dict[player]["sbstolen"]) != 0: 

842 stat = float(stat_dict[player]["sbnotdef"]) / float(stat_dict[player]["sbstolen"]) 

843 return ( 

844 stat, 

845 "%3.1f" % (100.0 * stat), 

846 "fSB=%3.1f%%" % (100.0 * stat), 

847 "fSB_s=%3.1f%%" % (100.0 * stat), 

848 "(%d/%d)" % (stat_dict[player]["sbnotdef"], stat_dict[player]["sbstolen"]), 

849 "% folded SB to steal", 

850 ) 

851 except (KeyError, ValueError, TypeError): 

852 return (stat, "NA", "fSB=NA", "fSB_s=NA", "(0/0)", "% folded SB to steal") 

853 

854 

855def f_BB_steal(stat_dict, player): 

856 """Calculate the folded Big Blind (BB) to steal statistics for a player. 

857 

858 Args: 

859 stat_dict (dict): A dictionary containing player statistics. 

860 player (int): The player for whom the statistics are calculated. 

861 

862 Returns: 

863 tuple: A tuple containing the calculated statistics in different formats: 

864 - Float: The calculated statistic. 

865 - String: The statistic formatted as a percentage with one decimal place. 

866 - String: A formatted string representing the statistic. 

867 - String: A formatted string representing the statistic with a suffix. 

868 - String: A formatted string showing the count of BB not defended and BB stolen. 

869 - String: A description of the statistic. 

870 

871 If an exception occurs during the calculation, returns default values for the statistics. 

872 """ 

873 stat = 0.0 

874 try: 

875 if float(stat_dict[player]["bbstolen"]) != 0: 

876 stat = float(stat_dict[player]["bbnotdef"]) / float(stat_dict[player]["bbstolen"]) 

877 return ( 

878 stat, 

879 "%3.1f" % (100.0 * stat), 

880 "fBB=%3.1f%%" % (100.0 * stat), 

881 "fBB_s=%3.1f%%" % (100.0 * stat), 

882 "(%d/%d)" % (stat_dict[player]["bbnotdef"], stat_dict[player]["bbstolen"]), 

883 "% folded BB to steal", 

884 ) 

885 except (KeyError, ValueError, TypeError): 

886 return (stat, "NA", "fBB=NA", "fBB_s=NA", "(0/0)", "% folded BB to steal") 

887 

888 

889def f_steal(stat_dict, player): 

890 """ 

891 Calculate the folded blind to steal statistics for a player. 

892 

893 Args: 

894 stat_dict (dict): A dictionary containing player statistics. 

895 player (int): The player for whom the statistics are calculated. 

896 

897 Returns: 

898 tuple: A tuple containing the calculated statistics in different formats: 

899 - Float: The calculated statistic. 

900 - String: The statistic formatted as a percentage with one decimal place. 

901 - String: A formatted string representing the statistic. 

902 - String: A formatted string representing the statistic with a suffix. 

903 - String: A formatted string showing the count of folded blind not defended and blind stolen. 

904 - String: A description of the statistic. 

905 

906 If an exception occurs during the calculation, returns default values for the statistics. 

907 """ 

908 stat = 0.0 

909 try: 

910 folded_blind = stat_dict[player].get("sbnotdef", 0) + stat_dict[player].get("bbnotdef", 0) 

911 blind_stolen = stat_dict[player].get("sbstolen", 0) + stat_dict[player].get("bbstolen", 0) 

912 

913 if blind_stolen != 0: # Check if blind_stolen is non-zero to avoid division by zero 

914 stat = float(folded_blind) / float(blind_stolen) 

915 

916 return ( 

917 stat, 

918 "%3.1f" % (100.0 * stat), 

919 "fB=%3.1f%%" % (100.0 * stat), 

920 "fB_s=%3.1f%%" % (100.0 * stat), 

921 "(%d/%d)" % (folded_blind, blind_stolen), 

922 "% folded blind to steal", 

923 ) 

924 except (KeyError, ValueError, TypeError): 

925 return (stat, "NA", "fB=NA", "fB_s=NA", "(0/0)", "% folded blind to steal") 

926 

927 

928def three_B(stat_dict, player): 

929 """ 

930 Calculate the three bet statistics for a player. 

931 

932 Args: 

933 stat_dict (dict): A dictionary containing player statistics. 

934 player (int): The player for whom the statistics are calculated. 

935 

936 Returns: 

937 tuple: A tuple containing the calculated statistics in different formats: 

938 - Float: The calculated statistic. 

939 - String: The statistic formatted as a percentage with one decimal place. 

940 - String: A formatted string representing the statistic. 

941 - String: A formatted string representing the statistic with a suffix. 

942 - String: A formatted string showing the count of three bets made and opponent's three bets. 

943 - String: A description of the statistic. 

944 

945 If an exception occurs during the calculation, returns default values for the statistics. 

946 """ 

947 stat = 0.0 

948 try: 

949 tb_opp_0 = float(stat_dict[player].get("tb_opp_0", 0)) # Ensure key exists 

950 if tb_opp_0 != 0: # Check if tb_opp_0 is non-zero to avoid division by zero 

951 stat = float(stat_dict[player]["tb_0"]) / tb_opp_0 

952 else: 

953 stat = 0 # Default to 0 if tb_opp_0 is zero 

954 

955 return ( 

956 stat, 

957 "%3.1f" % (100.0 * stat), 

958 "3B=%3.1f%%" % (100.0 * stat), 

959 "3B_pf=%3.1f%%" % (100.0 * stat), 

960 "(%d/%d)" % (stat_dict[player]["tb_0"], tb_opp_0), 

961 "% 3 bet preflop/3rd street", 

962 ) 

963 except (KeyError, ValueError, TypeError): 

964 return (stat, "NA", "3B=NA", "3B_pf=NA", "(0/0)", "% 3 bet preflop/3rd street") 

965 

966 

967def four_B(stat_dict, player): 

968 """ 

969 Calculate the four bet statistics for a player. 

970 

971 Args: 

972 stat_dict (dict): A dictionary containing player statistics. 

973 player (int): The player for whom the statistics are calculated. 

974 

975 Returns: 

976 tuple: A tuple containing the calculated statistics in different formats: 

977 - Float: The calculated statistic. 

978 - String: The statistic formatted as a percentage with one decimal place. 

979 - String: A formatted string representing the statistic. 

980 - String: A formatted string representing the statistic with a suffix. 

981 - String: A formatted string showing the count of four bets made and opponent's four bets. 

982 - String: A description of the statistic. 

983 

984 If an exception occurs during the calculation, returns default values for the statistics. 

985 """ 

986 stat = 0.0 

987 try: 

988 fb_opp_0 = float(stat_dict[player].get("fb_opp_0", 0)) # Ensure key exists 

989 if fb_opp_0 != 0: # Check if fb_opp_0 is non-zero to avoid division by zero 

990 stat = float(stat_dict[player]["fb_0"]) / fb_opp_0 

991 else: 

992 stat = 0 # Default to 0 if fb_opp_0 is zero 

993 

994 return ( 

995 stat, 

996 "%3.1f" % (100.0 * stat), 

997 "4B=%3.1f%%" % (100.0 * stat), 

998 "4B=%3.1f%%" % (100.0 * stat), 

999 "(%d/%d)" % (stat_dict[player]["fb_0"], fb_opp_0), 

1000 "% 4 bet preflop/3rd street", 

1001 ) 

1002 except (KeyError, ValueError, TypeError): 

1003 return (stat, "NA", "4B=NA", "4B=NA", "(0/0)", "% 4 bet preflop/3rd street") 

1004 

1005 

1006def cfour_B(stat_dict, player): 

1007 """ 

1008 Calculate the cold 4 bet statistics for a player. 

1009 

1010 Args: 

1011 stat_dict (dict): A dictionary containing player statistics. 

1012 player (int): The player for whom the statistics are calculated. 

1013 

1014 Returns: 

1015 tuple: A tuple containing the calculated statistics in different formats: 

1016 - Float: The calculated statistic. 

1017 - String: The statistic formatted as a percentage with one decimal place. 

1018 - String: A formatted string representing the statistic. 

1019 - String: A formatted string representing the statistic with a suffix. 

1020 - String: A formatted string showing the count of cold 4 bets made and opponent's cold 4 bets. 

1021 - String: A description of the statistic. 

1022 

1023 If an exception occurs during the calculation, returns default values for the statistics. 

1024 """ 

1025 stat = 0.0 

1026 try: 

1027 if float(stat_dict[player]["cfb_opp_0"]) != 0: 

1028 stat = float(stat_dict[player]["cfb_0"]) / float(stat_dict[player]["cfb_opp_0"]) 

1029 return ( 

1030 stat, 

1031 "%3.1f" % (100.0 * stat), 

1032 "C4B=%3.1f%%" % (100.0 * stat), 

1033 "C4B_pf=%3.1f%%" % (100.0 * stat), 

1034 "(%d/%d)" % (stat_dict[player]["cfb_0"], stat_dict[player]["cfb_opp_0"]), 

1035 "% cold 4 bet preflop/3rd street", 

1036 ) 

1037 except (KeyError, ValueError, TypeError): 

1038 return (stat, "NA", "C4B=NA", "C4B_pf=NA", "(0/0)", "% cold 4 bet preflop/3rd street") 

1039 

1040 

1041# Four Bet Range 

1042def fbr(stat_dict, player): 

1043 """ 

1044 A function to calculate the four bet range statistics for a player. 

1045 

1046 Args: 

1047 stat_dict (dict): A dictionary containing player statistics. 

1048 player (int): The player for whom the statistics are calculated. 

1049 

1050 Returns: 

1051 tuple: A tuple containing the calculated statistics in different formats: 

1052 - Float: The calculated statistic. 

1053 - String: The statistic formatted as a percentage with one decimal place. 

1054 - String: A formatted string representing the statistic. 

1055 - String: A formatted string representing the statistic with a suffix. 

1056 - String: A formatted string showing the product of 'pfr' and 'four_B'. 

1057 - String: A description of the statistic. 

1058 

1059 If an exception occurs during the calculation, returns default values for the statistics. 

1060 """ 

1061 stat = 0.0 

1062 try: 

1063 fb_opp_0 = float(stat_dict[player].get("fb_opp_0", 0)) # Ensure key exists 

1064 pfr_opp = float(stat_dict[player].get("n", 0)) # Ensure key exists 

1065 

1066 if fb_opp_0 != 0 and pfr_opp != 0: # Check both values to avoid division by zero 

1067 stat = (float(stat_dict[player]["fb_0"]) / fb_opp_0) * (float(stat_dict[player]["pfr"]) / pfr_opp) 

1068 else: 

1069 stat = 0 # Default to 0 if any of the values is zero 

1070 

1071 return ( 

1072 stat, 

1073 "%3.1f" % (100.0 * stat), 

1074 "fbr=%3.1f%%" % (100.0 * stat), 

1075 "4Brange=%3.1f%%" % (100.0 * stat), 

1076 "(pfr*four_B)", 

1077 "4 bet range", 

1078 ) 

1079 except (KeyError, ValueError, TypeError): 

1080 return (stat, "NA", "fbr=NA", "fbr=NA", "(pfr*four_B)", "4 bet range") 

1081 

1082 

1083# Call 3 Bet 

1084def ctb(stat_dict, player): 

1085 """ 

1086 A function to calculate the call three bet statistics for a player. 

1087 

1088 Args: 

1089 stat_dict (dict): A dictionary containing player statistics. 

1090 player (int): The player for whom the statistics are calculated. 

1091 

1092 Returns: 

1093 tuple: A tuple containing the calculated statistics in different formats: 

1094 - Float: The calculated statistic. 

1095 - String: The statistic formatted as a percentage with one decimal place. 

1096 - String: A formatted string representing the statistic. 

1097 - String: A formatted string representing the statistic with a suffix. 

1098 - String: A formatted string showing the product of 'f3b_opp_0', 'f3b_0', and 'fb_0'. 

1099 - String: A description of the statistic. 

1100 

1101 If an exception occurs during the calculation, returns default values for the statistics. 

1102 """ 

1103 stat = 0.0 

1104 try: 

1105 f3b_opp_0 = float(stat_dict[player].get("f3b_opp_0", 0)) # Ensure key exists 

1106 

1107 if f3b_opp_0 != 0: # Check if f3b_opp_0 is non-zero to avoid division by zero 

1108 stat = ( 

1109 float(stat_dict[player]["f3b_opp_0"]) 

1110 - float(stat_dict[player]["f3b_0"]) 

1111 - float(stat_dict[player]["fb_0"]) 

1112 ) / f3b_opp_0 

1113 else: 

1114 stat = 0 # Default to 0 if f3b_opp_0 is zero 

1115 

1116 return ( 

1117 stat, 

1118 "%3.1f" % (100.0 * stat), 

1119 "ctb=%3.1f%%" % (100.0 * stat), 

1120 "call3B=%3.1f%%" % (100.0 * stat), 

1121 "(%d/%d)" 

1122 % ( 

1123 float(stat_dict[player]["f3b_opp_0"]) - stat_dict[player]["fb_0"] - stat_dict[player]["f3b_0"], 

1124 stat_dict[player]["fb_opp_0"], 

1125 ), 

1126 "% call 3 bet", 

1127 ) 

1128 except (KeyError, ValueError, TypeError): 

1129 return (stat, "NA", "ctb=NA", "ctb=NA", "(0/0)", "% call 3 bet") 

1130 

1131 

1132def dbr1(stat_dict, player): 

1133 """Calculate and return the Donk Bet and Raise statistic for a given player on flop/4th street. 

1134 

1135 Args: 

1136 stat_dict (dict): A dictionary containing player statistics. 

1137 player (int): The player for whom the statistic is calculated. 

1138 

1139 Returns: 

1140 tuple: A tuple containing the calculated statistic, percentage representation, formatted strings, and additional information. 

1141 

1142 Example: 

1143 dbr1(stat_dict, player) 

1144 """ 

1145 stat = 0.0 

1146 try: 

1147 aggr_1 = float(stat_dict[player].get("aggr_1", 0)) 

1148 cb_1 = float(stat_dict[player].get("cb_1", 0)) 

1149 saw_f = float(stat_dict[player].get("saw_f", 0)) 

1150 cb_opp_1 = float(stat_dict[player].get("cb_opp_1", 0)) 

1151 

1152 if (saw_f - cb_opp_1) != 0: # Check to avoid division by zero 

1153 stat = (aggr_1 - cb_1) / (saw_f - cb_opp_1) 

1154 else: 

1155 stat = 0 # Default to 0 if the denominator is zero 

1156 

1157 return ( 

1158 stat, 

1159 "%3.1f" % (100.0 * stat), 

1160 "dbr1=%3.1f%%" % (100.0 * stat), 

1161 "dbr1=%3.1f%%" % (100.0 * stat), 

1162 "(%d/%d)" 

1163 % ( 

1164 aggr_1 - cb_1, 

1165 saw_f - cb_opp_1, 

1166 ), 

1167 "% DonkBetAndRaise flop/4th street", 

1168 ) 

1169 except (KeyError, ValueError, TypeError): 

1170 return (stat, "NA", "dbr1=NA", "dbr1=NA", "(0/0)", "% DonkBetAndRaise flop/4th street") 

1171 

1172 

1173def dbr2(stat_dict, player): 

1174 """Calculate and return the Donk Bet and Raise statistic for a given player on turn/5th street. 

1175 

1176 Args: 

1177 stat_dict (dict): A dictionary containing player statistics. 

1178 player (int): The player for whom the statistic is calculated. 

1179 

1180 Returns: 

1181 tuple: A tuple containing the calculated statistic, percentage representation, formatted strings, and additional information. 

1182 

1183 Example: 

1184 dbr2(stat_dict, player) 

1185 """ 

1186 stat = 0.0 

1187 try: 

1188 aggr_2 = float(stat_dict[player].get("aggr_2", 0)) 

1189 cb_2 = float(stat_dict[player].get("cb_2", 0)) 

1190 saw_2 = float(stat_dict[player].get("saw_2", 0)) 

1191 cb_opp_2 = float(stat_dict[player].get("cb_opp_2", 0)) 

1192 

1193 if (saw_2 - cb_opp_2) != 0: # Check to avoid division by zero 

1194 stat = (aggr_2 - cb_2) / (saw_2 - cb_opp_2) 

1195 else: 

1196 stat = 0 # Default to 0 if the denominator is zero 

1197 

1198 return ( 

1199 stat, 

1200 "%3.1f" % (100.0 * stat), 

1201 "dbr2=%3.1f%%" % (100.0 * stat), 

1202 "dbr2=%3.1f%%" % (100.0 * stat), 

1203 "(%d/%d)" 

1204 % ( 

1205 aggr_2 - cb_2, 

1206 saw_2 - cb_opp_2, 

1207 ), 

1208 "% DonkBetAndRaise turn/5th street", 

1209 ) 

1210 except (KeyError, ValueError, TypeError): 

1211 return (stat, "NA", "dbr2=NA", "dbr2=NA", "(0/0)", "% DonkBetAndRaise turn/5th street") 

1212 

1213 

1214def dbr3(stat_dict, player): 

1215 """Calculate and return the Donk Bet and Raise statistic for a given player on river/6th street. 

1216 

1217 Args: 

1218 stat_dict (dict): A dictionary containing player statistics. 

1219 player (int): The player for whom the statistic is calculated. 

1220 

1221 Returns: 

1222 tuple: A tuple containing the calculated statistic, percentage representation, formatted strings, and additional information. 

1223 

1224 Example: 

1225 dbr3(stat_dict, player) 

1226 """ 

1227 stat = 0.0 

1228 try: 

1229 aggr_3 = float(stat_dict[player].get("aggr_3", 0)) 

1230 cb_3 = float(stat_dict[player].get("cb_3", 0)) 

1231 saw_3 = float(stat_dict[player].get("saw_3", 0)) 

1232 cb_opp_3 = float(stat_dict[player].get("cb_opp_3", 0)) 

1233 

1234 if (saw_3 - cb_opp_3) != 0: # Check to avoid division by zero 

1235 stat = (aggr_3 - cb_3) / (saw_3 - cb_opp_3) 

1236 else: 

1237 stat = 0 # Default to 0 if the denominator is zero 

1238 

1239 return ( 

1240 stat, 

1241 "%3.1f" % (100.0 * stat), 

1242 "dbr3=%3.1f%%" % (100.0 * stat), 

1243 "dbr3=%3.1f%%" % (100.0 * stat), 

1244 "(%d/%d)" 

1245 % ( 

1246 aggr_3 - cb_3, 

1247 saw_3 - cb_opp_3, 

1248 ), 

1249 "% DonkBetAndRaise river/6th street", 

1250 ) 

1251 except (KeyError, ValueError, TypeError): 

1252 return (stat, "NA", "dbr3=NA", "dbr3=NA", "(0/0)", "% DonkBetAndRaise river/6th street") 

1253 

1254 

1255def f_dbr1(stat_dict, player): 

1256 """Calculate and return the fold to DonkBetAndRaise statistic for a given player on flop/4th street. 

1257 

1258 Args: 

1259 stat_dict (dict): A dictionary containing player statistics. 

1260 player (int): The player for whom the statistic is calculated. 

1261 

1262 Returns: 

1263 tuple: A tuple containing the calculated statistic, formatted strings, and additional information. 

1264 

1265 Example: 

1266 f_dbr1(stat_dict, player) 

1267 

1268 Note: 

1269 If an exception occurs during calculation, 'NA' values are returned. 

1270 """ 

1271 stat = 0.0 

1272 try: 

1273 f_freq_1 = float(stat_dict[player].get("f_freq_1", 0)) 

1274 f_cb_1 = float(stat_dict[player].get("f_cb_1", 0)) 

1275 was_raised_1 = float(stat_dict[player].get("was_raised_1", 0)) 

1276 f_cb_opp_1 = float(stat_dict[player].get("f_cb_opp_1", 0)) 

1277 

1278 if (was_raised_1 - f_cb_opp_1) != 0: # Check to avoid division by zero 

1279 stat = (f_freq_1 - f_cb_1) / (was_raised_1 - f_cb_opp_1) 

1280 else: 

1281 stat = 0 # Default to 0 if the denominator is zero 

1282 

1283 return ( 

1284 stat, 

1285 "%3.1f" % (100.0 * stat), 

1286 "f_dbr1=%3.1f%%" % (100.0 * stat), 

1287 "f_dbr1=%3.1f%%" % (100.0 * stat), 

1288 "(%d/%d)" 

1289 % ( 

1290 f_freq_1 - f_cb_1, 

1291 was_raised_1 - f_cb_opp_1, 

1292 ), 

1293 "% Fold to DonkBetAndRaise flop/4th street", 

1294 ) 

1295 except (KeyError, ValueError, TypeError): 

1296 return (stat, "NA", "f_dbr1=NA", "f_dbr1=NA", "(0/0)", "% Fold DonkBetAndRaise flop/4th street") 

1297 

1298 

1299def f_dbr2(stat_dict, player): 

1300 """Calculate and return the fold to DonkBetAndRaise statistic for a given player on turn/5th street. 

1301 

1302 Args: 

1303 stat_dict (dict): A dictionary containing player statistics. 

1304 player (int): The player for whom the statistic is calculated. 

1305 

1306 Returns: 

1307 tuple: A tuple containing the calculated statistic, formatted strings, and additional information. 

1308 

1309 Example: 

1310 f_dbr2(stat_dict, player) 

1311 

1312 Note: 

1313 If an exception occurs during calculation, 'NA' values are returned. 

1314 """ 

1315 stat = 0.0 

1316 try: 

1317 f_freq_2 = float(stat_dict[player].get("f_freq_2", 0)) 

1318 f_cb_2 = float(stat_dict[player].get("f_cb_2", 0)) 

1319 was_raised_2 = float(stat_dict[player].get("was_raised_2", 0)) 

1320 f_cb_opp_2 = float(stat_dict[player].get("f_cb_opp_2", 0)) 

1321 

1322 if (was_raised_2 - f_cb_opp_2) != 0: # Check to avoid division by zero 

1323 stat = (f_freq_2 - f_cb_2) / (was_raised_2 - f_cb_opp_2) 

1324 else: 

1325 stat = 0 # Default to 0 if the denominator is zero 

1326 

1327 return ( 

1328 stat, 

1329 "%3.1f" % (100.0 * stat), 

1330 "f_dbr2=%3.1f%%" % (100.0 * stat), 

1331 "f_dbr2=%3.1f%%" % (100.0 * stat), 

1332 "(%d/%d)" 

1333 % ( 

1334 f_freq_2 - f_cb_2, 

1335 was_raised_2 - f_cb_opp_2, 

1336 ), 

1337 "% Fold to DonkBetAndRaise turn", 

1338 ) 

1339 except (KeyError, ValueError, TypeError): 

1340 return (stat, "NA", "f_dbr2=NA", "f_dbr2=NA", "(0/0)", "% Fold DonkBetAndRaise turn") 

1341 

1342 

1343def f_dbr3(stat_dict, player): 

1344 """Calculate and return the fold to DonkBetAndRaise statistic for a given player on river/6th street. 

1345 

1346 Args: 

1347 stat_dict (dict): A dictionary containing player statistics. 

1348 player (int): The player for whom the statistic is calculated. 

1349 

1350 Returns: 

1351 tuple: A tuple containing the calculated statistic, formatted strings, and additional information. 

1352 

1353 Example: 

1354 f_dbr3(stat_dict, player) 

1355 

1356 Note: 

1357 If an exception occurs during calculation, 'NA' values are returned. 

1358 """ 

1359 stat = 0.0 

1360 try: 

1361 f_freq_3 = float(stat_dict[player].get("f_freq_3", 0)) 

1362 f_cb_3 = float(stat_dict[player].get("f_cb_3", 0)) 

1363 was_raised_3 = float(stat_dict[player].get("was_raised_3", 0)) 

1364 f_cb_opp_3 = float(stat_dict[player].get("f_cb_opp_3", 0)) 

1365 

1366 if (was_raised_3 - f_cb_opp_3) != 0: # Check to avoid division by zero 

1367 stat = (f_freq_3 - f_cb_3) / (was_raised_3 - f_cb_opp_3) 

1368 else: 

1369 stat = 0 # Default to 0 if the denominator is zero 

1370 

1371 return ( 

1372 stat, 

1373 "%3.1f" % (100.0 * stat), 

1374 "f_dbr3=%3.1f%%" % (100.0 * stat), 

1375 "f_dbr3=%3.1f%%" % (100.0 * stat), 

1376 "(%d/%d)" 

1377 % ( 

1378 f_freq_3 - f_cb_3, 

1379 was_raised_3 - f_cb_opp_3, 

1380 ), 

1381 "% Fold to DonkBetAndRaise river", 

1382 ) 

1383 except (KeyError, ValueError, TypeError): 

1384 return (stat, "NA", "f_dbr3=NA", "f_dbr3=NA", "(0/0)", "% Fold DonkBetAndRaise river") 

1385 

1386 

1387def squeeze(stat_dict, player): 

1388 """ 

1389 Calculate the squeeze statistic for a player. 

1390 

1391 Args: 

1392 stat_dict (dict): A dictionary containing player statistics. 

1393 player (int): The player for whom the statistic is calculated. 

1394 

1395 Returns: 

1396 tuple: A tuple containing the calculated statistic, percentage values, and formatted strings. 

1397 """ 

1398 stat = 0.0 

1399 try: 

1400 sqz_opp_0 = float(stat_dict[player].get("sqz_opp_0", 0)) # Ensure key exists and default to 0 

1401 if sqz_opp_0 != 0: # Check to avoid division by zero 

1402 stat = float(stat_dict[player]["sqz_0"]) / sqz_opp_0 

1403 else: 

1404 stat = 0 # Default to 0 if sqz_opp_0 is zero 

1405 

1406 return ( 

1407 stat, 

1408 "%3.1f" % (100.0 * stat), 

1409 "SQZ=%3.1f%%" % (100.0 * stat), 

1410 "SQZ_pf=%3.1f%%" % (100.0 * stat), 

1411 "(%d/%d)" % (stat_dict[player]["sqz_0"], sqz_opp_0), 

1412 "% squeeze preflop", 

1413 ) 

1414 except (KeyError, ValueError, TypeError): 

1415 return (stat, "NA", "SQZ=NA", "SQZ_pf=NA", "(0/0)", "% squeeze preflop") 

1416 

1417 

1418def raiseToSteal(stat_dict, player): 

1419 """Calculate the raise to steal stat for a player. 

1420 

1421 Args: 

1422 stat_dict (dict): A dictionary containing stats for each player. 

1423 player (int): The player for whom the stat is calculated. 

1424 

1425 Returns: 

1426 tuple: A tuple containing the raise to steal stat, formatted percentages, and additional information. 

1427 """ 

1428 stat = 0.0 

1429 try: 

1430 rts_opp = float(stat_dict[player].get("rts_opp", 0)) # Ensure key exists and default to 0 

1431 if rts_opp != 0: # Check to avoid division by zero 

1432 stat = float(stat_dict[player]["rts"]) / rts_opp 

1433 else: 

1434 stat = 0 # Default to 0 if rts_opp is zero 

1435 

1436 return ( 

1437 stat, 

1438 "%3.1f" % (100.0 * stat), 

1439 "RST=%3.1f%%" % (100.0 * stat), 

1440 "RST_pf=%3.1f%%" % (100.0 * stat), 

1441 "(%d/%d)" % (stat_dict[player]["rts"], rts_opp), 

1442 "% raise to steal", 

1443 ) 

1444 except (KeyError, ValueError, TypeError): 

1445 return (stat, "NA", "RST=NA", "RST_pf=NA", "(0/0)", "% raise to steal") 

1446 

1447 

1448def car0(stat_dict, player): 

1449 """Calculate the percentage of called a raise preflop stat for a player based on the provided stat dictionary. 

1450 

1451 Args: 

1452 stat_dict (dict): A dictionary containing stats for each player. 

1453 player (int): The player for whom the CAR0 stat is calculated. 

1454 

1455 Returns: 

1456 tuple: A tuple containing various formatted strings representing the CAR0 stat. 

1457 If an exception occurs during calculation, returns default 'NA' values. 

1458 """ 

1459 stat = 0.0 

1460 try: 

1461 car_opp_0 = float(stat_dict[player].get("car_opp_0", 0)) # Ensure key exists and default to 0 

1462 if car_opp_0 != 0: # Check to avoid division by zero 

1463 stat = float(stat_dict[player]["car_0"]) / car_opp_0 

1464 else: 

1465 stat = 0 # Default to 0 if car_opp_0 is zero 

1466 

1467 return ( 

1468 stat, 

1469 "%3.1f" % (100.0 * stat), 

1470 "CAR0=%3.1f%%" % (100.0 * stat), 

1471 "CAR_pf=%3.1f%%" % (100.0 * stat), 

1472 "(%d/%d)" % (stat_dict[player]["car_0"], car_opp_0), 

1473 "% called a raise preflop", 

1474 ) 

1475 except (KeyError, ValueError, TypeError): 

1476 return (stat, "NA", "CAR0=NA", "CAR_pf=NA", "(0/0)", "% called a raise preflop") 

1477 

1478 

1479def f_3bet(stat_dict, player): 

1480 """Calculate the Fold to 3-Bet statistic for a player. 

1481 

1482 Args: 

1483 stat_dict (dict): A dictionary containing player statistics. 

1484 player (int): The player for whom the statistic is calculated. 

1485 

1486 Returns: 

1487 tuple: A tuple containing various representations of the Fold to 3-Bet statistic. 

1488 The tuple includes the statistic value, percentage, labels, and counts. 

1489 If an error occurs during calculation, returns 'NA' values. 

1490 """ 

1491 stat = 0.0 

1492 try: 

1493 f3b_opp_0 = float(stat_dict[player].get("f3b_opp_0", 0)) # Ensure key exists and default to 0 

1494 if f3b_opp_0 != 0: # Check to avoid division by zero 

1495 stat = float(stat_dict[player]["f3b_0"]) / f3b_opp_0 

1496 else: 

1497 stat = 0 # Default to 0 if f3b_opp_0 is zero 

1498 

1499 return ( 

1500 stat, 

1501 "%3.1f" % (100.0 * stat), 

1502 "F3B=%3.1f%%" % (100.0 * stat), 

1503 "F3B_pf=%3.1f%%" % (100.0 * stat), 

1504 "(%d/%d)" % (stat_dict[player]["f3b_0"], f3b_opp_0), 

1505 "% fold to 3 bet preflop/3rd street", 

1506 ) 

1507 except (KeyError, ValueError, TypeError): 

1508 return (stat, "NA", "F3B=NA", "F3B_pf=NA", "(0/0)", "% fold to 3 bet preflop/3rd street") 

1509 

1510 

1511def f_4bet(stat_dict, player): 

1512 """ 

1513 Calculate and return fold to 4-bet statistics for a player. 

1514 

1515 Args: 

1516 stat_dict (dict): Dictionary containing player statistics. 

1517 player (int): Player identifier. 

1518 

1519 Returns: 

1520 tuple: Tuple containing various statistics related to fold to 4-bet. 

1521 """ 

1522 stat = 0.0 

1523 try: 

1524 f4b_opp_0 = float(stat_dict[player].get("f4b_opp_0", 0)) # Ensure key exists and default to 0 

1525 if f4b_opp_0 != 0: # Check to avoid division by zero 

1526 stat = float(stat_dict[player]["f4b_0"]) / f4b_opp_0 

1527 else: 

1528 stat = 0 # Default to 0 if f4b_opp_0 is zero 

1529 

1530 return ( 

1531 stat, 

1532 "%3.1f" % (100.0 * stat), 

1533 "F4B=%3.1f%%" % (100.0 * stat), 

1534 "F4B_pf=%3.1f%%" % (100.0 * stat), 

1535 "(%d/%d)" % (stat_dict[player]["f4b_0"], f4b_opp_0), 

1536 "% fold to 4 bet preflop/3rd street", 

1537 ) 

1538 except (KeyError, ValueError, TypeError): 

1539 return (stat, "NA", "F4B=NA", "F4B_pf=NA", "(0/0)", "% fold to 4 bet preflop/3rd street") 

1540 

1541 

1542def WMsF(stat_dict, player): 

1543 """Calculate the win money percentage when seeing the flop or 4th street. 

1544 

1545 Args: 

1546 stat_dict (dict): A dictionary containing player statistics. 

1547 player (int): The player for whom the statistics are calculated. 

1548 

1549 Returns: 

1550 tuple: A tuple containing various win money percentage statistics and descriptions. 

1551 """ 

1552 stat = 0.0 

1553 try: 

1554 saw_1 = float(stat_dict[player].get("saw_1", 0)) # Ensure key exists and default to 0 

1555 if saw_1 != 0: # Check to avoid division by zero 

1556 stat = float(stat_dict[player]["w_w_s_1"]) / saw_1 

1557 else: 

1558 stat = 0 # Default to 0 if saw_1 is zero 

1559 

1560 return ( 

1561 stat, 

1562 "%3.1f" % (100.0 * stat), 

1563 "wf=%3.1f%%" % (100.0 * stat), 

1564 "w_w_f=%3.1f%%" % (100.0 * stat), 

1565 "(%d/%d)" % (stat_dict[player]["w_w_s_1"], stat_dict[player]["saw_f"]), 

1566 "% won money when seen flop/4th street", 

1567 ) 

1568 except (KeyError, ValueError, TypeError): 

1569 return (stat, "NA", "wf=NA", "w_w_f=NA", "(0/0)", "% won money when seen flop/4th street") 

1570 

1571 

1572def a_freq1(stat_dict, player): 

1573 """Calculate aggression frequency for a specific player based on their stats on flop/4th street. 

1574 

1575 Args: 

1576 stat_dict (dict): A dictionary containing player statistics. 

1577 player (int): The player for whom the aggression frequency is calculated. 

1578 

1579 Returns: 

1580 tuple: A tuple containing the calculated aggression frequency, formatted strings, and related information. 

1581 """ 

1582 stat = 0.0 

1583 try: 

1584 saw_f = float(stat_dict[player].get("saw_f", 0)) # Ensure key exists and default to 0 

1585 if saw_f != 0: # Check to avoid division by zero 

1586 stat = float(stat_dict[player]["aggr_1"]) / saw_f 

1587 else: 

1588 stat = 0 # Default to 0 if saw_f is zero 

1589 

1590 return ( 

1591 stat, 

1592 "%3.1f" % (100.0 * stat), 

1593 "a1=%3.1f%%" % (100.0 * stat), 

1594 "a_fq_1=%3.1f%%" % (100.0 * stat), 

1595 "(%d/%d)" % (stat_dict[player]["aggr_1"], saw_f), 

1596 "Aggression frequency flop/4th street", 

1597 ) 

1598 except (KeyError, ValueError, TypeError): 

1599 return (stat, "NA", "a1=NA", "a_fq_1=NA", "(0/0)", "Aggression frequency flop/4th street") 

1600 

1601 

1602def a_freq2(stat_dict, player): 

1603 """Calculate aggression frequency for a specific player based on their stats on turn/5th street. 

1604 

1605 Args: 

1606 stat_dict (dict): A dictionary containing player statistics. 

1607 player (int): The player for whom the aggression frequency is calculated. 

1608 

1609 Returns: 

1610 tuple: A tuple containing the calculated aggression frequency, formatted strings, and related information. 

1611 """ 

1612 stat = 0.0 

1613 try: 

1614 saw_2 = float(stat_dict[player].get("saw_2", 0)) # Ensure key exists and default to 0 

1615 if saw_2 != 0: # Check to avoid division by zero 

1616 stat = float(stat_dict[player]["aggr_2"]) / saw_2 

1617 else: 

1618 stat = 0 # Default to 0 if saw_2 is zero 

1619 

1620 return ( 

1621 stat, 

1622 "%3.1f" % (100.0 * stat), 

1623 "a2=%3.1f%%" % (100.0 * stat), 

1624 "a_fq_2=%3.1f%%" % (100.0 * stat), 

1625 "(%d/%d)" % (stat_dict[player]["aggr_2"], saw_2), 

1626 "Aggression frequency turn/5th street", 

1627 ) 

1628 except (KeyError, ValueError, TypeError): 

1629 return (stat, "NA", "a2=NA", "a_fq_2=NA", "(0/0)", "Aggression frequency turn/5th street") 

1630 

1631 

1632def a_freq3(stat_dict, player): 

1633 """Calculate aggression frequency for a specific player based on their stats on river/6th street. 

1634 

1635 Args: 

1636 stat_dict (dict): A dictionary containing player statistics. 

1637 player (int): The player for whom the aggression frequency is calculated. 

1638 

1639 Returns: 

1640 tuple: A tuple containing the calculated aggression frequency, formatted strings, and related information. 

1641 """ 

1642 stat = 0.0 

1643 try: 

1644 saw_3 = float(stat_dict[player].get("saw_3", 0)) # Ensure key exists and default to 0 

1645 if saw_3 != 0: # Check to avoid division by zero 

1646 stat = float(stat_dict[player]["aggr_3"]) / saw_3 

1647 else: 

1648 stat = 0 # Default to 0 if saw_3 is zero 

1649 

1650 return ( 

1651 stat, 

1652 "%3.1f" % (100.0 * stat), 

1653 "a3=%3.1f%%" % (100.0 * stat), 

1654 "a_fq_3=%3.1f%%" % (100.0 * stat), 

1655 "(%d/%d)" % (stat_dict[player]["aggr_3"], saw_3), 

1656 "Aggression frequency river/6th street", 

1657 ) 

1658 except (KeyError, ValueError, TypeError): 

1659 return (stat, "NA", "a3=NA", "a_fq_3=NA", "(0/0)", "Aggression frequency river/6th street") 

1660 

1661 

1662def a_freq4(stat_dict, player): 

1663 """Calculate aggression frequency for a specific player based on their stats on 7th street. 

1664 

1665 Args: 

1666 stat_dict (dict): A dictionary containing player statistics. 

1667 player (int): The player for whom the aggression frequency is calculated. 

1668 

1669 Returns: 

1670 tuple: A tuple containing the calculated aggression frequency, formatted strings, and related information. 

1671 """ 

1672 stat = 0.0 

1673 try: 

1674 if float(stat_dict[player]["saw_4"]) != 0: 

1675 stat = float(stat_dict[player]["aggr_4"]) / float(stat_dict[player]["saw_4"]) 

1676 return ( 

1677 stat, 

1678 "%3.1f" % (100.0 * stat), 

1679 "a4=%3.1f%%" % (100.0 * stat), 

1680 "a_fq_4=%3.1f%%" % (100.0 * stat), 

1681 "(%d/%d)" % (stat_dict[player]["aggr_4"], stat_dict[player]["saw_4"]), 

1682 "Aggression frequency 7th street", 

1683 ) 

1684 except (KeyError, ValueError, TypeError): 

1685 return (stat, "NA", "a4=NA", "a_fq_4=NA", "(0/0)", "Aggression frequency 7th street") 

1686 

1687 

1688def a_freq_123(stat_dict, player): 

1689 """Calculate aggression frequency for a specific player based on their stats post-flop. 

1690 

1691 Args: 

1692 stat_dict (dict): A dictionary containing player statistics. 

1693 player (int): The player for whom the aggression frequency is calculated. 

1694 

1695 Returns: 

1696 tuple: A tuple containing the calculated aggression frequency, formatted strings, and related information. 

1697 """ 

1698 stat = 0.0 

1699 try: 

1700 # Sum up aggression and seen stats 

1701 total_aggr = ( 

1702 stat_dict[player].get("aggr_1", 0) + stat_dict[player].get("aggr_2", 0) + stat_dict[player].get("aggr_3", 0) 

1703 ) 

1704 total_saw = ( 

1705 stat_dict[player].get("saw_1", 0) + stat_dict[player].get("saw_2", 0) + stat_dict[player].get("saw_3", 0) 

1706 ) 

1707 

1708 # Check to avoid division by zero 

1709 if total_saw != 0: 

1710 stat = float(total_aggr) / float(total_saw) 

1711 else: 

1712 stat = 0 # Default to 0 if total_saw is zero 

1713 

1714 return ( 

1715 stat, 

1716 "%3.1f" % (100.0 * stat), 

1717 "afq=%3.1f%%" % (100.0 * stat), 

1718 "post_a_fq=%3.1f%%" % (100.0 * stat), 

1719 "(%d/%d)" % (total_aggr, total_saw), 

1720 "Post-flop aggression frequency", 

1721 ) 

1722 except (KeyError, ValueError, TypeError): 

1723 return (stat, "NA", "afq=NA", "post_a_fq=NA", "(0/0)", "Post-flop aggression frequency") 

1724 

1725 

1726def agg_fact(stat_dict, player): 

1727 """Calculate the aggression factor based on the given player's statistics. 

1728 

1729 Args: 

1730 stat_dict (dict): A dictionary containing the player's statistics. 

1731 player (str): The player for whom the aggression factor is calculated. 

1732 

1733 Returns: 

1734 tuple: A tuple containing the calculated aggression factor in different formats. 

1735 The formats include percentage, float, and string representations. 

1736 If an exception occurs during calculation, default values are returned. 

1737 """ 

1738 stat = 0.0 

1739 try: 

1740 # Sum of all bet/raise and call actions 

1741 bet_raise = ( 

1742 stat_dict[player].get("aggr_1", 0) 

1743 + stat_dict[player].get("aggr_2", 0) 

1744 + stat_dict[player].get("aggr_3", 0) 

1745 + stat_dict[player].get("aggr_4", 0) 

1746 ) 

1747 post_call = ( 

1748 stat_dict[player].get("call_1", 0) 

1749 + stat_dict[player].get("call_2", 0) 

1750 + stat_dict[player].get("call_3", 0) 

1751 + stat_dict[player].get("call_4", 0) 

1752 ) 

1753 

1754 # Check to avoid division by zero 

1755 if post_call > 0: 

1756 stat = float(bet_raise) / float(post_call) 

1757 else: 

1758 stat = float(bet_raise) 

1759 

1760 return ( 

1761 stat / 100.0, 

1762 "%2.2f" % (stat), 

1763 "afa=%2.2f" % (stat), 

1764 "agg_fa=%2.2f" % (stat), 

1765 "(%d/%d)" % (bet_raise, post_call), 

1766 "Aggression factor", 

1767 ) 

1768 except (KeyError, ValueError, TypeError): 

1769 return (stat, "NA", "afa=NA", "agg_fa=NA", "(0/0)", "Aggression factor") 

1770 

1771 

1772def agg_fact_pct(stat_dict, player): 

1773 """Calculate the aggression factor percentage based on the given player's stats. 

1774 

1775 Args: 

1776 stat_dict (dict): A dictionary containing the player's statistics. 

1777 player (int): The player for whom the aggression factor percentage is calculated. 

1778 

1779 Returns: 

1780 tuple: A tuple containing the aggression factor percentage, formatted strings, and related information. 

1781 

1782 Raises: 

1783 Exception: If an error occurs during the calculation, returns default values. 

1784 """ 

1785 stat = 0.0 

1786 try: 

1787 # Safely retrieve the values, defaulting to 0 if the keys are missing 

1788 bet_raise = ( 

1789 stat_dict[player].get("aggr_1", 0) 

1790 + stat_dict[player].get("aggr_2", 0) 

1791 + stat_dict[player].get("aggr_3", 0) 

1792 + stat_dict[player].get("aggr_4", 0) 

1793 ) 

1794 post_call = ( 

1795 stat_dict[player].get("call_1", 0) 

1796 + stat_dict[player].get("call_2", 0) 

1797 + stat_dict[player].get("call_3", 0) 

1798 + stat_dict[player].get("call_4", 0) 

1799 ) 

1800 

1801 # Check to avoid division by zero 

1802 if float(post_call + bet_raise) > 0.0: 

1803 stat = float(bet_raise) / float(post_call + bet_raise) 

1804 

1805 return ( 

1806 stat / 100.0, 

1807 "%2.2f" % (stat), 

1808 "afap=%2.2f" % (stat), 

1809 "agg_fa_pct=%2.2f" % (stat), 

1810 "(%d/%d)" % (bet_raise, post_call), 

1811 "Aggression factor pct", 

1812 ) 

1813 except (KeyError, ValueError, TypeError): 

1814 return (stat, "NA", "afap=NA", "agg_fa_pct=NA", "(0/0)", "Aggression factor pct") 

1815 

1816 

1817def cbet(stat_dict, player): 

1818 """Calculate the continuation bet (cbet) percentage for a player. 

1819 

1820 Args: 

1821 stat_dict (dict): A dictionary containing statistics for the player. 

1822 player (int): The player for whom the cbet percentage is calculated. 

1823 

1824 Returns: 

1825 tuple: A tuple containing the cbet percentage, formatted strings, and stats. 

1826 

1827 Raises: 

1828 Exception: If there is an issue with calculating the cbet percentage. 

1829 """ 

1830 stat = 0.0 

1831 try: 

1832 # Safely retrieve cbet and opportunity values, defaulting to 0 if keys don't exist 

1833 cbets = ( 

1834 stat_dict[player].get("cb_1", 0) 

1835 + stat_dict[player].get("cb_2", 0) 

1836 + stat_dict[player].get("cb_3", 0) 

1837 + stat_dict[player].get("cb_4", 0) 

1838 ) 

1839 oppt = ( 

1840 stat_dict[player].get("cb_opp_1", 0) 

1841 + stat_dict[player].get("cb_opp_2", 0) 

1842 + stat_dict[player].get("cb_opp_3", 0) 

1843 + stat_dict[player].get("cb_opp_4", 0) 

1844 ) 

1845 

1846 # Check to avoid division by zero 

1847 if oppt != 0: 

1848 stat = float(cbets) / float(oppt) 

1849 else: 

1850 stat = 0 # Default to 0 if oppt is zero 

1851 

1852 return ( 

1853 stat, 

1854 "%3.1f" % (100.0 * stat), 

1855 "cbet=%3.1f%%" % (100.0 * stat), 

1856 "cbet=%3.1f%%" % (100.0 * stat), 

1857 "(%d/%d)" % (cbets, oppt), 

1858 "% continuation bet", 

1859 ) 

1860 except (KeyError, ValueError, TypeError): 

1861 return (stat, "NA", "cbet=NA", "cbet=NA", "(0/0)", "% continuation bet") 

1862 

1863 

1864def cb1(stat_dict, player): 

1865 """Calculate the continuation bet statistic for a given player on flop/4th street. 

1866 

1867 Args: 

1868 stat_dict (dict): A dictionary containing player statistics. 

1869 player (int): The player for whom the statistic is calculated. 

1870 

1871 Returns: 

1872 tuple: A tuple containing various formatted strings representing the continuation bet statistic. 

1873 """ 

1874 stat = 0.0 

1875 try: 

1876 if float(stat_dict[player]["cb_opp_1"]) != 0: 

1877 stat = float(stat_dict[player]["cb_1"]) / float(stat_dict[player]["cb_opp_1"]) 

1878 return ( 

1879 stat, 

1880 "%3.1f" % (100.0 * stat), 

1881 "cb1=%3.1f%%" % (100.0 * stat), 

1882 "cb_1=%3.1f%%" % (100.0 * stat), 

1883 "(%d/%d)" % (stat_dict[player]["cb_1"], stat_dict[player]["cb_opp_1"]), 

1884 "% continuation bet flop/4th street", 

1885 ) 

1886 except (KeyError, ValueError, TypeError): 

1887 return (stat, "NA", "cb1=NA", "cb_1=NA", "(0/0)", "% continuation bet flop/4th street") 

1888 

1889 

1890def cb2(stat_dict, player): 

1891 """Calculate the continuation bet statistic for a given player on turn/5th street. 

1892 

1893 Args: 

1894 stat_dict (dict): A dictionary containing player statistics. 

1895 player (int): The player for whom the statistic is calculated. 

1896 

1897 Returns: 

1898 tuple: A tuple containing various formatted strings representing the continuation bet statistic. 

1899 """ 

1900 stat = 0.0 

1901 try: 

1902 cb_opp_2 = float(stat_dict[player].get("cb_opp_2", 0)) # Ensure key exists and default to 0 

1903 if cb_opp_2 != 0: # Check to avoid division by zero 

1904 stat = float(stat_dict[player]["cb_2"]) / cb_opp_2 

1905 else: 

1906 stat = 0 # Default to 0 if cb_opp_2 is zero 

1907 

1908 return ( 

1909 stat, 

1910 "%3.1f" % (100.0 * stat), 

1911 "cb2=%3.1f%%" % (100.0 * stat), 

1912 "cb_2=%3.1f%%" % (100.0 * stat), 

1913 "(%d/%d)" % (stat_dict[player]["cb_2"], cb_opp_2), 

1914 "% continuation bet turn/5th street", 

1915 ) 

1916 except (KeyError, ValueError, TypeError): 

1917 return (stat, "NA", "cb2=NA", "cb_2=NA", "(0/0)", "% continuation bet turn/5th street") 

1918 

1919 

1920def cb3(stat_dict, player): 

1921 """Calculate the continuation bet statistic for a given player on river/6th street. 

1922 

1923 Args: 

1924 stat_dict (dict): A dictionary containing player statistics. 

1925 player (int): The player for whom the statistic is calculated. 

1926 

1927 Returns: 

1928 tuple: A tuple containing various formatted strings representing the continuation bet statistic. 

1929 """ 

1930 stat = 0.0 

1931 try: 

1932 cb_opp_3 = float(stat_dict[player].get("cb_opp_3", 0)) # Ensure key exists and default to 0 

1933 if cb_opp_3 != 0: # Check to avoid division by zero 

1934 stat = float(stat_dict[player]["cb_3"]) / cb_opp_3 

1935 else: 

1936 stat = 0 # Default to 0 if cb_opp_3 is zero 

1937 

1938 return ( 

1939 stat, 

1940 "%3.1f" % (100.0 * stat), 

1941 "cb3=%3.1f%%" % (100.0 * stat), 

1942 "cb_3=%3.1f%%" % (100.0 * stat), 

1943 "(%d/%d)" % (stat_dict[player]["cb_3"], cb_opp_3), 

1944 "% continuation bet river/6th street", 

1945 ) 

1946 except (KeyError, ValueError, TypeError): 

1947 return (stat, "NA", "cb3=NA", "cb_3=NA", "(0/0)", "% continuation bet river/6th street") 

1948 

1949 

1950def cb4(stat_dict, player): 

1951 """Calculate the continuation bet statistic for a given player on 7th street. 

1952 

1953 Args: 

1954 stat_dict (dict): A dictionary containing player statistics. 

1955 player (int): The player for whom the statistic is calculated. 

1956 

1957 Returns: 

1958 tuple: A tuple containing various formatted strings representing the continuation bet statistic. 

1959 """ 

1960 stat = 0.0 

1961 try: 

1962 if float(stat_dict[player]["cb_opp_4"]) != 0: 

1963 stat = float(stat_dict[player]["cb_4"]) / float(stat_dict[player]["cb_opp_4"]) 

1964 return ( 

1965 stat, 

1966 "%3.1f" % (100.0 * stat), 

1967 "cb4=%3.1f%%" % (100.0 * stat), 

1968 "cb_4=%3.1f%%" % (100.0 * stat), 

1969 "(%d/%d)" % (stat_dict[player]["cb_4"], stat_dict[player]["cb_opp_4"]), 

1970 "% continuation bet 7th street", 

1971 ) 

1972 except (KeyError, ValueError, TypeError): 

1973 return (stat, "NA", "cb4=NA", "cb_4=NA", "(0/0)", "% continuation bet 7th street") 

1974 

1975 

1976def ffreq1(stat_dict, player): 

1977 """ 

1978 Calculate the fold frequency statistic for a given player on the flop/4th street. 

1979 

1980 Args: 

1981 stat_dict (dict): A dictionary containing player statistics. 

1982 player (int): The player for whom the statistic is calculated. 

1983 

1984 Returns: 

1985 tuple: A tuple containing various formatted strings representing the fold frequency statistic. 

1986 """ 

1987 stat = 0.0 

1988 try: 

1989 was_raised_1 = float(stat_dict[player].get("was_raised_1", 0)) # Ensure key exists and default to 0 

1990 if was_raised_1 != 0: # Check to avoid division by zero 

1991 stat = float(stat_dict[player]["f_freq_1"]) / was_raised_1 

1992 else: 

1993 stat = 0 # Default to 0 if was_raised_1 is zero 

1994 

1995 return ( 

1996 stat, 

1997 "%3.1f" % (100.0 * stat), 

1998 "ff1=%3.1f%%" % (100.0 * stat), 

1999 "ff_1=%3.1f%%" % (100.0 * stat), 

2000 "(%d/%d)" % (stat_dict[player]["f_freq_1"], was_raised_1), 

2001 "% fold frequency flop/4th street", 

2002 ) 

2003 except (KeyError, ValueError, TypeError): 

2004 return (stat, "NA", "ff1=NA", "ff_1=NA", "(0/0)", "% fold frequency flop/4th street") 

2005 

2006 

2007def ffreq2(stat_dict, player): 

2008 """ 

2009 Calculate the fold frequency statistic for a given player on the turn/5th street. 

2010 

2011 Args: 

2012 stat_dict (dict): A dictionary containing player statistics. 

2013 player (int): The player for whom the statistic is calculated. 

2014 

2015 Returns: 

2016 tuple: A tuple containing various formatted strings representing the fold frequency statistic. 

2017 """ 

2018 stat = 0.0 

2019 try: 

2020 was_raised_2 = float(stat_dict[player].get("was_raised_2", 0)) # Ensure key exists and default to 0 

2021 if was_raised_2 != 0: # Check to avoid division by zero 

2022 stat = float(stat_dict[player]["f_freq_2"]) / was_raised_2 

2023 else: 

2024 stat = 0 # Default to 0 if was_raised_2 is zero 

2025 

2026 return ( 

2027 stat, 

2028 "%3.1f" % (100.0 * stat), 

2029 "ff2=%3.1f%%" % (100.0 * stat), 

2030 "ff_2=%3.1f%%" % (100.0 * stat), 

2031 "(%d/%d)" % (stat_dict[player]["f_freq_2"], was_raised_2), 

2032 "% fold frequency turn/5th street", 

2033 ) 

2034 except (KeyError, ValueError, TypeError): 

2035 return (stat, "NA", "ff2=NA", "ff_2=NA", "(0/0)", "% fold frequency turn/5th street") 

2036 

2037 

2038def ffreq3(stat_dict, player): 

2039 """ 

2040 Calculate the fold frequency statistic for a given player on the river/6th street. 

2041 

2042 Args: 

2043 stat_dict (dict): A dictionary containing player statistics. 

2044 player (int): The player for whom the statistic is calculated. 

2045 

2046 Returns: 

2047 tuple: A tuple containing various formatted strings representing the fold frequency statistic. 

2048 """ 

2049 stat = 0.0 

2050 try: 

2051 was_raised_3 = float(stat_dict[player].get("was_raised_3", 0)) # Ensure key exists and default to 0 

2052 if was_raised_3 != 0: # Check to avoid division by zero 

2053 stat = float(stat_dict[player]["f_freq_3"]) / was_raised_3 

2054 else: 

2055 stat = 0 # Default to 0 if was_raised_3 is zero 

2056 

2057 return ( 

2058 stat, 

2059 "%3.1f" % (100.0 * stat), 

2060 "ff3=%3.1f%%" % (100.0 * stat), 

2061 "ff_3=%3.1f%%" % (100.0 * stat), 

2062 "(%d/%d)" % (stat_dict[player]["f_freq_3"], was_raised_3), 

2063 "% fold frequency river/6th street", 

2064 ) 

2065 except (KeyError, ValueError, TypeError): 

2066 return (stat, "NA", "ff3=NA", "ff_3=NA", "(0/0)", "% fold frequency river/6th street") 

2067 

2068 

2069def ffreq4(stat_dict, player): 

2070 """ 

2071 Calculate the fold frequency statistic for a given player on the 7th street. 

2072 

2073 Args: 

2074 stat_dict (dict): A dictionary containing player statistics. 

2075 player (int): The player for whom the statistic is calculated. 

2076 

2077 Returns: 

2078 tuple: A tuple containing various formatted strings representing the fold frequency statistic. 

2079 """ 

2080 stat = 0.0 

2081 try: 

2082 if float(stat_dict[player]["was_raised_4"]) != 0: 

2083 stat = float(stat_dict[player]["f_freq_4"]) / float(stat_dict[player]["was_raised_4"]) 

2084 return ( 

2085 stat, 

2086 "%3.1f" % (100.0 * stat), 

2087 "ff4=%3.1f%%" % (100.0 * stat), 

2088 "ff_4=%3.1f%%" % (100.0 * stat), 

2089 "(%d/%d)" % (stat_dict[player]["f_freq_4"], stat_dict[player]["was_raised_4"]), 

2090 "% fold frequency 7th street", 

2091 ) 

2092 except (KeyError, ValueError, TypeError): 

2093 return (stat, "NA", "ff4=NA", "ff_4=NA", "(0/0)", "% fold frequency 7th street") 

2094 

2095 

2096def f_cb1(stat_dict, player): 

2097 """ 

2098 Calculate the fold to continuation bet statistic for a given player on the flop/4th street. 

2099 

2100 Args: 

2101 stat_dict (dict): A dictionary containing player statistics. 

2102 player (int): The player for whom the statistic is calculated. 

2103 

2104 Returns: 

2105 tuple: A tuple containing various formatted strings representing the fold to continuation bet statistic. 

2106 The tuple contains the following elements: 

2107 - stat (float): The calculated statistic value. 

2108 - percent (str): The calculated statistic value formatted as a percentage. 

2109 - f_cb1 (str): The calculated statistic value formatted as a percentage with a specific format. 

2110 - f_cb_1 (str): The calculated statistic value formatted as a percentage with a specific format. 

2111 - count (str): The count of occurrences divided by the count of opponent's continuation bets. 

2112 - description (str): A description of the statistic. 

2113 """ 

2114 stat = 0.0 

2115 try: 

2116 f_cb_opp_1 = float(stat_dict[player].get("f_cb_opp_1", 0)) # Ensure key exists and default to 0 

2117 if f_cb_opp_1 != 0: # Check to avoid division by zero 

2118 stat = float(stat_dict[player]["f_cb_1"]) / f_cb_opp_1 

2119 else: 

2120 stat = 0 # Default to 0 if f_cb_opp_1 is zero 

2121 

2122 return ( 

2123 stat, 

2124 "%3.1f" % (100.0 * stat), 

2125 "f_cb1=%3.1f%%" % (100.0 * stat), 

2126 "f_cb_1=%3.1f%%" % (100.0 * stat), 

2127 "(%d/%d)" % (stat_dict[player]["f_cb_1"], f_cb_opp_1), 

2128 "% fold to continuation bet flop/4th street", 

2129 ) 

2130 except (KeyError, ValueError, TypeError): 

2131 return (stat, "NA", "f_cb1=NA", "f_cb_1=NA", "(0/0)", "% fold to continuation bet flop/4th street") 

2132 

2133 

2134def f_cb2(stat_dict, player): 

2135 """ 

2136 Calculate the fold to continuation bet statistic for a given player on the turn/5th street. 

2137 

2138 Args: 

2139 stat_dict (dict): A dictionary containing player statistics. 

2140 player (int): The player for whom the statistic is calculated. 

2141 

2142 Returns: 

2143 tuple: A tuple containing various formatted strings representing the fold to continuation bet statistic. 

2144 The tuple contains the following elements: 

2145 - stat (float): The calculated statistic value. 

2146 - percent (str): The calculated statistic value formatted as a percentage. 

2147 - f_cb2 (str): The calculated statistic value formatted as a percentage with a specific format. 

2148 - f_cb_2 (str): The calculated statistic value formatted as a percentage with a specific format. 

2149 - count (str): The count of occurrences divided by the count of opponent's continuation bets. 

2150 - description (str): A description of the statistic. 

2151 """ 

2152 stat = 0.0 

2153 try: 

2154 if float(stat_dict[player]["f_cb_opp_2"]) != 0: 

2155 stat = float(stat_dict[player]["f_cb_2"]) / float(stat_dict[player]["f_cb_opp_2"]) 

2156 return ( 

2157 stat, 

2158 "%3.1f" % (100.0 * stat), 

2159 "f_cb2=%3.1f%%" % (100.0 * stat), 

2160 "f_cb_2=%3.1f%%" % (100.0 * stat), 

2161 "(%d/%d)" % (stat_dict[player]["f_cb_2"], stat_dict[player]["f_cb_opp_2"]), 

2162 "% fold to continuation bet turn/5th street", 

2163 ) 

2164 except (KeyError, ValueError, TypeError): 

2165 return (stat, "NA", "f_cb2=NA", "f_cb_2=NA", "(0/0)", "% fold to continuation bet turn/5th street") 

2166 

2167 

2168def f_cb3(stat_dict, player): 

2169 """ 

2170 Calculate the fold to continuation bet statistic for a given player on the river/6th street. 

2171 

2172 Args: 

2173 stat_dict (dict): A dictionary containing player statistics. 

2174 player (int): The player for whom the statistic is calculated. 

2175 

2176 Returns: 

2177 tuple: A tuple containing various formatted strings representing the fold to continuation bet statistic. 

2178 The tuple contains the following elements: 

2179 - stat (float): The calculated statistic value. 

2180 - percent (str): The calculated statistic value formatted as a percentage. 

2181 - f_cb3 (str): The calculated statistic value formatted as a percentage with a specific format. 

2182 - f_cb_3 (str): The calculated statistic value formatted as a percentage with a specific format. 

2183 - count (str): The count of occurrences divided by the count of opponent's continuation bets. 

2184 - description (str): A description of the statistic. 

2185 """ 

2186 stat = 0.0 

2187 try: 

2188 if float(stat_dict[player]["f_cb_opp_3"]) != 0: 

2189 stat = float(stat_dict[player]["f_cb_3"]) / float(stat_dict[player]["f_cb_opp_3"]) 

2190 return ( 

2191 stat, 

2192 "%3.1f" % (100.0 * stat), 

2193 "f_cb3=%3.1f%%" % (100.0 * stat), 

2194 "f_cb_3=%3.1f%%" % (100.0 * stat), 

2195 "(%d/%d)" % (stat_dict[player]["f_cb_3"], stat_dict[player]["f_cb_opp_3"]), 

2196 "% fold to continuation bet river/6th street", 

2197 ) 

2198 except (KeyError, ValueError, TypeError): 

2199 return (stat, "NA", "f_cb3=NA", "f_cb_3=NA", "(0/0)", "% fold to continuation bet river/6th street") 

2200 

2201 

2202def f_cb4(stat_dict, player): 

2203 """ 

2204 Calculate the fold to continuation bet statistic for a given player on the 7th street. 

2205 

2206 Args: 

2207 stat_dict (dict): A dictionary containing player statistics. 

2208 player (int): The player for whom the statistic is calculated. 

2209 

2210 Returns: 

2211 tuple: A tuple containing various formatted strings representing the fold to continuation bet statistic. 

2212 The tuple contains the following elements: 

2213 - stat (float): The calculated statistic value. 

2214 - percent (str): The calculated statistic value formatted as a percentage. 

2215 - f_cb4 (str): The calculated statistic value formatted as a percentage with a specific format. 

2216 - f_cb_4 (str): The calculated statistic value formatted as a percentage with a specific format. 

2217 - count (str): The count of occurrences divided by the count of opponent's continuation bets. 

2218 - description (str): A description of the statistic. 

2219 

2220 Raises: 

2221 None 

2222 """ 

2223 stat = 0.0 

2224 try: 

2225 if float(stat_dict[player]["f_cb_opp_4"]) != 0: 

2226 stat = float(stat_dict[player]["f_cb_4"]) / float(stat_dict[player]["f_cb_opp_4"]) 

2227 return ( 

2228 stat, 

2229 "%3.1f" % (100.0 * stat), 

2230 "f_cb4=%3.1f%%" % (100.0 * stat), 

2231 "f_cb_4=%3.1f%%" % (100.0 * stat), 

2232 "(%d/%d)" % (stat_dict[player]["f_cb_4"], stat_dict[player]["f_cb_opp_4"]), 

2233 "% fold to continuation bet 7th street", 

2234 ) 

2235 except (KeyError, ValueError, TypeError): 

2236 return (stat, "NA", "f_cb4=NA", "f_cb_4=NA", "(0/0)", "% fold to continuation bet 7th street") 

2237 

2238 

2239def cr1(stat_dict, player): 

2240 """ 

2241 Calculate the check-raise flop/4th street statistic for a given player. 

2242 

2243 Args: 

2244 stat_dict (dict): A dictionary containing player statistics. 

2245 player (int): The player for whom the statistic is calculated. 

2246 

2247 Returns: 

2248 tuple: A tuple containing various formatted strings representing the check-raise flop/4th street statistic. 

2249 The tuple contains the following elements: 

2250 - stat (float): The calculated statistic value. 

2251 - percent (str): The calculated statistic value formatted as a percentage. 

2252 - cr1 (str): The calculated statistic value formatted with a specific format. 

2253 - cr_1 (str): The calculated statistic value formatted with a specific format. 

2254 - count (str): The count of occurrences divided by the count of opponent's check-raises. 

2255 - description (str): A description of the statistic. 

2256 

2257 Raises: 

2258 None 

2259 """ 

2260 stat = 0.0 

2261 try: 

2262 ccr_opp_1 = float(stat_dict[player].get("ccr_opp_1", 0)) # Ensure key exists and default to 0 

2263 if ccr_opp_1 != 0: # Check to avoid division by zero 

2264 stat = float(stat_dict[player]["cr_1"]) / ccr_opp_1 

2265 else: 

2266 stat = 0 # Default to 0 if ccr_opp_1 is zero 

2267 

2268 return ( 

2269 stat, 

2270 "%3.1f" % (100.0 * stat), 

2271 "cr1=%3.1f%%" % (100.0 * stat), 

2272 "cr_1=%3.1f%%" % (100.0 * stat), 

2273 "(%d/%d)" % (stat_dict[player]["cr_1"], ccr_opp_1), 

2274 "% check-raise flop/4th street", 

2275 ) 

2276 except (KeyError, ValueError, TypeError): 

2277 return (stat, "NA", "cr1=NA", "cr_1=NA", "(0/0)", "% check-raise flop/4th street") 

2278 

2279 

2280def cr2(stat_dict, player): 

2281 """ 

2282 Calculates the check-raise turn/5th street for a given player. 

2283 

2284 Args: 

2285 stat_dict (dict): A dictionary containing player statistics. 

2286 player (int): The player for whom the statistic is calculated. 

2287 

2288 Returns: 

2289 tuple: A tuple containing various formatted strings representing the check-raise to fold ratio. 

2290 The tuple contains the following elements: 

2291 - stat (float): The calculated statistic value. 

2292 - percent (str): The calculated statistic value formatted as a percentage. 

2293 - cr2 (str): The calculated statistic value formatted with a specific format. 

2294 - cr_2 (str): The calculated statistic value formatted with a specific format. 

2295 - count (str): The count of occurrences divided by the count of opponent's check-raises. 

2296 - description (str): A description of the statistic. 

2297 """ 

2298 stat = 0.0 

2299 try: 

2300 ccr_opp_2 = float(stat_dict[player].get("ccr_opp_2", 0)) # Ensure key exists and default to 0 

2301 if ccr_opp_2 != 0: # Check to avoid division by zero 

2302 stat = float(stat_dict[player]["cr_2"]) / ccr_opp_2 

2303 return ( 

2304 stat, 

2305 "%3.1f" % (100.0 * stat), 

2306 "cr2=%3.1f%%" % (100.0 * stat), 

2307 "cr_2=%3.1f%%" % (100.0 * stat), 

2308 "(%d/%d)" % (stat_dict[player]["cr_2"], ccr_opp_2), 

2309 "% check-raise turn/5th street", 

2310 ) 

2311 except (KeyError, ValueError, TypeError): 

2312 return (stat, "NA", "cr2=NA", "cr_2=NA", "(0/0)", "% check-raise turn/5th street") 

2313 

2314 

2315def cr3(stat_dict, player): 

2316 """ 

2317 Calculate the river/6th street check-raise statistic for a given player. 

2318 

2319 Args: 

2320 stat_dict (dict): A dictionary containing player statistics. 

2321 player (int): The player for whom the statistic is calculated. 

2322 

2323 Returns: 

2324 tuple: A tuple containing various formatted strings representing the check-raise to fold ratio. 

2325 The tuple contains the following elements: 

2326 - stat (float): The calculated statistic value. 

2327 - percent (str): The calculated statistic value formatted as a percentage. 

2328 - cr3 (str): The calculated statistic value formatted with a specific format. 

2329 - cr_3 (str): The calculated statistic value formatted with a specific format. 

2330 - count (str): The count of occurrences divided by the count of opponent's check-raises. 

2331 - description (str): A description of the statistic. 

2332 

2333 Raises: 

2334 None 

2335 """ 

2336 stat = 0.0 

2337 try: 

2338 ccr_opp_3 = float(stat_dict[player].get("ccr_opp_3", 0)) # Ensure key exists and default to 0 

2339 if ccr_opp_3 != 0: # Check to avoid division by zero 

2340 stat = float(stat_dict[player]["cr_3"]) / ccr_opp_3 

2341 return ( 

2342 stat, 

2343 "%3.1f" % (100.0 * stat), 

2344 "cr3=%3.1f%%" % (100.0 * stat), 

2345 "cr_3=%3.1f%%" % (100.0 * stat), 

2346 "(%d/%d)" % (stat_dict[player]["cr_3"], ccr_opp_3), 

2347 "% check-raise river/6th street", 

2348 ) 

2349 except (KeyError, ValueError, TypeError): 

2350 return (stat, "NA", "cr3=NA", "cr_3=NA", "(0/0)", "% check-raise river/6th street") 

2351 

2352 

2353def cr4(stat_dict, player): 

2354 """ 

2355 Calculate the 7th street check-raise statistics for a given player on the 7th street. 

2356 

2357 Args: 

2358 stat_dict (dict): A dictionary containing player statistics. 

2359 player (int): The player for whom the statistic is calculated. 

2360 

2361 Returns: 

2362 tuple: A tuple containing various formatted strings representing the check-raise to fold ratio. 

2363 The tuple contains the following elements: 

2364 - stat (float): The calculated statistic value. 

2365 - percent (str): The calculated statistic value formatted as a percentage. 

2366 - cr4 (str): The calculated statistic value formatted with a specific format. 

2367 - cr_4 (str): The calculated statistic value formatted with a specific format. 

2368 - count (str): The count of occurrences divided by the count of opponent's check-raises. 

2369 - description (str): A description of the statistic. 

2370 

2371 Raises: 

2372 None 

2373 """ 

2374 stat = 0.0 

2375 try: 

2376 ccr_opp_4 = float(stat_dict[player].get("ccr_opp_4", 0)) # Ensure key exists and default to 0 

2377 if ccr_opp_4 != 0: # Check to avoid division by zero 

2378 stat = float(stat_dict[player]["cr_4"]) / ccr_opp_4 

2379 return ( 

2380 stat, 

2381 "%3.1f" % (100.0 * stat), 

2382 "cr4=%3.1f%%" % (100.0 * stat), 

2383 "cr_4=%3.1f%%" % (100.0 * stat), 

2384 "(%d/%d)" % (stat_dict[player]["cr_4"], ccr_opp_4), 

2385 "% check-raise 7th street", 

2386 ) 

2387 except (KeyError, ValueError, TypeError): 

2388 return (stat, "NA", "cr4=NA", "cr_4=NA", "(0/0)", "% check-raise 7th street") 

2389 

2390 

2391def game_abbr(stat_dict, player): 

2392 """ 

2393 Function to retrieve the abbreviation for a specific poker game based on the game category and limit type. 

2394 

2395 Parameters: 

2396 - stat_dict: Dictionary containing statistics related to the game. 

2397 - player: Integer representing the player number. 

2398 

2399 Returns: 

2400 - Tuple containing various representations of the game abbreviation. 

2401 """ 

2402 hand_instance = _global_hand_instance 

2403 stat = "" 

2404 try: 

2405 if hand_instance is None or "gametype" not in hand_instance: 

2406 # If hand_instance is None, return default empty values 

2407 return ("NA", "NA", "game=NA", "game_abbr=NA", "(NA)", "Game abbreviation") 

2408 

2409 cat_plus_limit = hand_instance.gametype["category"] + "." + hand_instance.gametype["limitType"] 

2410 stat = { 

2411 # ftp's 10-game with official abbreviations 

2412 "holdem.fl": "H", 

2413 "studhilo.fl": "E", 

2414 "omahahi.pl": "P", 

2415 "27_3draw.fl": "T", 

2416 "razz.fl": "R", 

2417 "holdem.nl": "N", 

2418 "omahahilo.fl": "O", 

2419 "studhi.fl": "S", 

2420 "27_1draw.nl": "K", 

2421 "badugi.fl": "B", 

2422 # other common games with dubious abbreviations 

2423 "fivedraw.fl": "F", 

2424 "fivedraw.pl": "Fp", 

2425 "fivedraw.nl": "Fn", 

2426 "27_3draw.pl": "Tp", 

2427 "27_3draw.nl": "Tn", 

2428 "badugi.pl": "Bp", 

2429 "badugi.hp": "Bh", 

2430 "omahahilo.pl": "Op", 

2431 "omahahilo.nl": "On", 

2432 "holdem.pl": "Hp", 

2433 "studhi.nl": "Sn", 

2434 }.get(cat_plus_limit, "Unknown") # Default to "Unknown" if not found 

2435 return (stat, "%s" % stat, "game=%s" % stat, "game_abbr=%s" % stat, "(%s)" % stat, "Game abbreviation") 

2436 except (KeyError, ValueError, TypeError): 

2437 return ("NA", "NA", "game=NA", "game_abbr=NA", "(NA)", "Game abbreviation") 

2438 

2439 

2440def blank(stat_dict, player): 

2441 # blank space on the grid 

2442 # stat = " " 

2443 return ("", "", "", "", "", "<blank>") 

2444 

2445 

2446################################################################################################ 

2447# NEW STATS 

2448 

2449 

2450def vpip_pfr_ratio(stat_dict, player): 

2451 """ 

2452 Calculate the VPIP/PFR ratio for a player. 

2453 

2454 This statistic represents the ratio between a player's VPIP (Voluntarily Put money In Pot) 

2455 and PFR (Pre-Flop Raise) percentages, which gives an indication of the player's preflop aggression. 

2456 

2457 Args: 

2458 stat_dict (dict): A dictionary containing player statistics. 

2459 player (int): The player for whom the statistic is calculated. 

2460 

2461 Returns: 

2462 tuple: A tuple containing the calculated statistic, formatted strings, and related information. 

2463 """ 

2464 try: 

2465 vpip_opp = float(stat_dict[player].get("vpip_opp", 0)) 

2466 pfr_opp = float(stat_dict[player].get("pfr_opp", 0)) 

2467 

2468 # Ensure vpip_opp and pfr_opp are not zero to avoid division by zero 

2469 vpip = float(stat_dict[player]["vpip"]) / vpip_opp if vpip_opp != 0 else 0 

2470 pfr = float(stat_dict[player]["pfr"]) / pfr_opp if pfr_opp != 0 else 0 

2471 

2472 if pfr > 0: 

2473 stat = vpip / pfr 

2474 else: 

2475 stat = float("inf") # Avoid division by zero for PFR 

2476 

2477 return ( 

2478 stat, 

2479 "%2.2f" % (stat), 

2480 "v/p=%2.2f" % (stat), 

2481 "vpip/pfr=%2.2f" % (stat), 

2482 "(%d/%d)/(%d/%d)" 

2483 % ( 

2484 stat_dict[player]["vpip"], 

2485 stat_dict[player]["vpip_opp"], 

2486 stat_dict[player]["pfr"], 

2487 stat_dict[player]["pfr_opp"], 

2488 ), 

2489 "VPIP/PFR ratio", 

2490 ) 

2491 except (KeyError, ValueError, TypeError): 

2492 return (float("inf"), "NA", "v/p=NA", "vpip/pfr=NA", "(0/0)/(0/0)", "VPIP/PFR ratio") 

2493 

2494 

2495def three_bet_range(stat_dict, player): 

2496 try: 

2497 # Retrieve and check for division by zero in PFR 

2498 pfr_opp = float(stat_dict[player].get("pfr_opp", 0)) 

2499 if pfr_opp != 0: 

2500 pfr = float(stat_dict[player]["pfr"]) / pfr_opp 

2501 else: 

2502 pfr = 0 # Avoid division by zero for PFR 

2503 

2504 # Retrieve and check for division by zero in 3-Bet 

2505 tb_opp_0 = float(stat_dict[player].get("tb_opp_0", 0)) 

2506 if tb_opp_0 != 0: 

2507 three_bet = float(stat_dict[player]["tb_0"]) / tb_opp_0 

2508 else: 

2509 three_bet = 0 # Avoid division by zero for 3-Bet 

2510 

2511 # Calculate the 3-Bet Range 

2512 stat = pfr * three_bet 

2513 return ( 

2514 stat, 

2515 "%3.1f" % (100.0 * stat), 

2516 "3BR=%3.1f%%" % (100.0 * stat), 

2517 "3BetRange=%3.1f%%" % (100.0 * stat), 

2518 "(%d/%d)*(%d/%d)" 

2519 % ( 

2520 stat_dict[player]["pfr"], 

2521 stat_dict[player]["pfr_opp"], 

2522 stat_dict[player]["tb_0"], 

2523 stat_dict[player]["tb_opp_0"], 

2524 ), 

2525 "3-Bet Range", 

2526 ) 

2527 except (KeyError, ValueError, TypeError): 

2528 return (0, "NA", "3BR=NA", "3BetRange=NA", "(0/0)*(0/0)", "3-Bet Range") 

2529 

2530 

2531def check_raise_frequency(stat_dict, player): 

2532 try: 

2533 # Sum the total check-raises and opportunities 

2534 total_cr = ( 

2535 stat_dict[player].get("cr_1", 0) + stat_dict[player].get("cr_2", 0) + stat_dict[player].get("cr_3", 0) 

2536 ) 

2537 total_opp = ( 

2538 stat_dict[player].get("ccr_opp_1", 0) 

2539 + stat_dict[player].get("ccr_opp_2", 0) 

2540 + stat_dict[player].get("ccr_opp_3", 0) 

2541 ) 

2542 

2543 # Check to avoid division by zero 

2544 if total_opp != 0: 

2545 stat = float(total_cr) / float(total_opp) 

2546 else: 

2547 stat = 0 # Avoid division by zero 

2548 

2549 return ( 

2550 stat, 

2551 "%3.1f" % (100.0 * stat), 

2552 "CRF=%3.1f%%" % (100.0 * stat), 

2553 "CheckRaiseFreq=%3.1f%%" % (100.0 * stat), 

2554 "(%d/%d)" % (total_cr, total_opp), 

2555 "Check-Raise Frequency", 

2556 ) 

2557 except (KeyError, ValueError, TypeError): 

2558 return (0, "NA", "CRF=NA", "CheckRaiseFreq=NA", "(0/0)", "Check-Raise Frequency") 

2559 

2560 

2561def river_call_efficiency(stat_dict, player): 

2562 try: 

2563 river_calls = stat_dict[player].get("call_3", 0) # Safely get river calls, defaulting to 0 

2564 showdowns_won = stat_dict[player].get("wmsd", 0) # Safely get showdowns won, defaulting to 0 

2565 

2566 # Calculate the efficiency, ensuring no division by zero 

2567 stat = float(showdowns_won) / float(river_calls) if river_calls > 0 else 0 

2568 

2569 return ( 

2570 stat, 

2571 "%3.1f" % (100.0 * stat), 

2572 "RCE=%3.1f%%" % (100.0 * stat), 

2573 "RiverCallEff=%3.1f%%" % (100.0 * stat), 

2574 "(%d/%d)" % (showdowns_won, river_calls), 

2575 "River Call Efficiency", 

2576 ) 

2577 except (KeyError, ValueError, TypeError): 

2578 return (0, "NA", "RCE=NA", "RiverCallEff=NA", "(0/0)", "River Call Efficiency") 

2579 

2580 

2581# 

2582# 

2583# 

2584################################################################################################# 

2585 

2586 

2587def starthands(stat_dict, player): 

2588 """ 

2589 Retrieves the starting hands and their positions for a specific player in a hand. 

2590 

2591 Args: 

2592 stat_dict (dict): A dictionary containing the statistics. 

2593 player (int): The ID of the player. 

2594 

2595 Returns: 

2596 tuple: A tuple containing the following: 

2597 - A string representing the starting hands and their positions. 

2598 - A string representing the starting hands and their positions. 

2599 - A string representing the starting hands and their positions. 

2600 - A string representing the starting hands and their positions. 

2601 - A string representing the starting hands and their positions. 

2602 - A string representing the title of the statistic. 

2603 

2604 Raises: 

2605 None. 

2606 

2607 Notes: 

2608 - This function retrieves the starting hands and their positions for a specific player in a hand. 

2609 - The starting hands and their positions are displayed in a specific format. 

2610 - The function uses a global variable `_global_hand_instance` to get the hand instance. 

2611 - The function executes a SQL query to retrieve the starting hands and their positions from the database. 

2612 - The function formats the retrieved data and returns it as a tuple. 

2613 

2614 """ 

2615 

2616 hand_instance = _global_hand_instance 

2617 if not hand_instance: 

2618 return ("", "", "", "", "", "Hands seen at this table") 

2619 

2620 # summary of known starting hands+position 

2621 # data volumes could get crazy here,so info is limited to hands 

2622 # in the current HH file only 

2623 

2624 # this info is NOT read from the cache, so does not obey aggregation 

2625 # parameters for other stats 

2626 

2627 # display shows 5 categories 

2628 # PFcall - limp or coldcall preflop 

2629 # PFaggr - raise preflop 

2630 # PFdefend - defended in BB 

2631 # PFcar 

2632 

2633 # hand is shown, followed by position indicator 

2634 # (b=SB/BB. l=Button/cutoff m=previous 3 seats to that, e=remainder) 

2635 

2636 # due to screen space required for this stat, it should only 

2637 # be used in the popup section i.e. 

2638 # <pu_stat pu_stat_name="starthands"> </pu_stat> 

2639 handid = int(hand_instance.handid_selected) 

2640 PFlimp = "Limped:" 

2641 PFaggr = "Raised:" 

2642 PFcar = "Called raise:" 

2643 PFdefendBB = "Defend BB:" 

2644 count_pfl = count_pfa = count_pfc = count_pfd = 5 

2645 

2646 c = Configuration.Config() 

2647 db_connection = Database.Database(c) 

2648 sc = db_connection.get_cursor() 

2649 

2650 query = ( 

2651 "SELECT distinct startCards, street0Aggr, street0CalledRaiseDone, " 

2652 + "case when HandsPlayers.position = 'B' then 'b' " 

2653 + "when HandsPlayers.position = 'S' then 'b' " 

2654 + "when HandsPlayers.position = '0' then 'l' " 

2655 + "when HandsPlayers.position = '1' then 'l' " 

2656 + "when HandsPlayers.position = '2' then 'm' " 

2657 + "when HandsPlayers.position = '3' then 'm' " 

2658 + "when HandsPlayers.position = '4' then 'm' " 

2659 + "when HandsPlayers.position = '5' then 'e' " 

2660 + "when HandsPlayers.position = '6' then 'e' " 

2661 + "when HandsPlayers.position = '7' then 'e' " 

2662 + "when HandsPlayers.position = '8' then 'e' " 

2663 + "when HandsPlayers.position = '9' then 'e' " 

2664 + "else 'X' end " 

2665 + "FROM Hands, HandsPlayers, Gametypes " 

2666 + "WHERE HandsPlayers.handId = Hands.id " 

2667 + " AND Gametypes.id = Hands.gametypeid " 

2668 + " AND Gametypes.type = " 

2669 + " (SELECT Gametypes.type FROM Gametypes, Hands " 

2670 + " WHERE Hands.gametypeid = Gametypes.id and Hands.id = %d) " 

2671 + " AND Gametypes.Limittype = " 

2672 + " (SELECT Gametypes.limitType FROM Gametypes, Hands " 

2673 + " WHERE Hands.gametypeid = Gametypes.id and Hands.id = %d) " 

2674 + "AND Gametypes.category = 'holdem' " 

2675 + "AND fileId = (SELECT fileId FROM Hands " 

2676 + " WHERE Hands.id = %d) " 

2677 + "AND HandsPlayers.playerId = %d " 

2678 + "AND street0VPI " 

2679 + "AND startCards > 0 AND startCards <> 170 " 

2680 + "ORDER BY startCards DESC " 

2681 + ";" 

2682 ) % (int(handid), int(handid), int(handid), int(player)) 

2683 

2684 # print query 

2685 sc.execute(query) 

2686 for qstartcards, qstreet0Aggr, qstreet0CalledRaiseDone, qposition in sc.fetchall(): 

2687 humancards = Card.decodeStartHandValue("holdem", qstartcards) 

2688 # print humancards, qstreet0Aggr, qstreet0CalledRaiseDone, qposition 

2689 if qposition == "b" and qstreet0CalledRaiseDone: 

2690 PFdefendBB = PFdefendBB + "/" + humancards 

2691 count_pfd += 1 

2692 if count_pfd / 8.0 == int(count_pfd / 8.0): 

2693 PFdefendBB = PFdefendBB + "\n" 

2694 elif qstreet0Aggr is True: 

2695 PFaggr = PFaggr + "/" + humancards + "." + qposition 

2696 count_pfa += 1 

2697 if count_pfa / 8.0 == int(count_pfa / 8.0): 

2698 PFaggr = PFaggr + "\n" 

2699 elif qstreet0CalledRaiseDone: 

2700 PFcar = PFcar + "/" + humancards + "." + qposition 

2701 count_pfc += 1 

2702 if count_pfc / 8.0 == int(count_pfc / 8.0): 

2703 PFcar = PFcar + "\n" 

2704 else: 

2705 PFlimp = PFlimp + "/" + humancards + "." + qposition 

2706 count_pfl += 1 

2707 if count_pfl / 8.0 == int(count_pfl / 8.0): 

2708 PFlimp = PFlimp + "\n" 

2709 sc.close() 

2710 

2711 returnstring = PFlimp + "\n" + PFaggr + "\n" + PFcar + "\n" + PFdefendBB # + "\n" + str(handid) 

2712 

2713 return ( 

2714 (returnstring), 

2715 (returnstring), 

2716 (returnstring), 

2717 (returnstring), 

2718 (returnstring), 

2719 "Hands seen at this table\n", 

2720 ) 

2721 

2722 

2723def get_valid_stats(): 

2724 """ 

2725 Function to retrieve valid stats descriptions. 

2726 

2727 Returns: 

2728 dict: A dictionary containing descriptions of valid stats. 

2729 """ 

2730 global _global_hand_instance 

2731 _global_hand_instance = None 

2732 

2733 stat_descriptions = {} 

2734 for function in STATLIST: 

2735 function_instance = getattr(__import__(__name__), function) 

2736 res = function_instance(None, None) 

2737 stat_descriptions[function] = res[5] 

2738 

2739 return stat_descriptions 

2740 

2741 

2742STATLIST = sorted(dir()) 

2743misslist = [ 

2744 "Configuration", 

2745 "Database", 

2746 "Charset", 

2747 "codecs", 

2748 "encoder", 

2749 "GInitiallyUnowned", 

2750 "gtk", 

2751 "pygtk", 

2752 "Card", 

2753 "L10n", 

2754 "log", 

2755 "logging", 

2756 "Decimal", 

2757 "GFileDescriptorBased", 

2758 "GPollableInputStream", 

2759 "GPollableOutputStream", 

2760 "re", 

2761 "re_Places", 

2762 "Hand", 

2763] 

2764STATLIST = [x for x in STATLIST if x not in ("do_stat", "do_tip", "get_valid_stats")] 

2765STATLIST = [x for x in STATLIST if not x.startswith("_")] 

2766STATLIST = [x for x in STATLIST if x not in dir(sys)] 

2767STATLIST = [x for x in STATLIST if x not in dir(codecs)] 

2768STATLIST = [x for x in STATLIST if x not in misslist] 

2769# print "STATLIST is", STATLIST 

2770 

2771if __name__ == "__main__": 

2772 c = Configuration.Config() 

2773 db_connection = Database.Database(c) 

2774 h = db_connection.get_last_hand() 

2775 stat_dict = db_connection.get_stats_from_hand(h, "ring") 

2776 hand_instance = Hand.hand_factory(h, c, db_connection) 

2777 

2778 for player in stat_dict.keys(): 

2779 print(f"Example stats. Player = {player}, Hand = {h}:") 

2780 for attr in STATLIST: 

2781 print(attr, " : ", do_stat(stat_dict, player=player, stat=attr, hand_instance=hand_instance)) 

2782 break 

2783 

2784 print() 

2785 print("Legal stats:") 

2786 print("(add _0 to name to display with 0 decimal places, _1 to display with 1, etc)") 

2787 stat_descriptions = get_valid_stats() 

2788 for stat in STATLIST: 

2789 print(stat, " : ", stat_descriptions[stat])