Standardize formatting with black and apply ruff auto-fixes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
625 lines
21 KiB
Python
625 lines
21 KiB
Python
"""
|
|
Tony Smehrik - Custom Pitcher Card Preview
|
|
|
|
FIRST PASS - Target specs:
|
|
- Name: Tony Smehrik
|
|
- Hand: Left
|
|
- Position: RP, SP (starter_rating: 5, relief_rating: 5, closer_rating: None)
|
|
- Target Combined OPS: 0.585
|
|
- Target OPS vs L: 0.495 (dominant vs lefties - same-side advantage)
|
|
- K-rate vs L: average (~20%)
|
|
- K-rate vs R: very low (~12%)
|
|
- Flyball rate vs L: average (~35%)
|
|
- Flyball rate vs R: high (~45%)
|
|
- Team: Custom Ballplayers
|
|
- Cardset ID: 29
|
|
|
|
Uses CORRECT pitcher schema with:
|
|
- double_cf (not double_pull)
|
|
- flyout_lf_b, flyout_cf_b, flyout_rf_b (no flyout_a, flyout_bq)
|
|
- groundout_a, groundout_b only (no groundout_c)
|
|
- xcheck fields (P, C, 1B, 2B, 3B, SS, LF, CF, RF) = 29 chances total
|
|
"""
|
|
|
|
from creation_helpers import mround, sanitize_chance_output
|
|
|
|
# Default x-check values (from calcs_pitcher.py)
|
|
DEFAULT_XCHECKS = {
|
|
"xcheck_p": 1.0,
|
|
"xcheck_c": 3.0,
|
|
"xcheck_1b": 2.0,
|
|
"xcheck_2b": 6.0,
|
|
"xcheck_3b": 3.0,
|
|
"xcheck_ss": 7.0,
|
|
"xcheck_lf": 2.0,
|
|
"xcheck_cf": 3.0,
|
|
"xcheck_rf": 2.0,
|
|
}
|
|
TOTAL_XCHECK = sum(DEFAULT_XCHECKS.values()) # 29.0
|
|
|
|
|
|
def calculate_pitcher_rating(
|
|
vs_hand: str,
|
|
pit_hand: str,
|
|
avg: float,
|
|
obp: float,
|
|
slg: float,
|
|
bb_pct: float,
|
|
k_pct: float,
|
|
hr_per_hit: float,
|
|
triple_per_hit: float,
|
|
double_per_hit: float,
|
|
fb_pct: float,
|
|
gb_pct: float,
|
|
hard_pct: float,
|
|
med_pct: float,
|
|
soft_pct: float,
|
|
oppo_pct: float,
|
|
hr_fb_pct: float = 0.10,
|
|
) -> dict:
|
|
"""
|
|
Calculate pitcher card ratings using the correct schema.
|
|
|
|
Total chances = 108:
|
|
- Hits (HR, BP-HR, 3B, 2B, 1B variants)
|
|
- On-base (BB, HBP)
|
|
- Outs (K, flyouts, groundouts)
|
|
- X-checks (29 chances that redirect to pitcher's card)
|
|
"""
|
|
# Calculate base chances (108 total, minus 29 for x-checks = 79 "real" chances)
|
|
# But wait - the x-checks ARE part of the 108. They're outs that redirect.
|
|
# So: hits + ob + outs = 108, where outs includes K, flyouts, groundouts, AND xchecks
|
|
|
|
# From calcs_pitcher.py, total_chances includes xchecks in the out count
|
|
# Let's calculate hits and OB first, then outs fill the rest
|
|
|
|
# Hits allowed (adjusted for the 1.2x offense modifier used in game)
|
|
# Note: calcs_pitcher subtracts 0.05 from AVG for BP results
|
|
all_hits = sanitize_chance_output((avg - 0.05) * 108)
|
|
|
|
# OB (walks + HBP)
|
|
all_other_ob = sanitize_chance_output(
|
|
min(bb_pct * 108, 0.8 * 108)
|
|
) # Cap at ~86 chances
|
|
|
|
# Outs (everything else)
|
|
all_outs = mround(108 - all_hits - all_other_ob, base=0.5)
|
|
|
|
# ===== HITS DISTRIBUTION =====
|
|
# Singles
|
|
single_pct = 1.0 - hr_per_hit - triple_per_hit - double_per_hit
|
|
total_singles = sanitize_chance_output(all_hits * single_pct)
|
|
|
|
bp_single = 5.0 if total_singles >= 5 else 0.0
|
|
rem_singles = total_singles - bp_single
|
|
|
|
# Distribute singles based on contact quality
|
|
single_two = sanitize_chance_output(rem_singles / 2) if hard_pct >= 0.2 else 0.0
|
|
rem_singles -= single_two
|
|
|
|
single_one = sanitize_chance_output(rem_singles) if soft_pct >= 0.2 else 0.0
|
|
rem_singles -= single_one
|
|
|
|
single_center = sanitize_chance_output(rem_singles)
|
|
|
|
# XBH
|
|
rem_xbh = all_hits - bp_single - single_two - single_one - single_center
|
|
|
|
# Doubles
|
|
xbh_total = hr_per_hit + triple_per_hit + double_per_hit
|
|
if xbh_total > 0:
|
|
do_rate = double_per_hit / xbh_total
|
|
tr_rate = triple_per_hit / xbh_total
|
|
hr_rate = hr_per_hit / xbh_total
|
|
else:
|
|
do_rate = tr_rate = hr_rate = 0.0
|
|
|
|
raw_doubles = sanitize_chance_output(rem_xbh * do_rate)
|
|
double_two = raw_doubles if soft_pct > 0.2 else 0.0
|
|
double_cf = mround(raw_doubles - double_two)
|
|
double_three = 0.0 # Reserved for special cases
|
|
|
|
rem_xbh -= double_two + double_cf + double_three
|
|
|
|
# Triples
|
|
triple = sanitize_chance_output(rem_xbh * tr_rate)
|
|
rem_xbh = mround(rem_xbh - triple)
|
|
|
|
# Home runs
|
|
raw_hr = rem_xbh
|
|
if hr_fb_pct < 0.08:
|
|
bp_homerun = sanitize_chance_output(raw_hr, min_chances=1.0, rounding=1.0)
|
|
homerun = 0.0
|
|
elif hr_fb_pct > 0.28:
|
|
homerun = raw_hr
|
|
bp_homerun = 0.0
|
|
elif hr_fb_pct > 0.18:
|
|
bp_homerun = sanitize_chance_output(raw_hr * 0.4, min_chances=1.0, rounding=1.0)
|
|
homerun = mround(raw_hr - bp_homerun)
|
|
else:
|
|
bp_homerun = sanitize_chance_output(
|
|
raw_hr * 0.75, min_chances=1.0, rounding=1.0
|
|
)
|
|
homerun = mround(raw_hr - bp_homerun)
|
|
|
|
# ===== ON-BASE DISTRIBUTION =====
|
|
# Assume 90% walks, 10% HBP
|
|
hbp = mround(all_other_ob * 0.10)
|
|
walk = mround(all_other_ob - hbp)
|
|
|
|
# ===== OUTS DISTRIBUTION =====
|
|
# Strikeouts (adjusted for K%)
|
|
# K rate is K/PA, but we need K/(AB-H) for out distribution
|
|
# Simplified: use K% directly scaled to outs
|
|
raw_so = sanitize_chance_output(
|
|
all_outs * k_pct * 1.2
|
|
) # 1.2x modifier from calcs_pitcher
|
|
|
|
# Cap strikeouts to leave room for other outs
|
|
current_total = (
|
|
homerun
|
|
+ bp_homerun
|
|
+ triple
|
|
+ double_three
|
|
+ double_two
|
|
+ double_cf
|
|
+ single_two
|
|
+ single_one
|
|
+ single_center
|
|
+ bp_single
|
|
+ hbp
|
|
+ walk
|
|
)
|
|
max_so = (
|
|
108 - current_total - TOTAL_XCHECK - 5
|
|
) # Leave at least 5 for flyouts/groundouts
|
|
strikeout = min(raw_so, max_so)
|
|
|
|
# Remaining outs (after K and x-checks)
|
|
rem_outs = 108 - current_total - strikeout - TOTAL_XCHECK
|
|
|
|
# Flyouts vs groundouts based on FB%/GB%
|
|
total_batted = fb_pct + gb_pct
|
|
if total_batted > 0:
|
|
fb_share = fb_pct / total_batted
|
|
gb_share = gb_pct / total_batted
|
|
else:
|
|
fb_share = gb_share = 0.5
|
|
|
|
all_flyouts = sanitize_chance_output(rem_outs * fb_share)
|
|
all_groundouts = rem_outs - all_flyouts
|
|
|
|
# Distribute flyouts by field (pitcher hand affects distribution)
|
|
if pit_hand == "L":
|
|
# Lefty: more fly balls to LF (opposite field for RHB)
|
|
flyout_lf_b = sanitize_chance_output(all_flyouts * oppo_pct)
|
|
flyout_rf_b = sanitize_chance_output(all_flyouts * 0.25)
|
|
else:
|
|
# Righty: more fly balls to RF (opposite field for LHB)
|
|
flyout_rf_b = sanitize_chance_output(all_flyouts * oppo_pct)
|
|
flyout_lf_b = sanitize_chance_output(all_flyouts * 0.25)
|
|
flyout_cf_b = all_flyouts - flyout_lf_b - flyout_rf_b
|
|
|
|
# Distribute groundouts (A = DP potential, B = routine)
|
|
groundout_a = sanitize_chance_output(all_groundouts * soft_pct)
|
|
groundout_b = sanitize_chance_output(all_groundouts - groundout_a)
|
|
|
|
# Build the rating dict
|
|
rating = {
|
|
"pitchingcard_id": 0, # Will be set on submission
|
|
"vs_hand": vs_hand,
|
|
"homerun": mround(homerun),
|
|
"bp_homerun": mround(bp_homerun),
|
|
"triple": mround(triple),
|
|
"double_three": mround(double_three),
|
|
"double_two": mround(double_two),
|
|
"double_cf": mround(double_cf),
|
|
"single_two": mround(single_two),
|
|
"single_one": mround(single_one),
|
|
"single_center": mround(single_center),
|
|
"bp_single": mround(bp_single),
|
|
"hbp": mround(hbp),
|
|
"walk": mround(walk),
|
|
"strikeout": mround(strikeout),
|
|
"flyout_lf_b": mround(flyout_lf_b),
|
|
"flyout_cf_b": mround(flyout_cf_b),
|
|
"flyout_rf_b": mround(flyout_rf_b),
|
|
"groundout_a": mround(groundout_a),
|
|
"groundout_b": mround(groundout_b),
|
|
**DEFAULT_XCHECKS,
|
|
# Calculated stats for display
|
|
"avg": avg,
|
|
"obp": obp,
|
|
"slg": slg,
|
|
}
|
|
|
|
# Verify total and adjust if needed
|
|
total = sum(
|
|
[
|
|
rating["homerun"],
|
|
rating["bp_homerun"],
|
|
rating["triple"],
|
|
rating["double_three"],
|
|
rating["double_two"],
|
|
rating["double_cf"],
|
|
rating["single_two"],
|
|
rating["single_one"],
|
|
rating["single_center"],
|
|
rating["bp_single"],
|
|
rating["hbp"],
|
|
rating["walk"],
|
|
rating["strikeout"],
|
|
rating["flyout_lf_b"],
|
|
rating["flyout_cf_b"],
|
|
rating["flyout_rf_b"],
|
|
rating["groundout_a"],
|
|
rating["groundout_b"],
|
|
rating["xcheck_p"],
|
|
rating["xcheck_c"],
|
|
rating["xcheck_1b"],
|
|
rating["xcheck_2b"],
|
|
rating["xcheck_3b"],
|
|
rating["xcheck_ss"],
|
|
rating["xcheck_lf"],
|
|
rating["xcheck_cf"],
|
|
rating["xcheck_rf"],
|
|
]
|
|
)
|
|
|
|
# Adjust strikeouts to hit exactly 108
|
|
diff = 108 - total
|
|
if abs(diff) > 0.01:
|
|
rating["strikeout"] = mround(rating["strikeout"] + diff)
|
|
|
|
return rating
|
|
|
|
|
|
def preview_pitcher():
|
|
"""Preview Tony Smehrik's ratings without submitting to database."""
|
|
|
|
print("\n" + "=" * 70)
|
|
print("TONY SMEHRIK - CUSTOM PITCHER PREVIEW")
|
|
print("=" * 70)
|
|
|
|
# Target: Combined OPS 0.585, OPS vs L 0.495
|
|
# Combined OPS formula for pitchers: (OPS_vR + OPS_vL + max(OPS_vL, OPS_vR)) / 3
|
|
# If OPS_vL = 0.495 and OPS_vR > OPS_vL:
|
|
# 0.585 = (OPS_vR + 0.495 + OPS_vR) / 3
|
|
# 1.755 = 2*OPS_vR + 0.495
|
|
# OPS_vR = (1.755 - 0.495) / 2 = 0.630
|
|
|
|
pit_hand = "L"
|
|
|
|
# vs LHB (Same-side - Dominant)
|
|
# Target OPS: 0.465 (lowered from 0.495)
|
|
# OBP ~0.240, SLG ~0.225
|
|
print("\nCalculating vs LHB (same-side advantage)...")
|
|
vl = calculate_pitcher_rating(
|
|
vs_hand="L",
|
|
pit_hand=pit_hand,
|
|
avg=0.185, # Lowered from 0.195
|
|
obp=0.240, # Lowered from 0.252
|
|
slg=0.225, # Lowered from 0.243
|
|
bb_pct=0.055, # Slightly lower walks
|
|
k_pct=0.20, # Average K-rate
|
|
hr_per_hit=0.04,
|
|
triple_per_hit=0.02,
|
|
double_per_hit=0.22,
|
|
fb_pct=0.35, # Average flyball
|
|
gb_pct=0.45,
|
|
hard_pct=0.30,
|
|
med_pct=0.50,
|
|
soft_pct=0.20,
|
|
oppo_pct=0.26,
|
|
hr_fb_pct=0.06,
|
|
)
|
|
|
|
# vs RHB (Opposite-side - Weaker)
|
|
# Target OPS: 0.645 (raised from 0.630)
|
|
# OBP ~0.305, SLG ~0.340
|
|
print("Calculating vs RHB (opposite-side)...")
|
|
vr = calculate_pitcher_rating(
|
|
vs_hand="R",
|
|
pit_hand=pit_hand,
|
|
avg=0.245, # Raised from 0.238
|
|
obp=0.305, # Raised from 0.298
|
|
slg=0.340, # Raised from 0.332
|
|
bb_pct=0.075, # Slightly more walks
|
|
k_pct=0.08, # Very low K-rate (~8%)
|
|
hr_per_hit=0.07,
|
|
triple_per_hit=0.02,
|
|
double_per_hit=0.24,
|
|
fb_pct=0.45, # High flyball
|
|
gb_pct=0.35,
|
|
hard_pct=0.34,
|
|
med_pct=0.46,
|
|
soft_pct=0.20,
|
|
oppo_pct=0.26,
|
|
hr_fb_pct=0.09,
|
|
)
|
|
|
|
# ===== MANUAL ADJUSTMENTS =====
|
|
# Set half of ALL on-base chances (hits + BB + HBP) to HR
|
|
# Split 1:1 between HR and BP-HR
|
|
# BP-HR and HBP must be whole numbers
|
|
|
|
# vs LHB: Total OB = Hits + BB + HBP
|
|
vl_total_hits = (
|
|
vl["homerun"]
|
|
+ vl["bp_homerun"]
|
|
+ vl["triple"]
|
|
+ vl["double_three"]
|
|
+ vl["double_two"]
|
|
+ vl["double_cf"]
|
|
+ vl["single_two"]
|
|
+ vl["single_one"]
|
|
+ vl["single_center"]
|
|
+ vl["bp_single"]
|
|
)
|
|
vl_total_ob = vl_total_hits + vl["walk"] + vl["hbp"]
|
|
vl_hr_total = vl_total_ob / 2 # Half of all OB → HR
|
|
# Split 1:1, BP-HR must be whole number
|
|
vl["bp_homerun"] = round(vl_hr_total / 2) # Whole number
|
|
vl["homerun"] = round((vl_hr_total - vl["bp_homerun"]) * 20) / 20
|
|
# Ensure HBP is whole number
|
|
vl["hbp"] = 1.0
|
|
|
|
# vs RHB: Total OB = Hits + BB + HBP
|
|
vr_total_hits = (
|
|
vr["homerun"]
|
|
+ vr["bp_homerun"]
|
|
+ vr["triple"]
|
|
+ vr["double_three"]
|
|
+ vr["double_two"]
|
|
+ vr["double_cf"]
|
|
+ vr["single_two"]
|
|
+ vr["single_one"]
|
|
+ vr["single_center"]
|
|
+ vr["bp_single"]
|
|
)
|
|
vr_total_ob = vr_total_hits + vr["walk"] + vr["hbp"]
|
|
vr_hr_total = vr_total_ob / 2 # Half of all OB → HR
|
|
# Split 1:1, BP-HR must be whole number
|
|
vr["bp_homerun"] = round(vr_hr_total / 2) # Whole number
|
|
vr["homerun"] = round((vr_hr_total - vr["bp_homerun"]) * 20) / 20
|
|
# Ensure HBP is whole number
|
|
vr["hbp"] = 1.0
|
|
|
|
# The HR chances come FROM the existing hit pool, so we need to reduce other hits
|
|
# to keep total hits the same. Reduce singles proportionally.
|
|
vl_new_hr_total = vl["homerun"] + vl["bp_homerun"]
|
|
vl_old_hr_total = 0 # Started with 0 HR
|
|
vl_hr_increase = vl_new_hr_total - vl_old_hr_total
|
|
# Reduce singles to compensate - take from single_one, single_two, bp_single
|
|
vl_singles_to_reduce = vl_hr_increase
|
|
# Take from single_one first, then single_two, then bp_single
|
|
reduce_from_one = min(vl["single_one"], vl_singles_to_reduce)
|
|
vl["single_one"] -= reduce_from_one
|
|
vl_singles_to_reduce -= reduce_from_one
|
|
reduce_from_two = min(vl["single_two"], vl_singles_to_reduce)
|
|
vl["single_two"] -= reduce_from_two
|
|
vl_singles_to_reduce -= reduce_from_two
|
|
reduce_from_bp = min(vl["bp_single"], vl_singles_to_reduce)
|
|
vl["bp_single"] -= reduce_from_bp
|
|
vl_singles_to_reduce -= reduce_from_bp
|
|
# If still need to reduce, take from doubles
|
|
if vl_singles_to_reduce > 0:
|
|
vl["double_cf"] = max(0, vl["double_cf"] - vl_singles_to_reduce)
|
|
|
|
vr_new_hr_total = vr["homerun"] + vr["bp_homerun"]
|
|
vr_old_hr_total = 0.80 + 1.00 # Had some HR already
|
|
vr_hr_increase = vr_new_hr_total - vr_old_hr_total
|
|
# Reduce singles to compensate
|
|
vr_singles_to_reduce = vr_hr_increase
|
|
reduce_from_one = min(vr["single_one"], vr_singles_to_reduce)
|
|
vr["single_one"] -= reduce_from_one
|
|
vr_singles_to_reduce -= reduce_from_one
|
|
reduce_from_two = min(vr["single_two"], vr_singles_to_reduce)
|
|
vr["single_two"] -= reduce_from_two
|
|
vr_singles_to_reduce -= reduce_from_two
|
|
reduce_from_bp = min(vr["bp_single"], vr_singles_to_reduce)
|
|
vr["bp_single"] -= reduce_from_bp
|
|
vr_singles_to_reduce -= reduce_from_bp
|
|
if vr_singles_to_reduce > 0:
|
|
vr["double_cf"] = max(0, vr["double_cf"] - vr_singles_to_reduce)
|
|
|
|
# Force BP-SI to specific values
|
|
vl["bp_single"] = 5.0
|
|
vr["bp_single"] = 3.0
|
|
|
|
# Recalculate totals and adjust strikeouts to hit 108
|
|
for rating in [vl, vr]:
|
|
total = sum(
|
|
[
|
|
rating["homerun"],
|
|
rating["bp_homerun"],
|
|
rating["triple"],
|
|
rating["double_three"],
|
|
rating["double_two"],
|
|
rating["double_cf"],
|
|
rating["single_two"],
|
|
rating["single_one"],
|
|
rating["single_center"],
|
|
rating["bp_single"],
|
|
rating["hbp"],
|
|
rating["walk"],
|
|
rating["strikeout"],
|
|
rating["flyout_lf_b"],
|
|
rating["flyout_cf_b"],
|
|
rating["flyout_rf_b"],
|
|
rating["groundout_a"],
|
|
rating["groundout_b"],
|
|
rating["xcheck_p"],
|
|
rating["xcheck_c"],
|
|
rating["xcheck_1b"],
|
|
rating["xcheck_2b"],
|
|
rating["xcheck_3b"],
|
|
rating["xcheck_ss"],
|
|
rating["xcheck_lf"],
|
|
rating["xcheck_cf"],
|
|
rating["xcheck_rf"],
|
|
]
|
|
)
|
|
diff = 108 - total
|
|
rating["strikeout"] = round((rating["strikeout"] + diff) * 20) / 20
|
|
|
|
print("\nApplied HR adjustments + BP-SI override (vL: 5.0, vR: 3.0):")
|
|
print(
|
|
f" vs LHB: Total OB was {vl_total_ob:.2f} → Half ({vl_hr_total:.2f}) to HR (HR:{vl['homerun']:.2f} + BP-HR:{vl['bp_homerun']:.0f})"
|
|
)
|
|
print(
|
|
f" vs RHB: Total OB was {vr_total_ob:.2f} → Half ({vr_hr_total:.2f}) to HR (HR:{vr['homerun']:.2f} + BP-HR:{vr['bp_homerun']:.0f})"
|
|
)
|
|
|
|
# Calculate combined OPS
|
|
ops_vl = vl["obp"] + vl["slg"]
|
|
ops_vr = vr["obp"] + vr["slg"]
|
|
combined_ops = (ops_vr + ops_vl + max(ops_vl, ops_vr)) / 3
|
|
|
|
# Display results
|
|
print("\n" + "-" * 70)
|
|
print("TONY SMEHRIK (L) - SP/RP")
|
|
print("-" * 70)
|
|
|
|
print("\nTarget OPS: 0.585 combined, 0.495 vs L")
|
|
print(
|
|
f"Actual OPS: {combined_ops:.3f} combined, {ops_vl:.3f} vs L, {ops_vr:.3f} vs R"
|
|
)
|
|
|
|
for rating in [vl, vr]:
|
|
vs_hand = rating["vs_hand"]
|
|
print(f"\n{'='*35}")
|
|
print(f"VS {vs_hand}HB:")
|
|
print(f"{'='*35}")
|
|
|
|
ops = rating["obp"] + rating["slg"]
|
|
print(
|
|
f" AVG: {rating['avg']:.3f} OBP: {rating['obp']:.3f} SLG: {rating['slg']:.3f} OPS: {ops:.3f}"
|
|
)
|
|
|
|
# Show hit distribution
|
|
total_hits = (
|
|
rating["homerun"]
|
|
+ rating["bp_homerun"]
|
|
+ rating["triple"]
|
|
+ rating["double_three"]
|
|
+ rating["double_two"]
|
|
+ rating["double_cf"]
|
|
+ rating["single_two"]
|
|
+ rating["single_one"]
|
|
+ rating["single_center"]
|
|
+ rating["bp_single"]
|
|
)
|
|
doubles = rating["double_cf"] + rating["double_two"] + rating["double_three"]
|
|
singles = (
|
|
total_hits
|
|
- rating["homerun"]
|
|
- rating["bp_homerun"]
|
|
- rating["triple"]
|
|
- doubles
|
|
)
|
|
|
|
print(f"\n HITS ALLOWED: {total_hits:.2f}")
|
|
print(f" HR: {rating['homerun']:.2f} + BP-HR: {rating['bp_homerun']:.2f}")
|
|
print(f" 3B: {rating['triple']:.2f}")
|
|
print(
|
|
f" 2B: {doubles:.2f} (double**: {rating['double_two']:.2f}, double(cf): {rating['double_cf']:.2f})"
|
|
)
|
|
print(
|
|
f" 1B: {singles:.2f} (1B**: {rating['single_two']:.2f}, 1B*: {rating['single_one']:.2f}, 1B(cf): {rating['single_center']:.2f}, BP-1B: {rating['bp_single']:.2f})"
|
|
)
|
|
|
|
# Show walks/strikeouts
|
|
print("\n PLATE DISCIPLINE:")
|
|
print(f" BB: {rating['walk']:.2f} HBP: {rating['hbp']:.2f}")
|
|
print(f" K: {rating['strikeout']:.2f}")
|
|
|
|
# Show batted ball outs
|
|
flyouts = rating["flyout_lf_b"] + rating["flyout_cf_b"] + rating["flyout_rf_b"]
|
|
groundouts = rating["groundout_a"] + rating["groundout_b"]
|
|
xchecks = sum(
|
|
[
|
|
rating[f"xcheck_{pos}"]
|
|
for pos in ["p", "c", "1b", "2b", "3b", "ss", "lf", "cf", "rf"]
|
|
]
|
|
)
|
|
total_outs = rating["strikeout"] + flyouts + groundouts + xchecks
|
|
|
|
print(f"\n OUTS: {total_outs:.2f}")
|
|
print(f" Strikeouts: {rating['strikeout']:.2f}")
|
|
print(
|
|
f" Flyouts: {flyouts:.2f} (LF-B: {rating['flyout_lf_b']:.2f}, CF-B: {rating['flyout_cf_b']:.2f}, RF-B: {rating['flyout_rf_b']:.2f})"
|
|
)
|
|
print(
|
|
f" Groundouts: {groundouts:.2f} (A: {rating['groundout_a']:.2f}, B: {rating['groundout_b']:.2f})"
|
|
)
|
|
print(
|
|
f" X-Checks: {xchecks:.2f} (P:{rating['xcheck_p']:.0f} C:{rating['xcheck_c']:.0f} 1B:{rating['xcheck_1b']:.0f} 2B:{rating['xcheck_2b']:.0f} 3B:{rating['xcheck_3b']:.0f} SS:{rating['xcheck_ss']:.0f} LF:{rating['xcheck_lf']:.0f} CF:{rating['xcheck_cf']:.0f} RF:{rating['xcheck_rf']:.0f})"
|
|
)
|
|
|
|
batted_outs = flyouts + groundouts
|
|
if batted_outs > 0:
|
|
fb_rate = flyouts / batted_outs
|
|
gb_rate = groundouts / batted_outs
|
|
print(f" FB%: {fb_rate*100:.1f}% GB%: {gb_rate*100:.1f}%")
|
|
|
|
# Verify total = 108
|
|
total = sum(
|
|
[
|
|
rating["homerun"],
|
|
rating["bp_homerun"],
|
|
rating["triple"],
|
|
rating["double_three"],
|
|
rating["double_two"],
|
|
rating["double_cf"],
|
|
rating["single_two"],
|
|
rating["single_one"],
|
|
rating["single_center"],
|
|
rating["bp_single"],
|
|
rating["hbp"],
|
|
rating["walk"],
|
|
rating["strikeout"],
|
|
rating["flyout_lf_b"],
|
|
rating["flyout_cf_b"],
|
|
rating["flyout_rf_b"],
|
|
rating["groundout_a"],
|
|
rating["groundout_b"],
|
|
rating["xcheck_p"],
|
|
rating["xcheck_c"],
|
|
rating["xcheck_1b"],
|
|
rating["xcheck_2b"],
|
|
rating["xcheck_3b"],
|
|
rating["xcheck_ss"],
|
|
rating["xcheck_lf"],
|
|
rating["xcheck_cf"],
|
|
rating["xcheck_rf"],
|
|
]
|
|
)
|
|
print(f"\n Total chances: {total:.2f} (should be 108.0)")
|
|
|
|
# Summary
|
|
print("\n" + "=" * 70)
|
|
print("SUMMARY")
|
|
print("=" * 70)
|
|
print("\nName: Tony Smehrik")
|
|
print("Hand: L")
|
|
print("Positions: SP, RP")
|
|
print("Starter Rating: 5 | Relief Rating: 5 | Closer Rating: None")
|
|
print(f"\nCombined OPS Against: {combined_ops:.3f} (target: 0.585)")
|
|
print(f"OPS vs LHB: {ops_vl:.3f} (target: 0.495)")
|
|
print(f"OPS vs RHB: {ops_vr:.3f} (target: ~0.630)")
|
|
print("\nK-rate vs L: ~20% (average)")
|
|
print("K-rate vs R: ~12% (very low)")
|
|
print("\nFB% vs L: ~35% (average)")
|
|
print("FB% vs R: ~45% (high)")
|
|
|
|
print("\n" + "-" * 70)
|
|
print("Ready to submit? Run: python -m custom_cards.submit_tony_smehrik")
|
|
print("-" * 70 + "\n")
|
|
|
|
return vl, vr
|
|
|
|
|
|
if __name__ == "__main__":
|
|
preview_pitcher()
|