Z-Scored Role Profiles and Dual Hungarian Assignment: How We Build the Optimal XII
Z-Scored Role Profiles and Dual Hungarian Assignment: How We Build the Optimal XII
The Optimal XII system asks one question: which 12 players, assigned to their best-fit roles, maximize total team value given real-world constraints like overseas limits, keeper requirements, and bowling capacity?
Picking the 12 highest-rated players fails. A team of six openers and six death bowlers collapses in the middle overs. Position matters. Phase matters. The system assigns every player to one of 8 batting roles and 5 bowling roles, scores them against data-derived profiles using z-scored features, then solves two simultaneous assignment problems to find the combination that maximizes total positional value.
The Feature System
18 Batting Features
Every batter is scored on 18 features extracted from ball-by-ball data across IPL, T20I, SMAT, BBL, The Hundred, SA20, CPL, and PSL (2022-2025):
| Feature | Source | What It Captures |
|---|---|---|
| pp_sr | Phase strike rate, overs 1-6 | Powerplay scoring ability |
| mid_sr | Phase strike rate, overs 7-14 | Middle-overs run production |
| death_sr | Phase strike rate, overs 15-20 | Death-overs acceleration |
| mid_entry_sr | Cold-start SR entering in middle overs (first 10 balls) | Can they score from ball 1 at over 8? |
| death_entry_sr | Cold-start SR entering in death overs (first 10 balls) | Can they hit immediately at over 16? |
| set_mid_sr | SR in middle overs after being set (15+ balls faced) | Batting-through ability for openers |
| pp_share | Fraction of balls faced in powerplay | Positional signal: opener vs middle-order |
| death_share | Fraction of balls faced at death | Positional signal: finisher vs top-order |
| bpi | Balls per innings | Longevity |
| four_pct | Fours per ball | Boundary composition |
| six_pct | Sixes per ball | Power hitting ability |
| bdry_pct | Boundaries (4s + 6s) per ball | Overall boundary rate |
| dot_pct | Dot balls per ball | Scoring consistency |
| sr | Overall strike rate | Aggregate run rate |
| wpa_per_ball | Win probability added per ball | Impact on match outcomes |
| pressure_sr | Strike rate under high pressure | Performance when it matters most |
| consistency | Negative of score standard deviation | Reliability of output |
| avg | Batting average | Runs per dismissal |
The situation-aware features (`mid_entry_sr`, `death_entry_sr`, `set_mid_sr`) come from a dedicated pipeline that reconstructs every batter's innings from cricsheet ball-by-ball data. It tracks when each batter entered the innings (which over), then computes their strike rate in the first 10 legal balls (cold start) and after 15+ balls (set).
This distinction is critical: a #3 who enters cold at over 8 against spin faces a completely different challenge than an opener who reaches over 8 already set on 25 balls. Traditional middle-overs SR conflates these two situations. Cold-start SR separates them.
11 Bowling Features
Every bowler (players where `canBowl=True`) is scored on 11 features:
| Feature | Source | Direction |
|---|---|---|
| pp_econ | Phase economy, overs 1-6 | Inverted (lower = better) |
| mid_econ | Phase economy, overs 7-14 | Inverted |
| death_econ | Phase economy, overs 15-20 | Inverted |
| econ | Overall economy rate | Inverted |
| bowl_sr | Bowling strike rate | Inverted (lower = better) |
| dot_pct | Dot ball percentage | Natural (higher = better) |
| wpa_per_ball | Bowling win probability added per ball | Natural |
| pressure_econ | Economy under high pressure | Inverted |
| pp_wkt_rate | Wickets per over in powerplay | Natural |
| mid_wkt_rate | Wickets per over in middle overs | Natural |
| death_wkt_rate | Wickets per over at death | Natural |
For inverted features, the z-score is flipped so that positive values always mean "better." A bowler with 7 economy gets a positive z-score; one with 10 gets negative.
Format Weighting
Stats are aggregated across formats with diminishing weights:
| Format | Weight |
|---|---|
| IPL | 1.0 |
| T20I | 0.5 |
| SMAT | 0.3 |
| BBL, The Hundred, SA20, CPL, PSL | 0.3 |
IPL dominates because it is the target competition. The pitches, pressure, and opposition quality are what the system optimizes for. T20I data at 0.50 captures international skill level while acknowledging different conditions. SMAT and other franchise leagues at 0.30 provide supplementary signal, especially for players with limited IPL exposure.
Recency Weighting
Recent performance matters more than historical data. Season multipliers:
| Season | Weight |
|---|---|
| 2025-26 (current) | 3.0x |
| 2024 | 2.0x |
| 2023 | 1.0x |
| 2022 | 0.5x |
A player's 2025 IPL data contributes 6x more signal than their 2022 IPL data (3.0/0.5). Combined with format weighting, each data point carries: `recency_weight x format_weight x balls_in_phase`.
Z-Score Normalization
Raw features are converted to z-scores within the league: `z = (value - mean) / std_dev`. This puts all features on a common scale. A z-score of +1.5 on `pp_sr` means the batter's powerplay strike rate is 1.5 standard deviations above the league mean, directly comparable to a +1.5 on `death_entry_sr` or `six_pct`.
Batting and bowling features are z-scored separately (using their own league-wide means and standard deviations).
The 13 Role Profiles
8 Batting Roles
Each role profile is a set of feature weights derived from actual IPL data: classify players by where they bat, take the top-performing half (by batting WAR), compute their mean z-scores, and normalize positive values to sum to 1.0. The profiles reflect what elite players in each position actually look like, not theoretical ideals.
Opener (x2)
| Feature | Weight |
|---|---|
| pp_sr | 0.160 |
| set_mid_sr | 0.140 |
| avg | 0.120 |
| bpi | 0.100 |
| bdry_pct | 0.095 |
| sr | 0.080 |
| pp_share | 0.070 |
| pressure_sr | 0.070 |
| four_pct | 0.065 |
| six_pct | 0.050 |
| wpa_per_ball | 0.050 |
Openers are defined by powerplay scoring (pp_sr 0.160) and the ability to bat through into middle overs while set (set_mid_sr 0.140). The `set_mid_sr` feature captures what happens after an opener has faced 15+ balls and transitions into overs 7-14, separating true through-the-innings openers from PP-only hitters.
#3 (Top Order)
| Feature | Weight |
|---|---|
| wpa_per_ball | 0.150 |
| mid_entry_sr | 0.130 |
| sr | 0.120 |
| bdry_pct | 0.100 |
| avg | 0.100 |
| dot_pct | -0.080 |
| pp_sr | 0.080 |
| six_pct | 0.070 |
| bpi | 0.060 |
| set_mid_sr | 0.060 |
| pressure_sr | 0.050 |
The #3 typically arrives late in the powerplay or early middle overs. `mid_entry_sr` (0.130) captures cold-start scoring when entering around overs 6-8. This is the transition moment that defines great #3s.
Note the negative weight on `dot_pct` (-0.080): a #3 who starts slowly (high dot%) is penalized because the position demands immediate impact.
#4 (Top Order)
| Feature | Weight |
|---|---|
| mid_entry_sr | 0.280 |
| avg | 0.150 |
| bpi | 0.140 |
| wpa_per_ball | 0.120 |
| death_entry_sr | 0.100 |
| pressure_sr | 0.080 |
| six_pct | 0.070 |
| sr | 0.060 |
The #4 almost always enters cold in middle overs (overs 8-13) against spin. `mid_entry_sr` at 0.280 is the single highest weight for any feature in any batting profile. It answers one question: can they score from ball 1 when walking in at over 8 against a spinning ball?
Traditional `mid_sr` was removed from this profile because it conflates "set opener batting at over 10" with "cold #4 arriving at over 10."
#5 and #6 (Middle Order, x2)
| Feature | Weight |
|---|---|
| death_entry_sr | 0.220 |
| mid_entry_sr | 0.130 |
| six_pct | 0.130 |
| bdry_pct | 0.105 |
| wpa_per_ball | 0.090 |
| sr | 0.080 |
| avg | 0.078 |
| pressure_sr | 0.060 |
| bpi | 0.055 |
| death_share | 0.052 |
Middle-order batters at #5-6 enter in mid-to-death overs. `death_entry_sr` (0.220) is the primary feature because if they arrive at over 15, they must hit immediately. `six_pct` (0.130) rewards power. At this stage, singles are not enough.
Finisher
| Feature | Weight |
|---|---|
| death_entry_sr | 0.280 |
| six_pct | 0.180 |
| bdry_pct | 0.140 |
| sr | 0.120 |
| pressure_sr | 0.100 |
| death_share | 0.100 |
| consistency | 0.080 |
The finisher's sole job is death-overs destruction. `death_entry_sr` (0.280) dominates: they walk in cold at over 16 and must hit from ball 1. `six_pct` (0.180) follows because death-overs scoring relies on clearing the boundary when the field is spread.
`consistency` (0.080) is the surprise inclusion: a finisher who delivers 30(12) every time is more valuable than one who alternates 50(15) and 2(5).
Lower Order #8 (Depth Slot)
| Feature | Weight |
|---|---|
| death_entry_sr | 0.250 |
| six_pct | 0.200 |
| sr | 0.180 |
| bdry_pct | 0.170 |
| mid_entry_sr | 0.100 |
| pressure_sr | 0.100 |
This is not a full batting slot. It is a depth bonus. After the top 7 batters are assigned via Hungarian, the remaining players in the XI are scored against this profile, and the best score is added as a bonus. A team with Sunil Narine or Jason Holder at #8 gets significant depth credit; a team with six pure tailenders gets near-zero. This rewards teams that have genuine batting depth beyond their top 7.
5 Bowling Roles
Bowling profiles are derived using the same methodology as batting: classify bowlers by actual phase behavior in IPL 2022-2025, take the top 50% by bowling WAR, compute mean z-scores, normalize.
PP Bowler (x2)
| Feature | Weight |
|---|---|
| dot_pct | 0.375 |
| pp_econ | 0.186 |
| wpa_per_ball | 0.161 |
| econ | 0.127 |
| death_wkt_rate | 0.102 |
| bowl_sr | 0.049 |
New-ball bowling is defined by dot ball pressure. At 0.375, `dot_pct` is the single highest-weighted feature in the entire system, reflecting the reality that PP bowling is about building pressure through dots, forcing batters into errors. `pp_econ` (0.186) captures direct containment.
The `death_wkt_rate` weight (0.102) may seem surprising for a PP bowler, but elite new-ball bowlers like Bumrah and Boult also tend to take death wickets. It functions as a signal of overall bowling quality.
Middle Bowler (x2)
| Feature | Weight |
|---|---|
| pressure_econ | 0.282 |
| econ | 0.224 |
| mid_econ | 0.211 |
| death_econ | 0.135 |
| pp_wkt_rate | 0.108 |
| mid_wkt_rate | 0.025 |
| bowl_sr | 0.012 |
| wpa_per_ball | 0.004 |
Middle-overs bowling rewards control above all else. `pressure_econ` (0.282) dominates: maintaining low economy when the batting team is under pressure (or applying pressure when they are not). This is where spinners like Rashid Khan and Varun Chakravarthy separate from average options.
The low `wpa_per_ball` weight (0.004) reflects that middle-overs bowling is less about individual match-winning moments and more about sustained squeeze.
Death Bowler (x1)
| Feature | Weight |
|---|---|
| bowl_sr | 0.247 |
| mid_econ | 0.185 |
| wpa_per_ball | 0.128 |
| econ | 0.110 |
| pp_econ | 0.088 |
| pressure_econ | 0.084 |
| death_econ | 0.072 |
| death_wkt_rate | 0.045 |
| pp_wkt_rate | 0.039 |
`bowl_sr` (0.247) leads because death bowling is about wickets. A yorker that gets hit for 2 runs but takes a wicket is better than a length ball that goes for 1 run. The relatively low `death_econ` weight (0.072) is counterintuitive but data-derived: elite death bowlers are distinguished more by their ability to take wickets than by pure economy.
The Dual Hungarian Assignment
The optimizer runs two simultaneous assignment problems:
1. Batting Assignment (7 slots)
The top 7 non-tail batters in each XI are assigned to 7 batting roles (opener_1, opener_2, top_order_3, top_order_4, middle_order_5, middle_order_6, finisher) using the Hungarian algorithm. This finds the assignment that maximizes the sum of role-fit scores across all 7 positions, guaranteeing a globally optimal assignment in O(n cubed) time.
A player's role-fit score for a given role is: the sum of (feature_weight times z_score) across all features in that role's profile. A confidence multiplier (based on sample size) scales the result: players with more data get their z-scores counted at full strength; players with limited data are regressed toward zero.
After the top 7 are assigned, remaining XI members are scored against the `lower_order_8` depth profile. The best score among them becomes a depth bonus added to the team's batting value.
2. Bowling Assignment (5 slots)
All bowlers in the XI are assigned to 5 bowling roles (pp_bowler_1, pp_bowler_2, middle_bowler_1, middle_bowler_2, death_bowler_1) using a separate Hungarian assignment. Specialist bowlers fill slots first; allrounders only receive bowling credit for unfilled slots. This prevents allrounders from double-dipping (earning both batting and bowling credit) and displacing elite specialists.
3. Powerplay Quality Bonus
The two best PP batters (by pp_sr z-score) are scored against the opener profile, and a weighted bonus is added. This explicitly rewards teams with strong powerplay openers. Data shows teams with zero PP wickets win 69% of the time.
4. Bowling WAR Dilution
Only 20 overs exist in a T20 innings. If the XII has 28 overs of bowling capacity, each bowler's impact is diluted to 71% (20/28). The dilution factor is: `min(1.0, 20 / total_bowling_capacity)`.
This naturally penalizes excess bowlers without a hard cutoff. A 6th specialist (24 overs, 83% utilization) is slightly diluted. A 7th (28 overs, 71% utilization) is heavily diluted. Allrounders contribute 2-3 overs each, so they cause mild dilution at most. The dilution applies directly to the bowling value term in the final scoring formula.
5. Phase Balance Penalties
The optimizer penalizes structurally flawed attacks:
- Fewer than 2 pace bowlers: -3.0 penalty (cannot cover the PP with 0-1 pacers)
- Fewer than 3 pace bowlers: -1.0 penalty (thin pace coverage)
- Poor death bowling: If the top 2 death pacers average below -0.3 role-fit score, -3.0 penalty
- Below-average death bowling: If the top 2 death pacers average below 0, -1.5 penalty
- Only one viable death bowler: If the 2nd-best death pacer scores below -0.5, -1.0 penalty
- 4+ spinners: -2.0 penalty (leaves too few pace overs for PP/death)
- 7+ specialist bowlers: -3.0 penalty per excess bowler above 6. Allrounders do not count. A T20 team needs at most 6 specialists (5 bowl, 1 backup). Beyond that, a bowler is sitting idle, and the XII should select a batter or allrounder instead.
6. Final Team Score
```
total = bat_value x 5.0 + bowl_value x 4.0 x bowl_utilization + pp_bonus x 2.0 + phase_penalty
```
Batting is weighted at 5.0 and bowling at 4.0, reflecting that bowling wins T20 tournaments just as much as batting. The bowling term is scaled by `bowl_utilization` so that extra bowlers' value is proportionally reduced rather than counted at full strength. The PP bonus (2.0) adds an explicit opener-quality premium.
The Optimizer Search
For each team's squad (typically 20-25 players), the optimizer:
1. Filters out injured/unavailable players
2. Enumerates all C(n, 12) combinations of eligible players
3. For each combination, checks constraints (overseas, keeper, bowling capacity)
4. For valid combinations, runs dual Hungarian assignment + bonuses/penalties
5. Returns the combination and assignment with the highest total score
The captain (if available and not injured) is locked into every combination. When a captain is unavailable (e.g., Pat Cummins missing early SRH games), the captain constraint is released and the optimizer selects purely on merit.
The 6 Constraints
| Constraint | Rule |
|---|---|
| Captain lock | Named captain must appear in the XII (skipped if captain is injured/unavailable) |
| Overseas cap | Maximum 4 overseas players |
| Wicketkeeper | At least 1 designated keeper |
| Bowling capacity | XII must provide at least 23 total bowling overs (allows one bowler to sit out on match day) |
| Pace minimum | At least 2 specialist pace bowlers |
| Spin minimum | At least 1 specialist spinner |
The bowling capacity constraint uses 23 overs because the XII includes a 12th man. The playing XI on any given day needs 20 overs of bowling, and having 23 or more total across 12 players provides the flexibility to rest one bowler.
Unavailable Players
Players who are injured or otherwise unavailable for the start of IPL 2026 are filtered before optimization:
- Nathan Ellis (CSK): injured
- Sam Curran (RR): injured
- Lockie Ferguson (PBKS): unavailable for first 7 games
- Pat Cummins (SRH): unavailable for first few games
- Matheesha Pathirana (KKR): will miss first part of IPL 2026
- Jack Edwards (SRH): injured
When a captain is in the unavailable list (e.g., Cummins for SRH), the system automatically releases the captain constraint and the team's acting captain competes for selection on merit like any other player.
Data Sources
The pipeline ingests ball-by-ball data from 8 T20 formats via cricsheet:
| Format | Seasons | Source |
|---|---|---|
| IPL | 2022-2025 | cricsheet JSON |
| T20I | 2022-2025 | cricsheet JSON |
| SMAT | 2022-2025 | cricsheet JSON |
| BBL | 2022-2025 | cricsheet JSON |
| The Hundred | 2022-2025 | cricsheet JSON |
| SA20 | 2022-2025 | cricsheet JSON |
| CPL | 2022-2025 | cricsheet JSON |
| PSL | 2022-2025 | cricsheet JSON |
Three specialized pipelines extract features:
- Batting situation stats: Reconstructs per-batter innings to compute cold-start SR (first 10 balls) and set SR (after 15+ balls) by entry phase
- Batting bowling-type skills: Splits batter performance by pace vs spin bowling using a bowler-type lookup (from squads + heuristic classification)
- Bowling type skills: Per-bowler phase-specific economy, dot%, boundary%, and wicket rates
What Changed from v1
| Aspect | v1 | Current System |
|---|---|---|
| Batting features | SRae, vs-spin/pace SR, WAR | 18 z-scored features including cold-start SR, set SR |
| Batting roles | 7 slots (opener x2, first drop, middle x2, finisher, lower-order AR) | 8 roles (opener x2, #3, #4, #5-6 x2, finisher, lower_order_8 depth bonus) |
| Bowling evaluation | Raw bowling WAR/match add-on | 11 z-scored bowling features, 5 role profiles, full Hungarian assignment |
| Bowling roles | 4 slots (PP, middle x2, death) | 5 roles (PP x2, middle x2, death) |
| Bowling WAR scaling | None | Diluted by min(1.0, 20/capacity) to penalize over-sharing |
| Specialist bowler cap | None | -3.0 penalty per specialist bowler above 6 |
| Role profile source | Hand-tuned weights | Data-derived from IPL 2022-2025 (mean z-scores of top performers) |
| Role scoring | Weighted sum of raw metrics | Dot product of z-scored features x profile weights |
| Data formats | IPL only | IPL + T20I + SMAT + BBL + The Hundred + SA20 + CPL + PSL |
| Format weights | IPL 1.0, T20I 0.7, SMAT 0.4 | IPL 1.0, T20I 0.5, SMAT/others 0.3 |
| Assignment | Single Hungarian (batting only) | Dual Hungarian (batting + bowling independently) |
| Depth bonus | None | lower_order_8 profile scores best remaining batter |
| PP quality | Not modeled | Explicit bonus for strong powerplay openers |
| Phase balance | Not modeled | Penalties for poor pace coverage, weak death bowling, spin overload |
| Predicted XI | Separate "most likely" XI | Removed: Optimal XII only |
| Captain handling | Fixed lock | Lock with injury bypass |
Limitations
- Cold-start sample sizes: Some players have limited balls in specific entry situations. A batter with only 15 cold-start balls entering at death has noisy `death_entry_sr`. The confidence multiplier partially mitigates this but does not eliminate it.
- Bowler classification: Non-squad bowlers are classified as pace or spin using a heuristic (55%+ middle-overs deliveries = spin). This is imperfect. Medium-pace part-timers can be misclassified.
- Fixed slot structure: Real batting orders are fluid. A #3 might open some days; a finisher might come in at #4 during a collapse. The 8+5 role model is a simplification.
- Format transfer: A player's BBL stats may not translate to IPL conditions. Format weighting reduces this risk but does not eliminate it.
- Data gaps: Some players (especially domestic Indian batters with recent SMAT debuts) have limited or no cricsheet data. Their features are sparse and confidence-weighted toward zero, which may undervalue them.
- No pitch modeling: The system uses aggregate stats without adjusting for venue or pitch conditions on a given match day.
The Optimal XII is a pre-tournament structural recommendation: the best 12-man squad based on aggregate role-fit scoring. It captures why each player belongs in a specific position, not just whether they are broadly good. But T20 cricket is volatile, and a model operating on career aggregates will always miss the form swings, tactical adjustments, and matchup-specific rotations that define individual match days.