"""Pure helper functions for the /dev refractor-test command. Builds synthetic game data to push a card over its next refractor tier threshold with the minimum number of plays. """ import math # Batter: value = PA + (TB * 2). A HR play: PA=1, TB=4 → value = 1 + 8 = 9 BATTER_VALUE_PER_PLAY = 9 # Pitcher: value = IP + K. A K play: outs=1 (IP=1/3), K=1 → value = 1/3 + 1 = 4/3 PITCHER_VALUE_PER_PLAY = 4 / 3 def calculate_plays_needed(gap: int, card_type: str) -> dict: """Calculate the number of synthetic plays needed to close a refractor gap. Args: gap: Points needed to reach the next tier threshold. A gap of 0 means the card is exactly at threshold — we still need 1 play to push past it. card_type: One of "batter", "sp", "rp". Returns: dict with keys: num_plays: int — number of plays to create total_value: float — total refractor value those plays will add value_per_play: float — value each play contributes """ if card_type == "batter": value_per_play = BATTER_VALUE_PER_PLAY else: value_per_play = PITCHER_VALUE_PER_PLAY num_plays = max(1, math.ceil(gap / value_per_play)) total_value = num_plays * value_per_play return { "num_plays": num_plays, "total_value": total_value, "value_per_play": value_per_play, } def build_game_data(team_id: int, season: int) -> dict: """Build a minimal game record for refractor testing. Creates a self-play game (team vs itself) with game_type='test'. All score and ranking fields are zeroed; short_game=True avoids full simulation overhead when this record is posted to the API. """ return { "season": season, "game_type": "test", "away_team_id": team_id, "home_team_id": team_id, "week": 1, "away_score": 0, "home_score": 0, "away_team_value": 0, "home_team_value": 0, "away_team_ranking": 0, "home_team_ranking": 0, "ranked": False, "short_game": True, "forfeit": False, } def build_batter_plays( game_id: int, batter_id: int, team_id: int, pitcher_id: int, num_plays: int, ) -> list[dict]: """Build a list of synthetic solo-HR batter plays for refractor testing. Each play is a solo home run (PA=1, AB=1, H=1, HR=1, R=1, RBI=1). Structural fields use safe defaults so the batch is accepted by the plays API endpoint without requiring real game context. play_num is sequential starting at 1. Args: game_id: ID of the game these plays belong to. batter_id: Card/player ID of the batter receiving credit. team_id: Team ID used for both batter_team_id and pitcher_team_id (self-play game). pitcher_id: Card/player ID of the opposing pitcher. num_plays: Number of HR plays to generate. Returns: List of play dicts, one per home run. """ plays = [] for i in range(num_plays): plays.append( { "game_id": game_id, "play_num": i + 1, "batter_id": batter_id, "batter_team_id": team_id, "pitcher_id": pitcher_id, "pitcher_team_id": team_id, "pa": 1, "ab": 1, "hit": 1, "homerun": 1, "run": 1, "rbi": 1, "on_base_code": "000", "inning_half": "bot", "inning_num": 1, "batting_order": 1, "starting_outs": 0, "away_score": 0, "home_score": 0, } ) return plays def build_pitcher_plays( game_id: int, pitcher_id: int, team_id: int, batter_id: int, num_plays: int, ) -> list[dict]: """Build a list of synthetic strikeout pitcher plays for refractor testing. Each play is a strikeout (PA=1, AB=1, SO=1, outs=1, hit=0, homerun=0). Structural fields use the same safe defaults as build_batter_plays. play_num is sequential starting at 1. Args: game_id: ID of the game these plays belong to. pitcher_id: Card/player ID of the pitcher receiving credit. team_id: Team ID used for both pitcher_team_id and batter_team_id (self-play game). batter_id: Card/player ID of the opposing batter. num_plays: Number of strikeout plays to generate. Returns: List of play dicts, one per strikeout. """ plays = [] for i in range(num_plays): plays.append( { "game_id": game_id, "play_num": i + 1, "pitcher_id": pitcher_id, "pitcher_team_id": team_id, "batter_id": batter_id, "batter_team_id": team_id, "pa": 1, "ab": 1, "so": 1, "outs": 1, "hit": 0, "homerun": 0, "on_base_code": "000", "inning_half": "bot", "inning_num": 1, "batting_order": 1, "starting_outs": 0, "away_score": 0, "home_score": 0, } ) return plays def build_decision_data( game_id: int, pitcher_id: int, team_id: int, season: int, ) -> dict: """Build a minimal pitcher decision payload for refractor testing. Returns a decisions wrapper dict containing a single no-decision start entry. All win/loss/hold/save flags default to 0; is_start is True so the pitcher accrues IP-based refractor value from the associated plays. Args: game_id: ID of the game the decision belongs to. pitcher_id: Card/player ID of the pitcher. team_id: Team ID for pitcher_team_id. season: Season number for the decision record. Returns: Dict with key "decisions" containing a list with one decision dict. """ return { "decisions": [ { "game_id": game_id, "season": season, "week": 1, "pitcher_id": pitcher_id, "pitcher_team_id": team_id, "win": 0, "loss": 0, "hold": 0, "is_save": 0, "is_start": True, "b_save": 0, } ] }