Added complete error chart data for all infield positions to finalize Phase 3B X-Check league config tables implementation. Changes: - Added CATCHER_ERROR_CHART (17 ratings: 0-16) - Added FIRST_BASE_ERROR_CHART (31 ratings: 0-30) - Added SECOND_BASE_ERROR_CHART (40 ratings: 0-71, sparse) - Added THIRD_BASE_ERROR_CHART (45 ratings: 0-65, sparse) - Added SHORTSTOP_ERROR_CHART (43 ratings: 0-88, sparse) - Added PITCHER_ERROR_CHART (40 ratings: 0-51, sparse) All charts follow same structure as outfield charts with E3 field (empty for infield positions). Total ~250 lines of error chart data. Testing: - Updated test_infield_error_charts_complete() to verify charts populated - All 36 X-Check table tests passing - Verified chart structure matches outfield charts Phase 3B Status: 100% COMPLETE ✅ All defense tables complete, all error charts complete, ready for Phase 3C 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
490 lines
31 KiB
Python
490 lines
31 KiB
Python
"""
|
||
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]
|