""" Common X-Check resolution tables shared across SBA and PD leagues. Tables include: - Defense range tables (20x5) for each position type - Error charts mapping 3d6 rolls to error types - Holding runner responsibility chart Author: Claude Date: 2025-11-01 """ from typing import List # ============================================================================ # DEFENSE RANGE TABLES (1d20 × Defense Range 1-5) # ============================================================================ # Row index = d20 roll - 1 (0-indexed) # Column index = defense range - 1 (0-indexed) # Values = base result code (G1, SI2, F2, etc.) INFIELD_DEFENSE_TABLE: List[List[str]] = [ # Range: 1 2 3 4 5 # Best Good Avg Poor Worst ['G3#', 'SI1', 'SI2', 'SI2', 'SI2'], # d20 = 1 ['G2#', 'SI1', 'SI2', 'SI2', 'SI2'], # d20 = 2 ['G2#', 'G3#', 'SI1', 'SI2', 'SI2'], # d20 = 3 ['G2#', 'G3#', 'SI1', 'SI2', 'SI2'], # d20 = 4 ['G1', 'G3#', 'G3#', 'SI1', 'SI2'], # d20 = 5 ['G1', 'G2#', 'G3#', 'SI1', 'SI2'], # d20 = 6 ['G1', 'G2', 'G3#', 'G3#', 'SI1'], # d20 = 7 ['G1', 'G2', 'G3#', 'G3#', 'SI1'], # d20 = 8 ['G1', 'G2', 'G3', 'G3#', 'G3#'], # d20 = 9 ['G1', 'G1', 'G2', 'G3#', 'G3#'], # d20 = 10 ['G1', 'G1', 'G2', 'G3', 'G3#'], # d20 = 11 ['G1', 'G1', 'G2', 'G3', 'G3#'], # d20 = 12 ['G1', 'G1', 'G2', 'G3', 'G3'], # d20 = 13 ['G1', 'G1', 'G2', 'G2', 'G3'], # d20 = 14 ['G1', 'G1', 'G1', 'G2', 'G3'], # d20 = 15 ['G1', 'G1', 'G1', 'G2', 'G3'], # d20 = 16 ['G1', 'G1', 'G1', 'G1', 'G3'], # d20 = 17 ['G1', 'G1', 'G1', 'G1', 'G2'], # d20 = 18 ['G1', 'G1', 'G1', 'G1', 'G2'], # d20 = 19 ['G1', 'G1', 'G1', 'G1', 'G1'], # d20 = 20 ] OUTFIELD_DEFENSE_TABLE: List[List[str]] = [ # Range: 1 2 3 4 5 # Best Good Avg Poor Worst ['TR3', 'DO3', 'DO3', 'DO3', 'DO3'], # d20 = 1 ['DO3', 'DO3', 'DO3', 'DO3', 'DO3'], # d20 = 2 ['DO2', 'DO3', 'DO3', 'DO3', 'DO3'], # d20 = 3 ['DO2', 'DO2', 'DO3', 'DO3', 'DO3'], # d20 = 4 ['SI2', 'DO2', 'DO2', 'DO3', 'DO3'], # d20 = 5 ['SI2', 'SI2', 'DO2', 'DO2', 'DO3'], # d20 = 6 ['F1', 'SI2', 'SI2', 'DO2', 'DO2'], # d20 = 7 ['F1', 'F1', 'SI2', 'SI2', 'DO2'], # d20 = 8 ['F1', 'F1', 'F1', 'SI2', 'SI2'], # d20 = 9 ['F1', 'F1', 'F1', 'SI2', 'SI2'], # d20 = 10 ['F1', 'F1', 'F1', 'F1', 'SI2'], # d20 = 11 ['F1', 'F1', 'F1', 'F1', 'SI2'], # d20 = 12 ['F1', 'F1', 'F1', 'F1', 'F1'], # d20 = 13 ['F2', 'F1', 'F1', 'F1', 'F1'], # d20 = 14 ['F2', 'F2', 'F1', 'F1', 'F1'], # d20 = 15 ['F2', 'F2', 'F2', 'F1', 'F1'], # d20 = 16 ['F2', 'F2', 'F2', 'F2', 'F1'], # d20 = 17 ['F3', 'F2', 'F2', 'F2', 'F2'], # d20 = 18 ['F3', 'F3', 'F2', 'F2', 'F2'], # d20 = 19 ['F3', 'F3', 'F3', 'F2', 'F2'], # d20 = 20 ] CATCHER_DEFENSE_TABLE: List[List[str]] = [ # Range: 1 2 3 4 5 # Best Good Avg Poor Worst ['G3', 'SI1', 'SI1', 'SI1', 'SI1'], # d20 = 1 ['G2', 'G3', 'SI1', 'SI1', 'SI1'], # d20 = 2 ['G2', 'G3', 'SI1', 'SI1', 'SI1'], # d20 = 3 ['G1', 'G2', 'G3', 'SI1', 'SI1'], # d20 = 4 ['G1', 'G2', 'G3', 'SI1', 'SI1'], # d20 = 5 ['G1', 'G1', 'G2', 'G3', 'SI1'], # d20 = 6 ['G1', 'G1', 'G2', 'G3', 'SI1'], # d20 = 7 ['G1', 'G1', 'G1', 'G2', 'G3'], # d20 = 8 ['G1', 'G1', 'G1', 'G2', 'G3'], # d20 = 9 ['SPD', 'G1', 'G1', 'G1', 'G2'], # d20 = 10 ['SPD', 'SPD', 'G1', 'G1', 'G1'], # d20 = 11 ['SPD', 'SPD', 'SPD', 'G1', 'G1'], # d20 = 12 ['FO', 'SPD', 'SPD', 'SPD', 'G1'], # d20 = 13 ['FO', 'FO', 'SPD', 'SPD', 'SPD'], # d20 = 14 ['FO', 'FO', 'FO', 'SPD', 'SPD'], # d20 = 15 ['PO', 'FO', 'FO', 'FO', 'SPD'], # d20 = 16 ['PO', 'PO', 'FO', 'FO', 'FO'], # d20 = 17 ['PO', 'PO', 'PO', 'FO', 'FO'], # d20 = 18 ['PO', 'PO', 'PO', 'PO', 'FO'], # d20 = 19 ['PO', 'PO', 'PO', 'PO', 'PO'], # d20 = 20 ] # ============================================================================ # ERROR CHARTS (3d6 totals by Error Rating and Position Type) # ============================================================================ # Structure: {error_rating: {'RP': [rolls], 'E1': [rolls], 'E2': [rolls], 'E3': [rolls]}} # If 3d6 sum is in the list for that error rating, apply that error type # Otherwise, error_result = 'NO' (no error) # Corner Outfield (LF, RF) Error Chart LF_RF_ERROR_CHART: dict[int, dict[str, List[int]]] = { 0: {'RP': [5], 'E1': [], 'E2': [], 'E3': []}, 1: {'RP': [5], 'E1': [3], 'E2': [], 'E3': [17]}, 2: {'RP': [5], 'E1': [3, 18], 'E2': [], 'E3': [16]}, 3: {'RP': [5], 'E1': [3, 18], 'E2': [], 'E3': [15]}, 4: {'RP': [5], 'E1': [4], 'E2': [3, 15], 'E3': [18]}, 5: {'RP': [5], 'E1': [4], 'E2': [14], 'E3': [18]}, 6: {'RP': [5], 'E1': [3, 4], 'E2': [14, 17], 'E3': [18]}, 7: {'RP': [5], 'E1': [16], 'E2': [6, 15], 'E3': [18]}, 8: {'RP': [5], 'E1': [16], 'E2': [6, 15, 17], 'E3': [3, 18]}, 9: {'RP': [5], 'E1': [16], 'E2': [11], 'E3': [3, 18]}, 10: {'RP': [5], 'E1': [4, 16], 'E2': [14, 15, 17], 'E3': [3, 18]}, 11: {'RP': [5], 'E1': [4, 16], 'E2': [13, 15], 'E3': [3, 18]}, 12: {'RP': [5], 'E1': [4, 16], 'E2': [13, 14], 'E3': [3, 18]}, 13: {'RP': [5], 'E1': [6], 'E2': [4, 12, 15], 'E3': [17]}, 14: {'RP': [5], 'E1': [3, 6], 'E2': [12, 14], 'E3': [17]}, 15: {'RP': [5], 'E1': [3, 6, 18], 'E2': [4, 12, 14], 'E3': [17]}, 16: {'RP': [5], 'E1': [4, 6], 'E2': [12, 13], 'E3': [17]}, 17: {'RP': [5], 'E1': [4, 6], 'E2': [9, 12], 'E3': [17]}, 18: {'RP': [5], 'E1': [3, 4, 6], 'E2': [11, 12, 18], 'E3': [17]}, 19: {'RP': [5], 'E1': [7], 'E2': [3, 10, 11], 'E3': [17, 18]}, 20: {'RP': [5], 'E1': [3, 7], 'E2': [11, 13, 16], 'E3': [17, 18]}, 21: {'RP': [5], 'E1': [3, 7], 'E2': [11, 12, 15], 'E3': [17, 18]}, 22: {'RP': [5], 'E1': [3, 6, 16], 'E2': [9, 12, 14], 'E3': [17, 18]}, 23: {'RP': [5], 'E1': [4, 7], 'E2': [11, 12, 14], 'E3': [17, 18]}, 24: {'RP': [5], 'E1': [4, 6, 16], 'E2': [8, 11, 13], 'E3': [3, 17, 18]}, 25: {'RP': [5], 'E1': [4, 6, 16], 'E2': [11, 12, 13], 'E3': [3, 17, 18]}, } # Center Field Error Chart CF_ERROR_CHART: dict[int, dict[str, List[int]]] = { 0: {'RP': [5], 'E1': [], 'E2': [], 'E3': []}, 1: {'RP': [5], 'E1': [3], 'E2': [], 'E3': [17]}, 2: {'RP': [5], 'E1': [3, 18], 'E2': [], 'E3': [16]}, 3: {'RP': [5], 'E1': [3, 18], 'E2': [], 'E3': [15]}, 4: {'RP': [5], 'E1': [4], 'E2': [3, 15], 'E3': [18]}, 5: {'RP': [5], 'E1': [4], 'E2': [14], 'E3': [18]}, 6: {'RP': [5], 'E1': [3, 4], 'E2': [14, 17], 'E3': [18]}, 7: {'RP': [5], 'E1': [16], 'E2': [6, 15], 'E3': [18]}, 8: {'RP': [5], 'E1': [16], 'E2': [6, 15, 17], 'E3': [3, 18]}, 9: {'RP': [5], 'E1': [16], 'E2': [11], 'E3': [3, 18]}, 10: {'RP': [5], 'E1': [4, 16], 'E2': [14, 15, 17], 'E3': [3, 18]}, 11: {'RP': [5], 'E1': [4, 16], 'E2': [13, 15], 'E3': [3, 18]}, 12: {'RP': [5], 'E1': [4, 16], 'E2': [13, 14], 'E3': [3, 18]}, 13: {'RP': [5], 'E1': [6], 'E2': [4, 12, 15], 'E3': [17]}, 14: {'RP': [5], 'E1': [3, 6], 'E2': [12, 14], 'E3': [17]}, 15: {'RP': [5], 'E1': [3, 6, 18], 'E2': [4, 12, 14], 'E3': [17]}, 16: {'RP': [5], 'E1': [4, 6], 'E2': [12, 13], 'E3': [17]}, 17: {'RP': [5], 'E1': [4, 6], 'E2': [9, 12], 'E3': [17]}, 18: {'RP': [5], 'E1': [3, 4, 6], 'E2': [11, 12, 18], 'E3': [17]}, 19: {'RP': [5], 'E1': [7], 'E2': [3, 10, 11], 'E3': [17, 18]}, 20: {'RP': [5], 'E1': [3, 7], 'E2': [11, 13, 16], 'E3': [17, 18]}, 21: {'RP': [5], 'E1': [3, 7], 'E2': [11, 12, 15], 'E3': [17, 18]}, 22: {'RP': [5], 'E1': [3, 6, 16], 'E2': [9, 12, 14], 'E3': [17, 18]}, 23: {'RP': [5], 'E1': [4, 7], 'E2': [11, 12, 14], 'E3': [17, 18]}, 24: {'RP': [5], 'E1': [4, 6, 16], 'E2': [8, 11, 13], 'E3': [3, 17, 18]}, 25: {'RP': [5], 'E1': [4, 6, 16], 'E2': [11, 12, 13], 'E3': [3, 17, 18]}, } # Infield Error Charts # Note: Infield charts do not use E3 (unlike outfield charts) # Structure: same as OF but E3 is always empty # Catcher Error Chart CATCHER_ERROR_CHART: dict[int, dict[str, List[int]]] = { 0: {'RP': [5], 'E1': [], 'E2': [], 'E3': []}, 1: {'RP': [5], 'E1': [17], 'E2': [], 'E3': []}, 2: {'RP': [5], 'E1': [3, 17, 18], 'E2': [], 'E3': []}, 3: {'RP': [5], 'E1': [3, 16, 10], 'E2': [], 'E3': []}, 4: {'RP': [5], 'E1': [16, 17], 'E2': [18], 'E3': []}, 5: {'RP': [5], 'E1': [4, 16, 17], 'E2': [18], 'E3': []}, 6: {'RP': [5], 'E1': [14], 'E2': [18], 'E3': []}, 7: {'RP': [5], 'E1': [3, 15, 16], 'E2': [18], 'E3': []}, 8: {'RP': [5], 'E1': [6, 15], 'E2': [18], 'E3': []}, 9: {'RP': [5], 'E1': [3, 13], 'E2': [18], 'E3': []}, 10: {'RP': [5], 'E1': [12], 'E2': [18], 'E3': []}, 11: {'RP': [5], 'E1': [3, 11], 'E2': [18], 'E3': []}, 12: {'RP': [5], 'E1': [6, 15, 16, 17], 'E2': [3, 18], 'E3': []}, 13: {'RP': [5], 'E1': [4, 6, 15, 16, 17], 'E2': [3, 18], 'E3': []}, 14: {'RP': [5], 'E1': [12, 16, 17], 'E2': [3, 18], 'E3': []}, 15: {'RP': [5], 'E1': [11, 15], 'E2': [3, 18], 'E3': []}, 16: {'RP': [5], 'E1': [7, 14, 16, 17], 'E2': [3, 18], 'E3': []}, } # First Base Error Chart FIRST_BASE_ERROR_CHART: dict[int, dict[str, List[int]]] = { 0: {'RP': [5], 'E1': [], 'E2': [], 'E3': []}, 1: {'RP': [5], 'E1': [17, 18], 'E2': [], 'E3': []}, 2: {'RP': [5], 'E1': [3, 16, 18], 'E2': [], 'E3': []}, 3: {'RP': [5], 'E1': [3, 15], 'E2': [18], 'E3': []}, 4: {'RP': [5], 'E1': [14], 'E2': [18], 'E3': []}, 5: {'RP': [5], 'E1': [14, 17], 'E2': [18], 'E3': []}, 6: {'RP': [5], 'E1': [3, 13], 'E2': [18], 'E3': []}, 7: {'RP': [5], 'E1': [3, 9], 'E2': [18], 'E3': []}, 8: {'RP': [5], 'E1': [6, 15, 16, 17], 'E2': [3, 18], 'E3': []}, 9: {'RP': [5], 'E1': [7, 14, 17], 'E2': [3, 18], 'E3': []}, 10: {'RP': [5], 'E1': [11, 15], 'E2': [3, 18], 'E3': []}, 11: {'RP': [5], 'E1': [6, 8, 15], 'E2': [3, 18], 'E3': []}, 12: {'RP': [5], 'E1': [6, 9, 15], 'E2': [3, 18], 'E3': []}, 13: {'RP': [5], 'E1': [11, 13], 'E2': [17], 'E3': []}, 14: {'RP': [5], 'E1': [3, 9, 12], 'E2': [17], 'E3': []}, 15: {'RP': [5], 'E1': [7, 12, 14], 'E2': [17], 'E3': []}, 16: {'RP': [5], 'E1': [3, 11, 12, 16], 'E2': [17], 'E3': []}, 17: {'RP': [5], 'E1': [3, 6, 11, 12], 'E2': [17], 'E3': []}, 18: {'RP': [5], 'E1': [11, 12, 14], 'E2': [17], 'E3': []}, 19: {'RP': [5], 'E1': [10, 11, 15, 16], 'E2': [17, 18], 'E3': []}, 20: {'RP': [5], 'E1': [6, 10, 11, 15], 'E2': [17, 18], 'E3': []}, 21: {'RP': [5], 'E1': [3, 9, 10, 12], 'E2': [17, 18], 'E3': []}, 22: {'RP': [5], 'E1': [7, 11, 12, 14], 'E2': [17, 18], 'E3': []}, 23: {'RP': [5], 'E1': [10, 11, 12, 16], 'E2': [17, 18], 'E3': []}, 24: {'RP': [5], 'E1': [11, 12, 13, 14], 'E2': [3, 17, 18], 'E3': []}, 25: {'RP': [5], 'E1': [9, 11, 12, 14], 'E2': [3, 17, 18], 'E3': []}, 26: {'RP': [5], 'E1': [9, 12, 13, 14, 15], 'E2': [3, 17, 18], 'E3': []}, 27: {'RP': [5], 'E1': [7, 8, 11, 13, 14], 'E2': [3, 17, 18], 'E3': []}, 28: {'RP': [5], 'E1': [7, 11, 12, 13, 14], 'E2': [3, 17, 18], 'E3': []}, 29: {'RP': [5], 'E1': [9, 10, 11, 12, 17], 'E2': [16], 'E3': []}, 30: {'RP': [5], 'E1': [10, 11, 12, 13, 15, 18], 'E2': [16], 'E3': []}, } # Second Base Error Chart SECOND_BASE_ERROR_CHART: dict[int, dict[str, List[int]]] = { 0: {'RP': [5], 'E1': [], 'E2': [], 'E3': []}, 1: {'RP': [5], 'E1': [18], 'E2': [], 'E3': []}, 2: {'RP': [5], 'E1': [3, 18], 'E2': [], 'E3': []}, 3: {'RP': [5], 'E1': [3, 17], 'E2': [], 'E3': []}, 4: {'RP': [5], 'E1': [3, 17], 'E2': [18], 'E3': []}, 5: {'RP': [5], 'E1': [16], 'E2': [18], 'E3': []}, 6: {'RP': [5], 'E1': [3, 16], 'E2': [18], 'E3': []}, 8: {'RP': [5], 'E1': [16, 17], 'E2': [18], 'E3': []}, 10: {'RP': [5], 'E1': [4, 16, 17], 'E2': [18], 'E3': []}, 11: {'RP': [5], 'E1': [15, 17], 'E2': [18], 'E3': []}, 12: {'RP': [5], 'E1': [15, 17], 'E2': [3, 18], 'E3': []}, 13: {'RP': [5], 'E1': [14], 'E2': [3, 18], 'E3': []}, 14: {'RP': [5], 'E1': [15, 16], 'E2': [3, 18], 'E3': []}, 15: {'RP': [5], 'E1': [14, 17], 'E2': [3, 18], 'E3': []}, 16: {'RP': [5], 'E1': [15, 16, 17], 'E2': [3, 18], 'E3': []}, 17: {'RP': [5], 'E1': [6, 15], 'E2': [3, 18], 'E3': []}, 18: {'RP': [5], 'E1': [13], 'E2': [3, 18], 'E3': []}, 19: {'RP': [5], 'E1': [6, 15, 17], 'E2': [3, 18], 'E3': []}, 20: {'RP': [5], 'E1': [3, 13, 18], 'E2': [17], 'E3': []}, 21: {'RP': [5], 'E1': [4, 13], 'E2': [17], 'E3': []}, 22: {'RP': [5], 'E1': [12, 18], 'E2': [17], 'E3': []}, 23: {'RP': [5], 'E1': [11], 'E2': [17], 'E3': []}, 24: {'RP': [5], 'E1': [11, 18], 'E2': [17], 'E3': []}, 25: {'RP': [5], 'E1': [3, 11, 18], 'E2': [17], 'E3': []}, 26: {'RP': [5], 'E1': [13, 15], 'E2': [17], 'E3': []}, 27: {'RP': [5], 'E1': [13, 15, 18], 'E2': [17], 'E3': []}, 28: {'RP': [5], 'E1': [3, 13, 15], 'E2': [17, 18], 'E3': []}, 29: {'RP': [5], 'E1': [3, 11, 16], 'E2': [17, 18], 'E3': []}, 30: {'RP': [5], 'E1': [12, 15], 'E2': [17, 18], 'E3': []}, 32: {'RP': [5], 'E1': [11, 15], 'E2': [17, 18], 'E3': []}, 34: {'RP': [5], 'E1': [12, 14], 'E2': [17, 18], 'E3': []}, 37: {'RP': [5], 'E1': [11, 15, 18], 'E2': [3, 17, 18], 'E3': []}, 39: {'RP': [5], 'E1': [12, 13], 'E2': [3, 17, 18], 'E3': []}, 41: {'RP': [5], 'E1': [11, 13], 'E2': [3, 17, 18], 'E3': []}, 44: {'RP': [5], 'E1': [9, 12, 18], 'E2': [16], 'E3': []}, 47: {'RP': [5], 'E1': [7, 12, 14], 'E2': [16], 'E3': []}, 50: {'RP': [5], 'E1': [11, 13, 15, 18], 'E2': [16], 'E3': []}, 53: {'RP': [5], 'E1': [11, 12, 15], 'E2': [16, 18], 'E3': []}, 56: {'RP': [5], 'E1': [6, 12, 13, 15], 'E2': [16, 18], 'E3': []}, 59: {'RP': [5], 'E1': [6, 11, 13, 15], 'E2': [3, 16, 18], 'E3': []}, 62: {'RP': [5], 'E1': [6, 11, 12, 15], 'E2': [3, 16, 18], 'E3': []}, 65: {'RP': [5], 'E1': [7, 12, 13, 14], 'E2': [3, 16, 18], 'E3': []}, 68: {'RP': [5], 'E1': [10, 11, 12], 'E2': [16, 17], 'E3': []}, 71: {'RP': [5], 'E1': [11, 12, 13, 15], 'E2': [16, 17], 'E3': []}, } # Third Base Error Chart THIRD_BASE_ERROR_CHART: dict[int, dict[str, List[int]]] = { 0: {'RP': [5], 'E1': [], 'E2': [], 'E3': []}, 1: {'RP': [5], 'E1': [3], 'E2': [18], 'E3': []}, 2: {'RP': [5], 'E1': [3, 4], 'E2': [18], 'E3': []}, 3: {'RP': [5], 'E1': [3, 4], 'E2': [17], 'E3': []}, 4: {'RP': [5], 'E1': [3, 16], 'E2': [17], 'E3': []}, 5: {'RP': [5], 'E1': [15], 'E2': [17], 'E3': []}, 6: {'RP': [5], 'E1': [4, 15], 'E2': [17], 'E3': []}, 8: {'RP': [5], 'E1': [3, 15, 16], 'E2': [17, 18], 'E3': []}, 10: {'RP': [5], 'E1': [13], 'E2': [3, 17, 18], 'E3': []}, 11: {'RP': [5], 'E1': [6, 15, 17], 'E2': [16], 'E3': []}, 12: {'RP': [5], 'E1': [12], 'E2': [16], 'E3': []}, 13: {'RP': [5], 'E1': [11], 'E2': [16, 18], 'E3': []}, 14: {'RP': [5], 'E1': [3, 4, 14, 15], 'E2': [16, 18], 'E3': []}, 15: {'RP': [5], 'E1': [13, 15], 'E2': [3, 16, 18], 'E3': []}, 16: {'RP': [5], 'E1': [4, 7, 14], 'E2': [3, 16, 18], 'E3': []}, 17: {'RP': [5], 'E1': [12, 15], 'E2': [16, 17], 'E3': []}, 18: {'RP': [5], 'E1': [3, 11, 15], 'E2': [16, 17], 'E3': []}, 19: {'RP': [5], 'E1': [7, 14, 16, 17], 'E2': [15], 'E3': []}, 20: {'RP': [5], 'E1': [11, 14], 'E2': [15], 'E3': []}, 21: {'RP': [5], 'E1': [6, 11, 16], 'E2': [15, 18], 'E3': []}, 22: {'RP': [5], 'E1': [12, 14, 16], 'E2': [15, 18], 'E3': []}, 23: {'RP': [5], 'E1': [11, 13], 'E2': [3, 15, 18], 'E3': []}, 24: {'RP': [5], 'E1': [9, 12], 'E2': [3, 15, 18], 'E3': []}, 25: {'RP': [5], 'E1': [6, 8, 13], 'E2': [15, 17], 'E3': []}, 26: {'RP': [5], 'E1': [10, 11], 'E2': [15, 17], 'E3': []}, 27: {'RP': [5], 'E1': [9, 12, 16], 'E2': [15, 17, 18], 'E3': []}, 28: {'RP': [5], 'E1': [11, 13, 15], 'E2': [14], 'E3': []}, 29: {'RP': [5], 'E1': [9, 12, 15], 'E2': [14], 'E3': []}, 30: {'RP': [5], 'E1': [6, 8, 13, 15], 'E2': [14, 18], 'E3': []}, 31: {'RP': [5], 'E1': [10, 11, 15], 'E2': [14, 18], 'E3': []}, 32: {'RP': [5], 'E1': [11, 13, 14, 17], 'E2': [15, 16, 18], 'E3': []}, 33: {'RP': [5], 'E1': [8, 11, 13], 'E2': [15, 16, 18], 'E3': []}, 34: {'RP': [5], 'E1': [6, 9, 12, 15], 'E2': [14, 17], 'E3': []}, 35: {'RP': [5], 'E1': [11, 12, 13], 'E2': [14, 17], 'E3': []}, 37: {'RP': [5], 'E1': [9, 11, 12], 'E2': [15, 16, 17], 'E3': []}, 39: {'RP': [5], 'E1': [7, 9, 12, 14, 18], 'E2': [6, 15], 'E3': []}, 41: {'RP': [5], 'E1': [10, 11, 12, 16], 'E2': [13], 'E3': []}, 44: {'RP': [5], 'E1': [4, 11, 12, 13, 14], 'E2': [6, 15, 17], 'E3': []}, 47: {'RP': [5], 'E1': [8, 9, 11, 12], 'E2': [13, 17], 'E3': []}, 50: {'RP': [5], 'E1': [9, 10, 11, 12], 'E2': [14, 15, 18], 'E3': []}, 53: {'RP': [5], 'E1': [6, 8, 9, 10, 11], 'E2': [13, 16], 'E3': []}, 56: {'RP': [5], 'E1': [8, 9, 10, 15, 14, 17], 'E2': [3, 11, 18], 'E3': []}, 59: {'RP': [5], 'E1': [4, 7, 9, 10, 11, 12], 'E2': [13, 15], 'E3': []}, 62: {'RP': [5], 'E1': [7, 8, 9, 10, 11, 14], 'E2': [12, 16, 18], 'E3': []}, 65: {'RP': [5], 'E1': [7, 8, 9, 10, 12, 13], 'E2': [11, 16, 18], 'E3': []}, } # Shortstop Error Chart SHORTSTOP_ERROR_CHART: dict[int, dict[str, List[int]]] = { 0: {'RP': [5], 'E1': [], 'E2': [], 'E3': []}, 1: {'RP': [5], 'E1': [18], 'E2': [], 'E3': []}, 2: {'RP': [5], 'E1': [3, 18], 'E2': [], 'E3': []}, 3: {'RP': [5], 'E1': [17], 'E2': [], 'E3': []}, 4: {'RP': [5], 'E1': [17], 'E2': [18], 'E3': []}, 5: {'RP': [5], 'E1': [3, 17], 'E2': [18], 'E3': []}, 6: {'RP': [5], 'E1': [16], 'E2': [18], 'E3': []}, 7: {'RP': [5], 'E1': [3, 16], 'E2': [18], 'E3': []}, 8: {'RP': [5], 'E1': [16, 17], 'E2': [18], 'E3': []}, 10: {'RP': [5], 'E1': [6, 17], 'E2': [3, 18], 'E3': []}, 12: {'RP': [5], 'E1': [15], 'E2': [3, 18], 'E3': []}, 14: {'RP': [5], 'E1': [4, 15], 'E2': [17], 'E3': []}, 16: {'RP': [5], 'E1': [14], 'E2': [17], 'E3': []}, 17: {'RP': [5], 'E1': [15, 16], 'E2': [17], 'E3': []}, 18: {'RP': [5], 'E1': [15, 16, 18], 'E2': [17], 'E3': []}, 19: {'RP': [5], 'E1': [4, 14], 'E2': [17], 'E3': []}, 20: {'RP': [5], 'E1': [4, 15, 16], 'E2': [17], 'E3': []}, 21: {'RP': [5], 'E1': [6, 15], 'E2': [17], 'E3': []}, 22: {'RP': [5], 'E1': [6, 15], 'E2': [17, 18], 'E3': []}, 23: {'RP': [5], 'E1': [3, 13], 'E2': [17, 18], 'E3': []}, 24: {'RP': [5], 'E1': [4, 6, 15], 'E2': [17, 18], 'E3': []}, 25: {'RP': [5], 'E1': [4, 13], 'E2': [17, 18], 'E3': []}, 26: {'RP': [5], 'E1': [12], 'E2': [17, 18], 'E3': []}, 27: {'RP': [5], 'E1': [3, 12], 'E2': [17, 18], 'E3': []}, 28: {'RP': [5], 'E1': [6, 15, 16], 'E2': [3, 17, 18], 'E3': []}, 29: {'RP': [5], 'E1': [11], 'E2': [3, 17, 18], 'E3': []}, 30: {'RP': [5], 'E1': [4, 12], 'E2': [3, 17, 18], 'E3': []}, 31: {'RP': [5], 'E1': [4, 6, 15, 16], 'E2': [3, 17, 18], 'E3': []}, 32: {'RP': [5], 'E1': [13, 15], 'E2': [3, 17, 18], 'E3': []}, 33: {'RP': [5], 'E1': [13, 15], 'E2': [16], 'E3': []}, 34: {'RP': [5], 'E1': [13, 15, 18], 'E2': [16], 'E3': []}, 36: {'RP': [5], 'E1': [13, 15, 17], 'E2': [16], 'E3': []}, 38: {'RP': [5], 'E1': [13, 14], 'E2': [16], 'E3': []}, 40: {'RP': [5], 'E1': [11, 15], 'E2': [16, 18], 'E3': []}, 42: {'RP': [5], 'E1': [12, 14], 'E2': [16, 18], 'E3': []}, 44: {'RP': [5], 'E1': [8, 13], 'E2': [16, 18], 'E3': []}, 48: {'RP': [5], 'E1': [6, 12, 15], 'E2': [3, 18, 18], 'E3': []}, 52: {'RP': [5], 'E1': [11, 13, 18], 'E2': [16, 17], 'E3': []}, 56: {'RP': [5], 'E1': [11, 12, 18], 'E2': [16, 17], 'E3': []}, 60: {'RP': [5], 'E1': [7, 11, 14], 'E2': [15], 'E3': []}, 64: {'RP': [5], 'E1': [6, 9, 12], 'E2': [15, 18], 'E3': []}, 68: {'RP': [5], 'E1': [9, 12, 14], 'E2': [15, 18], 'E3': []}, 72: {'RP': [5], 'E1': [6, 11, 13, 15], 'E2': [4, 16, 17], 'E3': []}, 76: {'RP': [5], 'E1': [9, 12, 13], 'E2': [15, 17], 'E3': []}, 80: {'RP': [5], 'E1': [4, 11, 12, 13], 'E2': [15, 17], 'E3': []}, 84: {'RP': [5], 'E1': [10, 11, 12], 'E2': [3, 15, 17], 'E3': []}, 88: {'RP': [5], 'E1': [9, 11, 12, 16], 'E2': [14], 'E3': []}, } # Pitcher Error Chart PITCHER_ERROR_CHART: dict[int, dict[str, List[int]]] = { 0: {'RP': [5], 'E1': [], 'E2': [], 'E3': []}, 4: {'RP': [5], 'E1': [14], 'E2': [18], 'E3': []}, 6: {'RP': [5], 'E1': [3, 13], 'E2': [18], 'E3': []}, 7: {'RP': [5], 'E1': [3, 12], 'E2': [18], 'E3': []}, 8: {'RP': [5], 'E1': [6, 15, 16, 17], 'E2': [3, 18], 'E3': []}, 10: {'RP': [5], 'E1': [11, 15], 'E2': [3, 18], 'E3': []}, 11: {'RP': [5], 'E1': [12, 15, 16], 'E2': [3, 18], 'E3': []}, 12: {'RP': [5], 'E1': [6, 12, 15], 'E2': [3, 18], 'E3': []}, 13: {'RP': [5], 'E1': [11, 13], 'E2': [17], 'E3': []}, 14: {'RP': [5], 'E1': [7, 13, 14], 'E2': [17], 'E3': []}, 15: {'RP': [5], 'E1': [4, 11, 12], 'E2': [17], 'E3': []}, 16: {'RP': [5], 'E1': [4, 9, 12, 16], 'E2': [17], 'E3': []}, 17: {'RP': [5], 'E1': [3, 6, 11, 12], 'E2': [17], 'E3': []}, 18: {'RP': [5], 'E1': [11, 12, 14], 'E2': [17], 'E3': []}, 19: {'RP': [5], 'E1': [6, 9, 12, 15], 'E2': [17, 18], 'E3': []}, 20: {'RP': [5], 'E1': [6, 10, 11, 15], 'E2': [17, 18], 'E3': []}, 21: {'RP': [5], 'E1': [7, 11, 13, 14], 'E2': [17, 18], 'E3': []}, 22: {'RP': [5], 'E1': [8, 12, 13, 14], 'E2': [17, 18], 'E3': []}, 23: {'RP': [5], 'E1': [10, 11, 12, 16], 'E2': [17, 18], 'E3': []}, 24: {'RP': [5], 'E1': [10, 11, 12, 15], 'E2': [17, 18], 'E3': []}, 26: {'RP': [5], 'E1': [9, 12, 13, 14, 15], 'E2': [3, 17, 18], 'E3': []}, 27: {'RP': [5], 'E1': [10, 11, 12, 13], 'E2': [3, 17, 18], 'E3': []}, 28: {'RP': [5], 'E1': [9, 10, 11, 12], 'E2': [3, 17, 18], 'E3': []}, 30: {'RP': [5], 'E1': [3, 10, 11, 12, 13, 15], 'E2': [16], 'E3': []}, 31: {'RP': [5], 'E1': [10, 11, 12, 13, 14], 'E2': [16], 'E3': []}, 33: {'RP': [5], 'E1': [3, 8, 10, 11, 12, 13], 'E2': [16], 'E3': []}, 34: {'RP': [5], 'E1': [9, 10, 11, 12, 13], 'E2': [16, 18], 'E3': []}, 35: {'RP': [5], 'E1': [9, 10, 11, 12, 14, 15], 'E2': [16, 18], 'E3': []}, 36: {'RP': [5], 'E1': [3, 4, 6, 7, 9, 10, 11, 12], 'E2': [16, 18], 'E3': []}, 38: {'RP': [5], 'E1': [6, 8, 10, 11, 12, 13, 15], 'E2': [16, 18], 'E3': []}, 39: {'RP': [5], 'E1': [6, 7, 8, 9, 10, 12, 13], 'E2': [3, 16, 18], 'E3': []}, 40: {'RP': [5], 'E1': [4, 6, 9, 10, 11, 12, 13, 15], 'E2': [3, 16, 18], 'E3': []}, 42: {'RP': [5], 'E1': [7, 9, 10, 11, 12, 13, 14], 'E2': [3, 16, 18], 'E3': []}, 43: {'RP': [5], 'E1': [6, 7, 8, 9, 10, 12, 13, 14], 'E2': [3, 16, 18], 'E3': []}, 44: {'RP': [5], 'E1': [3, 7, 8, 9, 10, 11, 12, 13], 'E2': [16, 17], 'E3': []}, 46: {'RP': [5], 'E1': [6, 8, 9, 10, 11, 12, 13, 15], 'E2': [16, 17, 18], 'E3': []}, 47: {'RP': [5], 'E1': [7, 8, 9, 10, 11, 12, 13, 15], 'E2': [16, 17, 18], 'E3': []}, 48: {'RP': [5], 'E1': [7, 8, 9, 10, 11, 12, 13, 14], 'E2': [16, 17, 18], 'E3': []}, 50: {'RP': [5], 'E1': [7, 8, 9, 10, 11, 12, 13, 14], 'E2': [15, 17], 'E3': []}, 51: {'RP': [5], 'E1': [7, 8, 9, 10, 11, 12, 13, 14], 'E2': [15, 16], 'E3': []}, } # ============================================================================ # HOLDING RUNNER RESPONSIBILITY CHART # ============================================================================ def get_fielders_holding_runners( runner_bases: List[int], batter_handedness: str ) -> List[str]: """ Determine which fielders are responsible for holding runners. Used to determine if G2#/G3# results should convert to SI2. Args: runner_bases: List of bases with runners (e.g., [1, 3] for R1 and R3) batter_handedness: 'L' or 'R' Returns: List of position codes responsible for holds (e.g., ['1B', 'SS']) """ if not runner_bases: return [] holding_positions = [] mif_vs_batter = '2B' if batter_handedness.lower() == 'r' else 'SS' if 1 in runner_bases: holding_positions.append('1B') holding_positions.append(mif_vs_batter) if 2 in runner_bases and mif_vs_batter not in holding_positions: holding_positions.append(mif_vs_batter) if 3 in runner_bases: holding_positions.append('3B') return holding_positions # ============================================================================ # ERROR CHART LOOKUP HELPER # ============================================================================ def get_error_chart_for_position(position: str) -> dict[int, dict[str, List[int]]]: """ Get error chart for a specific position. Args: position: Position code (P, C, 1B, 2B, 3B, SS, LF, CF, RF) Returns: Error chart dict Raises: ValueError: If position not recognized """ charts = { 'P': PITCHER_ERROR_CHART, 'C': CATCHER_ERROR_CHART, '1B': FIRST_BASE_ERROR_CHART, '2B': SECOND_BASE_ERROR_CHART, '3B': THIRD_BASE_ERROR_CHART, 'SS': SHORTSTOP_ERROR_CHART, 'LF': LF_RF_ERROR_CHART, 'RF': LF_RF_ERROR_CHART, 'CF': CF_ERROR_CHART, } if position not in charts: raise ValueError(f"Unknown position: {position}") return charts[position]