Migrated to ruff for faster, modern code formatting and linting: Configuration changes: - pyproject.toml: Added ruff 0.8.6, removed black/flake8 - Configured ruff with black-compatible formatting (88 chars) - Enabled comprehensive linting rules (pycodestyle, pyflakes, isort, pyupgrade, bugbear, comprehensions, simplify, return) - Updated CLAUDE.md: Changed code quality commands to use ruff Code improvements (490 auto-fixes): - Modernized type hints: List[T] → list[T], Dict[K,V] → dict[K,V], Optional[T] → T | None - Sorted all imports (isort integration) - Removed unused imports - Fixed whitespace issues - Reformatted 38 files for consistency Bug fixes: - app/core/play_resolver.py: Fixed type hint bug (any → Any) - tests/unit/core/test_runner_advancement.py: Removed obsolete random mock Testing: - All 739 unit tests passing (100%) - No regressions introduced 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
487 lines
25 KiB
Python
487 lines
25 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
|
||
"""
|
||
|
||
# ============================================================================
|
||
# 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]
|