"""ざっくり勝敗スコアリング(ピタゴラス期待+ログ5法)""" from __future__ import annotations from dataclasses import dataclass from typing import Iterable PYTH_EXP = 1.83 HOME_FIELD_ADV = 0.02 MIN_GAMES = 10 @dataclass class TeamForm: games: int = 0 runs_scored: int = 0 runs_allowed: int = 0 @property def rs_per_game(self) -> float: if self.games == 0: return 4.5 return self.runs_scored / self.games @property def ra_per_game(self) -> float: if self.games == 0: return 4.5 return self.runs_allowed / self.games def pythagorean_win_pct(runs_scored: float, runs_allowed: float, exp: float = PYTH_EXP) -> float: if runs_scored <= 0 and runs_allowed <= 0: return 0.5 rs = max(runs_scored, 0.01) ra = max(runs_allowed, 0.01) num = rs**exp den = num + ra**exp return num / den if den > 0 else 0.5 def log5(home_pct: float, away_pct: float) -> float: """2チーム間の勝率(ホーム視点・ホーム補正なし)""" if home_pct <= 0 or away_pct <= 0: return 0.5 if home_pct >= 1 or away_pct >= 1: return 0.5 num = home_pct - home_pct * away_pct den = home_pct + away_pct - 2 * home_pct * away_pct if den == 0: return 0.5 return num / den def predict_matchup(home: TeamForm, away: TeamForm) -> dict[str, float]: """試合前のチーム成績からホーム勝率と目安得点を返す。""" home_pct = pythagorean_win_pct(home.rs_per_game, home.ra_per_game) away_pct = pythagorean_win_pct(away.rs_per_game, away.ra_per_game) if home.games < MIN_GAMES or away.games < MIN_GAMES: raw = 0.5 + HOME_FIELD_ADV else: raw = log5(home_pct, away_pct) + HOME_FIELD_ADV home_win = min(0.95, max(0.05, raw)) away_win = 1.0 - home_win # 目安得点: 各チームの得点力と相手失点力の平均(参考値) home_runs_est = (home.rs_per_game + away.ra_per_game) / 2 away_runs_est = (away.rs_per_game + home.ra_per_game) / 2 return { "home_win_prob": round(home_win, 4), "away_win_prob": round(away_win, 4), "home_runs_est": round(home_runs_est, 2), "away_runs_est": round(away_runs_est, 2), } def update_form(form: TeamForm, runs_scored: int, runs_allowed: int) -> None: form.games += 1 form.runs_scored += runs_scored form.runs_allowed += runs_allowed def reset_season_forms(team_ids: Iterable[int]) -> dict[int, TeamForm]: return {tid: TeamForm() for tid in team_ids}