paper-dynasty-card-creation/custom_cards/luan_arroto_final.py
Cal Corum 0a17745389 Run black and ruff across entire codebase
Standardize formatting with black and apply ruff auto-fixes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 14:24:33 -05:00

245 lines
8.7 KiB
Python

"""
Luan Arroto FINAL - With adjusted groundball ratios
Groundball distribution:
- vs R: 3:3:1 ratio (A:B:C) - More double play balls vs RHP
- vs L: 3:2:1 ratio (A:B:C) - Balanced
"""
from custom_cards.archetype_definitions import BatterArchetype
from custom_cards.archetype_calculator import (
BatterRatingCalculator,
calculate_total_ops,
)
def main():
archetype = BatterArchetype(
name="Luan Arroto Final",
description="Contact hitter with platoon advantage and spray approach",
avg_vs_r=0.310,
obp_vs_r=0.450,
slg_vs_r=0.385,
bb_pct_vs_r=0.14,
k_pct_vs_r=0.10,
avg_vs_l=0.285,
obp_vs_l=0.455,
slg_vs_l=0.355,
bb_pct_vs_l=0.17,
k_pct_vs_l=0.09,
hr_per_hit=0.025,
triple_per_hit=0.00,
double_per_hit=0.22,
gb_pct=0.48,
fb_pct=0.27,
ld_pct=0.25,
hard_pct=0.28,
med_pct=0.52,
soft_pct=0.20,
pull_pct=0.215,
center_pct=0.35,
oppo_pct=0.435,
ifh_pct=0.09,
hr_fb_pct=0.06,
speed_rating=8,
steal_jump=3,
xbt_pct=0.58,
hit_run_skill=9,
primary_positions=["1B", "RF"],
defensive_rating=6,
)
calc = BatterRatingCalculator(archetype)
ratings = calc.calculate_ratings(battingcard_id=0)
baserunning = calc.calculate_baserunning()
# Adjust pull rates
ratings[1]["pull_rate"] = 0.33 # vs R
ratings[0]["pull_rate"] = 0.10 # vs L
for rating in ratings:
rating["slap_rate"] = 1.0 - rating["pull_rate"] - rating["center_rate"]
# Adjust baserunning
baserunning.update(
{
"steal_low": 3,
"steal_high": 20,
"steal_auto": 0,
"steal_jump": 0.0277777,
"running": 10,
}
)
# CUSTOM GROUNDBALL RATIOS
vl = ratings[0]
vr = ratings[1]
# Calculate current totals
total_gb_vl = vl["groundout_a"] + vl["groundout_b"] + vl["groundout_c"]
total_gb_vr = vr["groundout_a"] + vr["groundout_b"] + vr["groundout_c"]
print("\nOriginal groundball totals:")
print(
f" vs L: {total_gb_vl:.2f} (A:{vl['groundout_a']:.2f} B:{vl['groundout_b']:.2f} C:{vl['groundout_c']:.2f})"
)
print(
f" vs R: {total_gb_vr:.2f} (A:{vr['groundout_a']:.2f} B:{vr['groundout_b']:.2f} C:{vr['groundout_c']:.2f})"
)
# vs L: 3:2:1 ratio (6 parts)
vl["groundout_a"] = (3 / 6) * total_gb_vl # 9.85
vl["groundout_b"] = (2 / 6) * total_gb_vl # 6.57
vl["groundout_c"] = (1 / 6) * total_gb_vl # 3.28
# vs R: 3:3:1 ratio (7 parts)
vr["groundout_a"] = (3 / 7) * total_gb_vr # 8.36
vr["groundout_b"] = (3 / 7) * total_gb_vr # 8.36
vr["groundout_c"] = (1 / 7) * total_gb_vr # 2.79
print("\nAdjusted groundball ratios:")
print(
f" vs L (3:2:1): {total_gb_vl:.2f} (A:{vl['groundout_a']:.2f} B:{vl['groundout_b']:.2f} C:{vl['groundout_c']:.2f})"
)
print(
f" vs R (3:3:1): {total_gb_vr:.2f} (A:{vr['groundout_a']:.2f} B:{vr['groundout_b']:.2f} C:{vr['groundout_c']:.2f})"
)
total_ops = calculate_total_ops(vl, vr, is_pitcher=False)
# Display complete comparison
print("\n" + "=" * 85)
print(" " * 25 + "LUAN ARROTO - FINAL VERSION")
print("=" * 85)
# SLASH LINES
print(f"\n{'SLASH LINE':<20} {'vs LHP':>20} {'vs RHP':>20} {'Difference':>20}")
print("-" * 85)
print(
f"{'AVG':<20} {vl['avg']:>20.3f} {vr['avg']:>20.3f} {vr['avg']-vl['avg']:>20.3f}"
)
print(
f"{'OBP':<20} {vl['obp']:>20.3f} {vr['obp']:>20.3f} {vr['obp']-vl['obp']:>20.3f}"
)
print(
f"{'SLG':<20} {vl['slg']:>20.3f} {vr['slg']:>20.3f} {vr['slg']-vl['slg']:>20.3f}"
)
print(
f"{'OPS':<20} {vl['obp']+vl['slg']:>20.3f} {vr['obp']+vr['slg']:>20.3f} {(vr['obp']+vr['slg'])-(vl['obp']+vl['slg']):>20.3f}"
)
print(
f"{'ISO':<20} {vl['slg']-vl['avg']:>20.3f} {vr['slg']-vr['avg']:>20.3f} {(vr['slg']-vr['avg'])-(vl['slg']-vl['avg']):>20.3f}"
)
print(f"\n{'COMBINED OPS':<20} {total_ops:>20.3f}")
# HITS
print(
f"\n{'HITS (out of 108)':<20} {'vs LHP':>20} {'vs RHP':>20} {'Difference':>20}"
)
print("-" * 85)
print(
f"{'Homerun':<20} {vl['homerun']:>20.2f} {vr['homerun']:>20.2f} {vr['homerun']-vl['homerun']:>20.2f}"
)
print(
f"{'BP-Homerun':<20} {vl['bp_homerun']:>20.2f} {vr['bp_homerun']:>20.2f} {vr['bp_homerun']-vl['bp_homerun']:>20.2f}"
)
print(
f"{'Triple':<20} {vl['triple']:>20.2f} {vr['triple']:>20.2f} {vr['triple']-vl['triple']:>20.2f}"
)
print(
f"{'Double (2-zone)':<20} {vl['double_two']:>20.2f} {vr['double_two']:>20.2f} {vr['double_two']-vl['double_two']:>20.2f}"
)
print(
f"{'Double (Pull)':<20} {vl['double_pull']:>20.2f} {vr['double_pull']:>20.2f} {vr['double_pull']-vl['double_pull']:>20.2f}"
)
print(
f"{'Single (2-zone)':<20} {vl['single_two']:>20.2f} {vr['single_two']:>20.2f} {vr['single_two']-vl['single_two']:>20.2f}"
)
print(
f"{'Single (1-zone)':<20} {vl['single_one']:>20.2f} {vr['single_one']:>20.2f} {vr['single_one']-vl['single_one']:>20.2f}"
)
print(
f"{'Single (Center)':<20} {vl['single_center']:>20.2f} {vr['single_center']:>20.2f} {vr['single_center']-vl['single_center']:>20.2f}"
)
print(
f"{'BP-Single':<20} {vl['bp_single']:>20.2f} {vr['bp_single']:>20.2f} {vr['bp_single']-vl['bp_single']:>20.2f}"
)
# ON-BASE
print(f"\n{'ON-BASE EVENTS':<20} {'vs LHP':>20} {'vs RHP':>20} {'Difference':>20}")
print("-" * 85)
print(
f"{'HBP':<20} {vl['hbp']:>20.2f} {vr['hbp']:>20.2f} {vr['hbp']-vl['hbp']:>20.2f}"
)
print(
f"{'Walk':<20} {vl['walk']:>20.2f} {vr['walk']:>20.2f} {vr['walk']-vl['walk']:>20.2f}"
)
# OUTS
print(f"\n{'OUTS':<20} {'vs LHP':>20} {'vs RHP':>20} {'Difference':>20}")
print("-" * 85)
print(
f"{'Strikeout':<20} {vl['strikeout']:>20.2f} {vr['strikeout']:>20.2f} {vr['strikeout']-vl['strikeout']:>20.2f}"
)
print(
f"{'Lineout':<20} {vl['lineout']:>20.2f} {vr['lineout']:>20.2f} {vr['lineout']-vl['lineout']:>20.2f}"
)
print(
f"{'Flyout (total)':<20} {vl['flyout_bq']+vl['flyout_lf_b']+vl['flyout_rf_b']:>20.2f} {vr['flyout_bq']+vr['flyout_lf_b']+vr['flyout_rf_b']:>20.2f} {(vr['flyout_bq']+vr['flyout_lf_b']+vr['flyout_rf_b'])-(vl['flyout_bq']+vl['flyout_lf_b']+vl['flyout_rf_b']):>20.2f}"
)
print(
f"\n{'GROUNDOUTS (CUSTOM)':<20} {'vs LHP':>20} {'vs RHP':>20} {'Difference':>20}"
)
print("-" * 85)
print(
f"{' A (DP balls)':<20} {vl['groundout_a']:>20.2f} {vr['groundout_a']:>20.2f} {vr['groundout_a']-vl['groundout_a']:>20.2f}"
)
print(
f"{' B':<20} {vl['groundout_b']:>20.2f} {vr['groundout_b']:>20.2f} {vr['groundout_b']-vl['groundout_b']:>20.2f}"
)
print(
f"{' C':<20} {vl['groundout_c']:>20.2f} {vr['groundout_c']:>20.2f} {vr['groundout_c']-vl['groundout_c']:>20.2f}"
)
print(
f"{' Total':<20} {vl['groundout_a']+vl['groundout_b']+vl['groundout_c']:>20.2f} {vr['groundout_a']+vr['groundout_b']+vr['groundout_c']:>20.2f} {(vr['groundout_a']+vr['groundout_b']+vr['groundout_c'])-(vl['groundout_a']+vl['groundout_b']+vl['groundout_c']):>20.2f}"
)
print(f"{' Ratio (A:B:C)':<20} {'3:2:1':>20} {'3:3:1':>20} {'-':>20}")
# SPRAY
print(f"\n{'SPRAY CHART':<20} {'vs LHP':>20} {'vs RHP':>20} {'Difference':>20}")
print("-" * 85)
print(
f"{'Pull %':<20} {vl['pull_rate']*100:>19.1f}% {vr['pull_rate']*100:>19.1f}% {(vr['pull_rate']-vl['pull_rate'])*100:>19.1f}%"
)
print(
f"{'Center %':<20} {vl['center_rate']*100:>19.1f}% {vr['center_rate']*100:>19.1f}% {(vr['center_rate']-vl['center_rate'])*100:>19.1f}%"
)
print(
f"{'Opposite %':<20} {vl['slap_rate']*100:>19.1f}% {vr['slap_rate']*100:>19.1f}% {(vr['slap_rate']-vl['slap_rate'])*100:>19.1f}%"
)
# BASERUNNING
print(f"\n{'BASERUNNING':<20} {'Value':>20}")
print("-" * 85)
print(f"{'Running':<20} {baserunning['running']:>20}")
print(
f"{'Steal Range':<20} {f'{baserunning['steal_low']}-{baserunning['steal_high']}':>20}"
)
print(f"{'Steal Jump':<20} {baserunning['steal_jump']:>20.7f}")
print(f"{'Hit-and-Run':<20} {baserunning['hit_and_run']:>20}")
# DEFENSE
print(f"\n{'DEFENSE':<20} {'Range':>20} {'Error':>20} {'Arm':>20}")
print("-" * 85)
print(f"{'1B':<20} {4:>20} {10:>20} {'-':>20}")
print(f"{'RF':<20} {4:>20} {6:>20} {'+2':>20}")
print("\n" + "=" * 85)
print("✓ FINAL VERSION - All requirements met with custom groundball ratios")
print("=" * 85)
print("")
if __name__ == "__main__":
main()