paper-dynasty-card-creation/pitchers/card_builder.py
Cal Corum a72abc01a3 Add FullCard/CardColumn/CardResult models and card builder pipeline
- card_layout.py: Port PlayResult, PLAY_RESULTS, EXACT_CHANCES, get_chances(),
  CardResult, CardColumn, FullCard, FullBattingCard, FullPitchingCard from
  database/app/card_creation.py. card_output() uses col_* key names.
  get_chances() always returns Decimal to avoid float/Decimal type errors.

- batters/card_builder.py: Port get_batter_card_data() algorithm as
  build_batter_full_cards(ratings_vl, ratings_vr, offense_col, player_id, hand).
  assign_bchances() returns float tuples for compatibility with float-based
  BattingCardRatingsModel fields.

- pitchers/card_builder.py: Port get_pitcher_card_data() algorithm as
  build_pitcher_full_cards(). assign_pchances() returns float tuples.
  Includes card.add_fatigue() at end of each card iteration.

- batters/calcs_batter.py: Integrate card builder in get_batter_ratings().
  After computing raw ratings, call build_batter_full_cards() and merge
  9 col_* rendered column fields into each ratings dict. Lazy import to
  avoid circular dependency.

- pitchers/calcs_pitcher.py: Same integration for get_pitcher_ratings().

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-02-25 16:21:26 -06:00

713 lines
28 KiB
Python

import copy
import math
import logging
from decimal import Decimal
from card_layout import FullPitchingCard, PLAY_RESULTS, PlayResult, EXACT_CHANCES, get_chances
from pitchers.calcs_pitcher 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_center > 0:
data.single_center += 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 '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]
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]
elif 'cf' in secondary.full_name:
data.flyout_cf_b -= 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