import copy import math import logging from decimal import Decimal from card_layout import ( FullPitchingCard, PLAY_RESULTS, PlayResult, EXACT_CHANCES, get_chances, ) from pitchers.models import PitchingCardRatingsModel logger = logging.getLogger(__name__) def build_pitcher_full_cards( ratings_vl: PitchingCardRatingsModel, ratings_vr: PitchingCardRatingsModel, offense_col: int, player_id: int, hand: str, ) -> tuple: """Build vL and vR FullPitchingCard objects from pre-calculated ratings. Returns (vl_card, vr_card). """ player_binary = player_id % 2 vl = FullPitchingCard(offense_col=offense_col, alt_direction=player_binary) vr = FullPitchingCard(offense_col=offense_col, alt_direction=player_binary) def assign_pchances(this_card, play, chances, secondary_play=None): r_data = this_card.add_result(play, chances, secondary_play) if r_data: return float(r_data[0]), float(r_data[1]) else: for x in EXACT_CHANCES + [Decimal("0.95")]: if x < math.floor(chances - Decimal("0.05")): r_data = this_card.add_result( play, Decimal(math.floor(chances)), secondary_play ) if r_data: return float(r_data[0]), float(r_data[1]) break if x < chances and secondary_play is not None: r_data = this_card.add_result(play, x, secondary_play) if r_data: return float(r_data[0]), float(r_data[1]) return 0, 0 def get_preferred_mif(ratings): if hand == "L" and ratings.vs_hand == "L": return "ss" elif hand == "L" or (hand == "R" and ratings.vs_hand == "R"): return "2b" else: return "ss" for card, data, vs_hand in [ (vl, copy.deepcopy(ratings_vl), "L"), (vr, copy.deepcopy(ratings_vr), "R"), ]: new_ratings = PitchingCardRatingsModel( pitchingcard_id=data.pitchingcard_id, pit_hand=data.pit_hand, vs_hand=vs_hand, hard_rate=data.hard_rate, med_rate=data.med_rate, soft_rate=data.soft_rate, xcheck_p=0.0, xcheck_c=0.0, xcheck_1b=0.0, xcheck_2b=0.0, xcheck_3b=0.0, xcheck_ss=0.0, xcheck_lf=0.0, xcheck_cf=0.0, xcheck_rf=0.0, ) res_chances = data.bp_homerun while res_chances > 0: ch = get_chances(res_chances) r_val = assign_pchances(card, PLAY_RESULTS["bp-hr"], ch) res_chances -= r_val[0] new_ratings.bp_homerun += r_val[0] res_chances = data.hbp while res_chances > 0: ch = get_chances(res_chances) r_val = assign_pchances( card, PlayResult(full_name="HBP", short_name="HBP"), ch ) res_chances -= r_val[0] new_ratings.hbp += r_val[0] if r_val[0] == 0: break res_chances = data.xcheck_p while res_chances > 0: ch = get_chances(res_chances) r_val = assign_pchances( card, PlayResult(full_name="GB (p) X", short_name="GB (p) X"), ch ) res_chances -= r_val[0] new_ratings.xcheck_p += r_val[0] res_chances = data.xcheck_c while res_chances > 0: ch = get_chances(res_chances) r_val = assign_pchances( card, PlayResult(full_name="CATCH X", short_name="CATCH X"), ch ) res_chances -= r_val[0] new_ratings.xcheck_c += r_val[0] res_chances = data.xcheck_1b while res_chances > 0: ch = get_chances(res_chances) r_val = assign_pchances( card, PlayResult(full_name="GB (1b) X", short_name="GB (1b) X"), ch ) res_chances -= r_val[0] new_ratings.xcheck_1b += r_val[0] res_chances = data.xcheck_3b while res_chances > 0: ch = get_chances(res_chances) r_val = assign_pchances( card, PlayResult(full_name="GB (3b) X", short_name="GB (3b) X"), ch ) res_chances -= r_val[0] new_ratings.xcheck_3b += r_val[0] res_chances = data.xcheck_rf while res_chances > 0: ch = get_chances(res_chances) r_val = assign_pchances( card, PlayResult(full_name="FLY (rf) X", short_name="FLY (rf) X"), ch ) res_chances -= r_val[0] new_ratings.xcheck_rf += r_val[0] res_chances = data.xcheck_lf while res_chances > 0: ch = get_chances(res_chances) r_val = assign_pchances( card, PlayResult(full_name="FLY (lf) X", short_name="FLY (lf) X"), ch ) res_chances -= r_val[0] new_ratings.xcheck_lf += r_val[0] res_chances = data.xcheck_2b while res_chances > 0: ch = get_chances(res_chances) r_val = assign_pchances( card, PlayResult(full_name="GB (2b) X", short_name="GB (2b) X"), ch ) res_chances -= r_val[0] new_ratings.xcheck_2b += r_val[0] res_chances = data.xcheck_cf while res_chances > 0: ch = get_chances(res_chances) r_val = assign_pchances( card, PlayResult(full_name="FLY (cf) X", short_name="FLY (cf) X"), ch ) res_chances -= r_val[0] new_ratings.xcheck_cf += r_val[0] res_chances = data.xcheck_ss while res_chances > 0: ch = get_chances(res_chances) r_val = assign_pchances( card, PlayResult(full_name="GB (ss) X", short_name="GB (ss) X"), ch ) res_chances -= r_val[0] new_ratings.xcheck_ss += r_val[0] res_chances = data.walk while res_chances >= 1: ch = get_chances(res_chances) if data.strikeout > max(1 - ch, 0): secondary = PlayResult(full_name="strikeout", short_name="so") else: secondary = None r_val = assign_pchances(card, PLAY_RESULTS["walk"], ch, secondary) res_chances -= r_val[0] new_ratings.walk += r_val[0] if r_val[1] > 0: data.strikeout -= r_val[1] new_ratings.strikeout += r_val[1] if r_val[0] == 0: break res_chances = data.homerun retries = 0 while res_chances > 0: if res_chances < 1 or retries > 0: if data.double_cf > 0: data.double_cf += res_chances elif data.double_two > 0: data.double_two += res_chances elif data.triple > 0: data.triple += res_chances elif data.single_two > 0: data.single_two += res_chances elif data.single_center > 0: data.single_center += res_chances elif data.single_one > 0: data.single_one += res_chances break ch = get_chances(res_chances) if data.double_cf > ( data.flyout_rf_b + data.flyout_lf_b ) and data.double_cf > max(1 - ch, 0): secondary = PLAY_RESULTS["do-cf"] elif data.flyout_cf_b > max(1 - ch, 0): secondary = PLAY_RESULTS["fly-cf"] elif data.flyout_lf_b > data.flyout_rf_b and data.flyout_lf_b > max( 1 - ch, 0 ): secondary = PLAY_RESULTS["fly-lf"] elif data.flyout_rf_b > max(1 - ch, 0): secondary = PLAY_RESULTS["fly-rf"] elif data.double_cf > max(1 - ch, 0): secondary = PLAY_RESULTS["do-cf"] elif data.double_three > max(1 - ch, 0): secondary = PLAY_RESULTS["do***"] elif data.double_two > max(1 - ch, 0): secondary = PLAY_RESULTS["do**"] elif data.triple > max(1 - ch, 0): secondary = PLAY_RESULTS["tr"] else: secondary = None r_val = assign_pchances(card, PLAY_RESULTS["hr"], ch, secondary) res_chances -= r_val[0] new_ratings.homerun += r_val[0] if r_val[1] > 0: if "DO (" in secondary.short_name: data.double_cf -= r_val[1] new_ratings.double_cf += r_val[1] elif "lf" in secondary.short_name: data.flyout_lf_b -= r_val[1] new_ratings.flyout_lf_b += r_val[1] elif "cf" in secondary.short_name: data.flyout_cf_b -= r_val[1] new_ratings.flyout_cf_b += r_val[1] elif "rf" in secondary.short_name: data.flyout_rf_b -= r_val[1] new_ratings.flyout_rf_b += r_val[1] elif "***" in secondary.short_name: data.double_three -= r_val[1] new_ratings.double_three += r_val[1] elif "**" in secondary.short_name: data.double_two -= r_val[1] new_ratings.double_two += r_val[1] elif "TR" in secondary.short_name: data.triple -= r_val[1] new_ratings.triple += r_val[1] if r_val[0] == 0: retries += 1 res_chances = data.triple retries = 0 while res_chances > 0: if res_chances < 1 or retries > 0: if data.double_cf > 0: data.double_cf += res_chances elif data.double_two > 0: data.double_two += res_chances elif data.single_two > 0: data.single_two += res_chances elif data.single_center > 0: data.single_center += res_chances elif data.single_one > 0: data.single_one += res_chances break ch = get_chances(res_chances) if data.single_two > max(1 - ch, 0): secondary = PLAY_RESULTS["si**"] elif data.flyout_cf_b > max(1 - ch, 0): secondary = PLAY_RESULTS["fly-cf"] elif data.flyout_lf_b > max(1 - ch, 0): secondary = PLAY_RESULTS["fly-lf"] elif data.flyout_rf_b > max(1 - ch, 0): secondary = PLAY_RESULTS["fly-rf"] elif data.double_cf > max(1 - ch, 0): secondary = PLAY_RESULTS["do-cf"] elif data.double_three > max(1 - ch, 0): secondary = PLAY_RESULTS["do***"] elif data.double_two > max(1 - ch, 0): secondary = PLAY_RESULTS["do**"] else: secondary = None r_val = assign_pchances(card, PLAY_RESULTS["tr"], ch, secondary) res_chances -= r_val[0] new_ratings.triple += r_val[0] if r_val[1] > 0: if "DO (" in secondary.short_name: data.double_cf -= r_val[1] new_ratings.double_cf += r_val[1] elif "lf" in secondary.short_name: data.flyout_lf_b -= r_val[1] new_ratings.flyout_lf_b += r_val[1] elif "cf" in secondary.short_name: data.flyout_cf_b -= r_val[1] new_ratings.flyout_cf_b += r_val[1] elif "rf" in secondary.short_name: data.flyout_rf_b -= r_val[1] new_ratings.flyout_rf_b += r_val[1] elif "***" in secondary.short_name: data.double_three -= r_val[1] new_ratings.double_three += r_val[1] elif "SI" in secondary.short_name: data.single_two -= r_val[1] new_ratings.single_two += r_val[1] elif "**" in secondary.short_name: data.double_two -= r_val[1] new_ratings.double_two += r_val[1] if r_val[0] == 0: retries += 1 res_chances = data.double_three retries = 0 while res_chances > 0: if res_chances < 1 or retries > 0: if data.double_cf > 0: data.double_cf += res_chances elif data.double_two > 0: data.double_two += res_chances elif data.single_two > 0: data.single_two += res_chances elif data.single_center > 0: data.single_center += res_chances elif data.single_one > 0: data.single_one += res_chances break ch = get_chances(res_chances) if data.single_two > max(1 - ch, 0): secondary = PLAY_RESULTS["si**"] elif data.flyout_cf_b > max(1 - ch, 0): secondary = PLAY_RESULTS["fly-cf"] elif data.flyout_lf_b > max(1 - ch, 0): secondary = PLAY_RESULTS["fly-lf"] elif data.flyout_rf_b > max(1 - ch, 0): secondary = PLAY_RESULTS["fly-rf"] elif data.double_cf > max(1 - ch, 0): secondary = PLAY_RESULTS["do-cf"] elif data.double_two > max(1 - ch, 0): secondary = PLAY_RESULTS["do**"] else: secondary = None r_val = assign_pchances(card, PLAY_RESULTS["do***"], ch, secondary) res_chances -= r_val[0] new_ratings.double_three += r_val[0] if r_val[1] > 0: if "DO (" in secondary.short_name: data.double_cf -= r_val[1] new_ratings.double_cf += r_val[1] elif "lf" in secondary.short_name: data.flyout_lf_b -= r_val[1] new_ratings.flyout_lf_b += r_val[1] elif "cf" in secondary.short_name: data.flyout_cf_b -= r_val[1] new_ratings.flyout_cf_b += r_val[1] elif "rf" in secondary.short_name: data.flyout_rf_b -= r_val[1] new_ratings.flyout_rf_b += r_val[1] elif "SI" in secondary.short_name: data.single_two -= r_val[1] new_ratings.single_two += r_val[1] elif "**" in secondary.short_name: data.double_two -= r_val[1] new_ratings.double_two += r_val[1] if r_val[0] == 0: retries += 1 res_chances = data.double_cf retries = 0 while res_chances > 0: if res_chances < 1 or retries > 0: if data.double_two > 0: data.double_two += res_chances elif data.single_two > 0: data.single_two += res_chances elif data.single_center > 0: data.single_center += res_chances elif data.single_one > 0: data.single_one += res_chances break ch = get_chances(res_chances) if data.flyout_cf_b > max(1 - ch, 0): secondary = PlayResult(full_name="fly (cf) B", short_name="fly B") elif data.flyout_lf_b > max(1 - ch, 0): secondary = PlayResult(full_name="fly (lf) B", short_name="fly B") elif data.flyout_rf_b > max(1 - ch, 0): secondary = PlayResult(full_name="fly (rf) B", short_name="fly b") elif data.single_one > max(1 - ch, 0): secondary = PLAY_RESULTS["si*"] elif data.single_two > max(1 - ch, 0): secondary = PLAY_RESULTS["si**"] else: secondary = None r_val = assign_pchances(card, PLAY_RESULTS["do-cf"], ch, secondary) res_chances -= r_val[0] new_ratings.double_cf += r_val[0] if r_val[1] > 0: if "lf" in secondary.full_name: data.flyout_lf_b -= r_val[1] new_ratings.flyout_lf_b += r_val[1] elif "rf" in secondary.full_name: data.flyout_rf_b -= r_val[1] new_ratings.flyout_rf_b += r_val[1] elif "cf" in secondary.full_name: data.flyout_cf_b -= r_val[1] new_ratings.flyout_cf_b += r_val[1] elif "***" in secondary.short_name: data.double_three -= r_val[1] new_ratings.double_three += r_val[1] elif "SI" in secondary.short_name: data.single_two -= r_val[1] new_ratings.single_two += r_val[1] elif "**" in secondary.short_name: data.double_two -= r_val[1] new_ratings.double_two += r_val[1] if r_val[0] == 0: retries += 1 res_chances = data.double_two retries = 0 while res_chances > 0: if res_chances < 1 or retries > 0: if data.single_two > 0: data.single_two += res_chances elif data.single_center > 0: data.single_center += res_chances elif data.single_one > 0: data.single_one += res_chances elif data.walk > 0: data.walk += res_chances break ch = get_chances(res_chances) if data.single_two > max(1 - ch, 0): secondary = PLAY_RESULTS["si**"] elif data.single_center > max(1 - ch, 0): secondary = PLAY_RESULTS["si-cf"] elif data.flyout_cf_b > max(1 - ch, 0): secondary = PLAY_RESULTS["fly-cf"] elif data.flyout_lf_b > max(1 - ch, 0): secondary = PLAY_RESULTS["fly-lf"] elif data.flyout_rf_b > max(1 - ch, 0): secondary = PLAY_RESULTS["fly-rf"] else: secondary = None r_val = assign_pchances(card, PLAY_RESULTS["do**"], ch, secondary) res_chances -= r_val[0] new_ratings.double_two += r_val[0] if r_val[1] > 0: if "lf" in secondary.full_name: data.flyout_lf_b -= r_val[1] new_ratings.flyout_lf_b += r_val[1] elif "rf" in secondary.full_name: data.flyout_rf_b -= r_val[1] new_ratings.flyout_rf_b += r_val[1] elif "cf" in secondary.full_name: data.flyout_cf_b -= r_val[1] new_ratings.flyout_cf_b += r_val[1] elif "SI" in secondary.short_name: data.single_two -= r_val[1] new_ratings.single_two += r_val[1] if r_val[0] == 0: retries += 1 res_chances = data.single_two retries = 0 while res_chances > 0: if res_chances < 1 or retries > 0: if data.single_center > 0: data.single_center += res_chances elif data.single_one > 0: data.single_one += res_chances elif data.walk > 0: data.walk += res_chances break pref_mif = get_preferred_mif(new_ratings) ch = get_chances(res_chances) if data.groundout_a > max(1 - ch, 0): temp_mif = get_preferred_mif(new_ratings) pref_mif = "ss" if temp_mif == "2b" else "2b" secondary = PlayResult( full_name=f"gb ({pref_mif}) A", short_name=f"gb ({pref_mif}) A" ) elif data.groundout_b > max(1 - ch, 0): secondary = PlayResult( full_name=f"gb ({pref_mif}) B", short_name=f"gb ({pref_mif}) B" ) elif data.flyout_cf_b > max(1 - ch, 0): secondary = PLAY_RESULTS["fly-cf"] elif data.flyout_lf_b > max(1 - ch, 0): secondary = PLAY_RESULTS["fly-lf"] elif data.flyout_rf_b > max(1 - ch, 0): secondary = PLAY_RESULTS["fly-rf"] else: secondary = None r_val = assign_pchances(card, PLAY_RESULTS["si**"], ch, secondary) res_chances -= r_val[0] new_ratings.single_two += r_val[0] if r_val[1] > 0: if "lf" in secondary.full_name: data.flyout_lf_b -= r_val[1] new_ratings.flyout_lf_b += r_val[1] elif "rf" in secondary.full_name: data.flyout_rf_b -= r_val[1] new_ratings.flyout_rf_b += r_val[1] elif "cf" in secondary.full_name: data.flyout_cf_b -= r_val[1] new_ratings.flyout_cf_b += r_val[1] elif "B" in secondary.short_name: data.groundout_b -= r_val[1] new_ratings.groundout_b += r_val[1] elif "A" in secondary.short_name: data.groundout_a -= r_val[1] new_ratings.groundout_a += r_val[1] if r_val[0] == 0: retries += 1 res_chances = data.single_center retries = 0 while res_chances > 0: if res_chances < 1 or retries > 0: if data.single_one > 0: data.single_one += res_chances elif data.walk > 0: data.walk += res_chances break ch = get_chances(res_chances) if data.flyout_cf_b > max(1 - ch, 0): secondary = PlayResult(full_name="fly (cf) B", short_name="fly B") elif ( data.flyout_lf_b > max(1 - ch, 0) and data.flyout_lf_b > data.flyout_rf_b ): secondary = PlayResult(full_name="fly (lf) B", short_name="fly B") elif data.flyout_rf_b > max(1 - ch, 0): secondary = PlayResult(full_name="fly (rf) B", short_name="fly B") elif data.flyout_lf_b > max(1 - ch, 0): secondary = PlayResult(full_name="fly (lf) B", short_name="fly B") else: secondary = None r_val = assign_pchances(card, PLAY_RESULTS["si-cf"], ch, secondary) res_chances -= r_val[0] new_ratings.single_center += r_val[0] if r_val[1] > 0: if "CF" in secondary.short_name: data.flyout_cf_b -= r_val[1] new_ratings.flyout_cf_b += r_val[1] elif "LF" in secondary.full_name: data.flyout_lf_b -= r_val[1] new_ratings.flyout_lf_b += r_val[1] elif "RF" in secondary.full_name: data.flyout_rf_b -= r_val[1] new_ratings.flyout_rf_b += r_val[1] if r_val[0] == 0: retries += 1 res_chances = data.single_one retries = 0 while res_chances > 0: if res_chances < 1 or retries > 0: if data.walk > 0: data.walk += res_chances break pref_mif = get_preferred_mif(new_ratings) ch = get_chances(res_chances) if data.groundout_b > max(1 - ch, 0): secondary = PlayResult( full_name=f"gb ({pref_mif}) B", short_name=f"gb ({pref_mif}) B" ) elif data.groundout_a > max(1 - ch, 0): temp_mif = get_preferred_mif(new_ratings) pref_mif = "ss" if temp_mif == "2b" else "2b" secondary = PlayResult( full_name=f"gb ({pref_mif}) A", short_name=f"gb ({pref_mif}) A" ) else: secondary = None r_val = assign_pchances(card, PLAY_RESULTS["si*"], ch, secondary) res_chances -= r_val[0] new_ratings.single_one += r_val[0] if r_val[1] > 0: if "B" in secondary.short_name: data.groundout_b -= r_val[1] new_ratings.groundout_b += r_val[1] elif "A" in secondary.short_name: data.groundout_a -= r_val[1] new_ratings.groundout_a += r_val[1] if r_val[0] == 0: retries += 1 res_chances = data.bp_single retries = 0 while res_chances > 0: if retries > 0: break ch = get_chances(res_chances) r_val = assign_pchances(card, PLAY_RESULTS["bp-si"], ch) res_chances -= r_val[0] new_ratings.bp_single += r_val[0] if r_val[0] == 0: retries += 1 res_chances = data.strikeout retries = 0 while res_chances > 0: if retries > 0: break ch = get_chances(res_chances) r_val = assign_pchances( card, PlayResult(full_name="strikeout", short_name="so"), ch ) res_chances -= r_val[0] new_ratings.strikeout += r_val[0] if r_val[0] == 0: retries += 1 res_chances = data.flyout_cf_b retries = 0 while res_chances > 0: if retries > 0: break ch = get_chances(res_chances) r_val = assign_pchances(card, PLAY_RESULTS["fly-cf"], ch) res_chances -= r_val[0] new_ratings.flyout_cf_b += r_val[0] if r_val[0] == 0: retries += 1 res_chances = data.flyout_lf_b retries = 0 while res_chances > 0: if retries > 0: break ch = get_chances(res_chances) r_val = assign_pchances(card, PLAY_RESULTS["fly-lf"], ch) res_chances -= r_val[0] new_ratings.flyout_lf_b += r_val[0] if r_val[0] == 0: retries += 1 res_chances = data.flyout_rf_b retries = 0 while res_chances > 0: if retries > 0: break ch = get_chances(res_chances) r_val = assign_pchances(card, PLAY_RESULTS["fly-rf"], ch) res_chances -= r_val[0] new_ratings.flyout_rf_b += r_val[0] if r_val[0] == 0: retries += 1 res_chances = data.groundout_a retries = 0 while res_chances > 0: if retries > 0: break temp_mif = get_preferred_mif(new_ratings) pref_mif = "ss" if temp_mif == "2b" else "2b" ch = get_chances(res_chances) r_val = assign_pchances( card, PlayResult( full_name=f"gb ({pref_mif}) A", short_name=f"gb ({pref_mif}) A" ), ch, ) res_chances -= r_val[0] new_ratings.groundout_a += r_val[0] if r_val[0] == 0: retries += 1 res_chances = data.groundout_b retries = 0 while res_chances > 0: if retries > 0: break pref_mif = get_preferred_mif(new_ratings) ch = get_chances(res_chances) r_val = assign_pchances( card, PlayResult( full_name=f"gb ({pref_mif}) B", short_name=f"gb ({pref_mif}) B" ), ch, ) res_chances -= r_val[0] new_ratings.groundout_b += r_val[0] if r_val[0] == 0: retries += 1 plays = sorted( [ (data.strikeout, "so"), (data.groundout_a, "gb"), (data.flyout_lf_b, "lf"), (data.flyout_rf_b, "rf"), ], key=lambda z: z[0], reverse=True, ) count_filler = -1 pref_mif = get_preferred_mif(new_ratings) while not card.is_complete(): count_filler += 1 this_play = plays[count_filler % 4] if this_play[1] == "so": play_res = PlayResult(full_name="strikeout", short_name="strikeout") elif this_play[1] == "gb": this_if = "3b" if pref_mif == "ss" else "1b" play_res = PlayResult( full_name=f"gb ({this_if}) A", short_name=f"gb ({this_if}) A" ) elif this_play[1] == "lf": play_res = PLAY_RESULTS["fly-lf"] else: play_res = PLAY_RESULTS["fly-rf"] r_raw = card.card_fill(play_res) r_val = (float(r_raw[0]), float(r_raw[1])) if this_play[1] == "so": new_ratings.strikeout += r_val[0] elif this_play[1] == "gb": new_ratings.groundout_a += r_val[0] elif this_play[1] == "lf": new_ratings.flyout_lf_b += r_val[0] else: new_ratings.flyout_rf_b += r_val[0] card.add_fatigue() new_ratings.calculate_rate_stats() return vl, vr