Live standings/bracket updates + possession stats
This commit is contained in:
parent
e3242583b6
commit
39ea6c0c0a
6 changed files with 249 additions and 85 deletions
13
DEVLOG.md
13
DEVLOG.md
|
|
@ -54,3 +54,16 @@
|
|||
- Updated `quick` command to support optional `--home` / `--away`:
|
||||
- If missing, CPU auto-fills from remaining teams.
|
||||
- Same seed yields the same auto-filled teams and outcomes.
|
||||
|
||||
## 2026-02-10 - Live standings/bracket updates + possession surfacing
|
||||
|
||||
### Scope completed
|
||||
- Added explicit possession fields to match results and surfaced them in quick mode output.
|
||||
- Added possession row to single-match CSV export output.
|
||||
- Implemented live detail updates driven by simulation frames:
|
||||
- per-frame stats updates
|
||||
- per-frame competition panel updates
|
||||
- incremental history updates
|
||||
- Added live league standings snapshots after each fixture.
|
||||
- Added ASCII knockout tree bracket snapshots and updates after each semifinal/final.
|
||||
- Tuned detail view layout to allocate more space to stats/competition/history panels.
|
||||
|
|
|
|||
|
|
@ -64,6 +64,14 @@ pub fn simulation_to_csv_bytes(sim: &PreparedSimulation) -> io::Result<Vec<u8>>
|
|||
format!("{:.2}", m.stats.away.xg),
|
||||
],
|
||||
)?;
|
||||
write_row(
|
||||
&mut out,
|
||||
&[
|
||||
"Possession".to_string(),
|
||||
format!("{}%", m.home_possession),
|
||||
format!("{}%", m.away_possession),
|
||||
],
|
||||
)?;
|
||||
write_row(
|
||||
&mut out,
|
||||
&[
|
||||
|
|
|
|||
|
|
@ -56,9 +56,9 @@ impl SimulationInstance {
|
|||
let mut rng = Rng::new(self.seed);
|
||||
let prepared = run_simulation(self.sim_type, &self.teams, &mut rng);
|
||||
let total_frames = prepared.frames.len();
|
||||
self.stats_lines = prepared.stats_lines.clone();
|
||||
self.competition_lines = prepared.competition_lines.clone();
|
||||
self.history_lines = prepared.history_lines.clone();
|
||||
self.stats_lines.clear();
|
||||
self.competition_lines.clear();
|
||||
self.history_lines.clear();
|
||||
self.status = SimStatus::Running {
|
||||
frame_index: 0,
|
||||
total_frames,
|
||||
|
|
@ -101,6 +101,15 @@ impl SimulationInstance {
|
|||
for line in frame.logs {
|
||||
self.push_log(line);
|
||||
}
|
||||
if let Some(stats) = frame.stats_lines {
|
||||
self.stats_lines = stats;
|
||||
}
|
||||
if let Some(comp) = frame.competition_lines {
|
||||
self.competition_lines = comp;
|
||||
}
|
||||
for item in frame.history_append {
|
||||
self.history_lines.push(item);
|
||||
}
|
||||
frame_index += 1;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -100,6 +100,7 @@ fn quick_mode(home: Option<String>, away: Option<String>, base_seed: u64) -> io:
|
|||
if let sim::SimOutcome::Single(m) = prepared.outcome {
|
||||
println!("{} {}-{} {}", m.home, m.home_goals, m.away_goals, m.away);
|
||||
println!("xG {:.2} - {:.2}", m.stats.home.xg, m.stats.away.xg);
|
||||
println!("Possession {}% - {}%", m.home_possession, m.away_possession);
|
||||
}
|
||||
|
||||
println!("-- log --");
|
||||
|
|
|
|||
293
src/sim.rs
293
src/sim.rs
|
|
@ -49,6 +49,8 @@ pub struct MatchResult {
|
|||
pub home_profile: TeamProfile,
|
||||
pub away_profile: TeamProfile,
|
||||
pub stats: MatchStats,
|
||||
pub home_possession: u8,
|
||||
pub away_possession: u8,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
@ -68,6 +70,9 @@ pub struct StandingsRow {
|
|||
pub struct SimFrame {
|
||||
pub scoreboard: String,
|
||||
pub logs: Vec<String>,
|
||||
pub stats_lines: Option<Vec<String>>,
|
||||
pub competition_lines: Option<Vec<String>>,
|
||||
pub history_append: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
@ -86,8 +91,6 @@ pub enum SimOutcome {
|
|||
pub struct PreparedSimulation {
|
||||
pub frames: Vec<SimFrame>,
|
||||
pub outcome: SimOutcome,
|
||||
pub stats_lines: Vec<String>,
|
||||
pub competition_lines: Vec<String>,
|
||||
pub history_lines: Vec<String>,
|
||||
}
|
||||
|
||||
|
|
@ -95,6 +98,38 @@ fn chance(rng: &mut Rng, p: f64) -> bool {
|
|||
rng.chance(p)
|
||||
}
|
||||
|
||||
fn possession_pct(result: &MatchResult) -> (u8, u8) {
|
||||
let home_poss_base = (result.stats.home.attacks as f64)
|
||||
* if result.home_profile.tactic == "possession" {
|
||||
1.15
|
||||
} else {
|
||||
1.0
|
||||
};
|
||||
let away_poss_base = (result.stats.away.attacks as f64)
|
||||
* if result.away_profile.tactic == "possession" {
|
||||
1.15
|
||||
} else {
|
||||
1.0
|
||||
};
|
||||
let home_poss = if (home_poss_base + away_poss_base) > 0.0 {
|
||||
((home_poss_base / (home_poss_base + away_poss_base)) * 100.0).round() as u8
|
||||
} else {
|
||||
50
|
||||
};
|
||||
let away_poss = 100 - home_poss;
|
||||
(home_poss, away_poss)
|
||||
}
|
||||
|
||||
fn empty_frame(scoreboard: String, logs: Vec<String>) -> SimFrame {
|
||||
SimFrame {
|
||||
scoreboard,
|
||||
logs,
|
||||
stats_lines: None,
|
||||
competition_lines: None,
|
||||
history_append: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn penalties(rng: &mut Rng) -> (u8, u8, bool) {
|
||||
let mut h = 0u8;
|
||||
let mut a = 0u8;
|
||||
|
|
@ -145,8 +180,8 @@ pub fn simulate_match(home: &str, away: &str, rng: &mut Rng) -> (MatchResult, Ve
|
|||
away_tactic.label
|
||||
);
|
||||
|
||||
let mut frames = vec![SimFrame {
|
||||
scoreboard: format!(
|
||||
let mut frames = vec![empty_frame(
|
||||
format!(
|
||||
"{} ({}) {} - {} {} ({}) | {}'",
|
||||
display_name(home),
|
||||
home_profile.formation,
|
||||
|
|
@ -156,8 +191,8 @@ pub fn simulate_match(home: &str, away: &str, rng: &mut Rng) -> (MatchResult, Ve
|
|||
away_profile.formation,
|
||||
pad2(minute)
|
||||
),
|
||||
logs: vec![kickoff],
|
||||
}];
|
||||
vec![kickoff],
|
||||
)];
|
||||
|
||||
while minute < 90 {
|
||||
minute += 1;
|
||||
|
|
@ -296,8 +331,8 @@ pub fn simulate_match(home: &str, away: &str, rng: &mut Rng) -> (MatchResult, Ve
|
|||
));
|
||||
}
|
||||
|
||||
frames.push(SimFrame {
|
||||
scoreboard: format!(
|
||||
frames.push(empty_frame(
|
||||
format!(
|
||||
"{} ({}) {} - {} {} ({}) | {}'",
|
||||
display_name(home),
|
||||
home_profile.formation,
|
||||
|
|
@ -308,9 +343,22 @@ pub fn simulate_match(home: &str, away: &str, rng: &mut Rng) -> (MatchResult, Ve
|
|||
pad2(minute)
|
||||
),
|
||||
logs,
|
||||
});
|
||||
));
|
||||
}
|
||||
|
||||
let preview_result = MatchResult {
|
||||
home: home.to_string(),
|
||||
away: away.to_string(),
|
||||
home_goals,
|
||||
away_goals,
|
||||
home_profile,
|
||||
away_profile,
|
||||
stats: stats.clone(),
|
||||
home_possession: 50,
|
||||
away_possession: 50,
|
||||
};
|
||||
let (home_poss, away_poss) = possession_pct(&preview_result);
|
||||
|
||||
(
|
||||
MatchResult {
|
||||
home: home.to_string(),
|
||||
|
|
@ -320,6 +368,8 @@ pub fn simulate_match(home: &str, away: &str, rng: &mut Rng) -> (MatchResult, Ve
|
|||
home_profile,
|
||||
away_profile,
|
||||
stats,
|
||||
home_possession: home_poss,
|
||||
away_possession: away_poss,
|
||||
},
|
||||
frames,
|
||||
)
|
||||
|
|
@ -328,24 +378,6 @@ pub fn simulate_match(home: &str, away: &str, rng: &mut Rng) -> (MatchResult, Ve
|
|||
pub fn match_stats_lines(result: &MatchResult) -> Vec<String> {
|
||||
let home_tactic = tactic_by_key(result.home_profile.tactic);
|
||||
let away_tactic = tactic_by_key(result.away_profile.tactic);
|
||||
let home_poss_base = (result.stats.home.attacks as f64)
|
||||
* if result.home_profile.tactic == "possession" {
|
||||
1.15
|
||||
} else {
|
||||
1.0
|
||||
};
|
||||
let away_poss_base = (result.stats.away.attacks as f64)
|
||||
* if result.away_profile.tactic == "possession" {
|
||||
1.15
|
||||
} else {
|
||||
1.0
|
||||
};
|
||||
let home_poss = if (home_poss_base + away_poss_base) > 0.0 {
|
||||
((home_poss_base / (home_poss_base + away_poss_base)) * 100.0).round() as u8
|
||||
} else {
|
||||
50
|
||||
};
|
||||
let away_poss = 100 - home_poss;
|
||||
|
||||
vec![
|
||||
format!(
|
||||
|
|
@ -385,7 +417,10 @@ pub fn match_stats_lines(result: &MatchResult) -> Vec<String> {
|
|||
"Saves: {} vs {}",
|
||||
result.stats.home.saves, result.stats.away.saves
|
||||
),
|
||||
format!("Possession: {}% vs {}%", home_poss, away_poss),
|
||||
format!(
|
||||
"Possession: {}% vs {}%",
|
||||
result.home_possession, result.away_possession
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -397,6 +432,36 @@ fn standings_cmp(a: &StandingsRow, b: &StandingsRow) -> Ordering {
|
|||
.then(a.team.cmp(&b.team))
|
||||
}
|
||||
|
||||
fn league_table_lines(rows: &[StandingsRow]) -> Vec<String> {
|
||||
let mut out = Vec::with_capacity(rows.len() + 2);
|
||||
out.push("TEAM P W D L GF GA GD PTS".to_string());
|
||||
out.push("--------------------------------------------------------".to_string());
|
||||
for r in rows {
|
||||
out.push(format!(
|
||||
"{:<28} {:>2} {:>2} {:>2} {:>2} {:>3} {:>2} {:>3} {:>3}",
|
||||
r.team, r.p, r.w, r.d, r.l, r.gf, r.ga, r.gd, r.pts
|
||||
));
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
fn knockout_bracket_lines(
|
||||
semi1: Option<&str>,
|
||||
semi2: Option<&str>,
|
||||
final_line: Option<&str>,
|
||||
champion: Option<&str>,
|
||||
) -> Vec<String> {
|
||||
vec![
|
||||
"Knockout Bracket".to_string(),
|
||||
format!("Semi 1: {}", semi1.unwrap_or("TBD")),
|
||||
" \\".to_string(),
|
||||
format!(" +-- Final: {}", final_line.unwrap_or("TBD")),
|
||||
" /".to_string(),
|
||||
format!("Semi 2: {}", semi2.unwrap_or("TBD")),
|
||||
format!("Champion: {}", champion.unwrap_or("TBD")),
|
||||
]
|
||||
}
|
||||
|
||||
fn init_table(teams: &[String]) -> BTreeMap<String, StandingsRow> {
|
||||
let mut map = BTreeMap::new();
|
||||
for team in teams {
|
||||
|
|
@ -423,11 +488,23 @@ pub fn run_single(teams: &[String], rng: &mut Rng) -> PreparedSimulation {
|
|||
let away = teams[1].clone();
|
||||
let (result, frames) = simulate_match(&home, &away, rng);
|
||||
let stats_lines = match_stats_lines(&result);
|
||||
let mut frames = frames;
|
||||
frames.push(SimFrame {
|
||||
scoreboard: format!(
|
||||
"{} {}-{} {} | FT",
|
||||
display_name(&result.home),
|
||||
result.home_goals,
|
||||
result.away_goals,
|
||||
display_name(&result.away)
|
||||
),
|
||||
logs: Vec::new(),
|
||||
stats_lines: Some(stats_lines.clone()),
|
||||
competition_lines: None,
|
||||
history_append: Vec::new(),
|
||||
});
|
||||
PreparedSimulation {
|
||||
frames,
|
||||
outcome: SimOutcome::Single(result),
|
||||
stats_lines,
|
||||
competition_lines: vec![],
|
||||
history_lines: vec![],
|
||||
}
|
||||
}
|
||||
|
|
@ -447,17 +524,27 @@ pub fn run_league4(teams: &[String], rng: &mut Rng) -> PreparedSimulation {
|
|||
let mut history = Vec::new();
|
||||
let mut last_stats = Vec::new();
|
||||
|
||||
let mut initial_table: Vec<StandingsRow> = table.values().cloned().collect();
|
||||
initial_table.sort_by(standings_cmp);
|
||||
frames.push(SimFrame {
|
||||
scoreboard: "League created - waiting for Matchday 1".to_string(),
|
||||
logs: vec!["League table initialized".to_string()],
|
||||
stats_lines: None,
|
||||
competition_lines: Some(league_table_lines(&initial_table)),
|
||||
history_append: Vec::new(),
|
||||
});
|
||||
|
||||
for (idx, (home, away)) in fixtures.iter().enumerate() {
|
||||
frames.push(SimFrame {
|
||||
scoreboard: format!("Running League Match {}/{}", idx + 1, fixtures.len()),
|
||||
logs: vec![format!(
|
||||
frames.push(empty_frame(
|
||||
format!("Running League Match {}/{}", idx + 1, fixtures.len()),
|
||||
vec![format!(
|
||||
"League fixture {}/{}: {} vs {}",
|
||||
idx + 1,
|
||||
fixtures.len(),
|
||||
display_name(home),
|
||||
display_name(away)
|
||||
)],
|
||||
});
|
||||
));
|
||||
|
||||
let (res, mut match_frames) = simulate_match(home, away, rng);
|
||||
frames.append(&mut match_frames);
|
||||
|
|
@ -504,6 +591,16 @@ pub fn run_league4(teams: &[String], rng: &mut Rng) -> PreparedSimulation {
|
|||
res.away_goals,
|
||||
display_name(away)
|
||||
));
|
||||
|
||||
let mut snapshot: Vec<StandingsRow> = table.values().cloned().collect();
|
||||
snapshot.sort_by(standings_cmp);
|
||||
frames.push(SimFrame {
|
||||
scoreboard: format!("League table updated after Match {}", idx + 1),
|
||||
logs: vec!["Standings updated".to_string()],
|
||||
stats_lines: Some(last_stats.clone()),
|
||||
competition_lines: Some(league_table_lines(&snapshot)),
|
||||
history_append: vec![history.last().cloned().unwrap_or_default()],
|
||||
});
|
||||
}
|
||||
|
||||
let mut final_table: Vec<StandingsRow> = table.into_values().collect();
|
||||
|
|
@ -515,23 +612,15 @@ pub fn run_league4(teams: &[String], rng: &mut Rng) -> PreparedSimulation {
|
|||
final_table[0].pts
|
||||
));
|
||||
|
||||
let competition = final_table
|
||||
.iter()
|
||||
.map(|r| {
|
||||
format!(
|
||||
"{} | P:{} W:{} D:{} L:{} GF:{} GA:{} GD:{} Pts:{}",
|
||||
display_name(&r.team),
|
||||
r.p,
|
||||
r.w,
|
||||
r.d,
|
||||
r.l,
|
||||
r.gf,
|
||||
r.ga,
|
||||
r.gd,
|
||||
r.pts
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
let competition = league_table_lines(&final_table);
|
||||
|
||||
frames.push(SimFrame {
|
||||
scoreboard: format!("League complete - Champion {}", display_name(&champion)),
|
||||
logs: vec!["League finished".to_string()],
|
||||
stats_lines: Some(last_stats.clone()),
|
||||
competition_lines: Some(competition.clone()),
|
||||
history_append: vec![history.last().cloned().unwrap_or_default()],
|
||||
});
|
||||
|
||||
PreparedSimulation {
|
||||
frames,
|
||||
|
|
@ -539,8 +628,6 @@ pub fn run_league4(teams: &[String], rng: &mut Rng) -> PreparedSimulation {
|
|||
champion,
|
||||
final_table,
|
||||
},
|
||||
stats_lines: last_stats,
|
||||
competition_lines: competition,
|
||||
history_lines: history,
|
||||
}
|
||||
}
|
||||
|
|
@ -553,24 +640,36 @@ pub fn run_knockout4(teams: &[String], rng: &mut Rng) -> PreparedSimulation {
|
|||
let mut winners = Vec::new();
|
||||
let mut history = Vec::new();
|
||||
let mut frames = Vec::new();
|
||||
let mut semi1_line: Option<String> = None;
|
||||
let mut semi2_line: Option<String> = None;
|
||||
|
||||
frames.push(SimFrame {
|
||||
scoreboard: "Knockout bracket initialized".to_string(),
|
||||
logs: vec!["Semi-finals ready".to_string()],
|
||||
stats_lines: None,
|
||||
competition_lines: Some(knockout_bracket_lines(None, None, None, None)),
|
||||
history_append: Vec::new(),
|
||||
});
|
||||
|
||||
for (idx, (home, away)) in semis.iter().enumerate() {
|
||||
frames.push(SimFrame {
|
||||
scoreboard: format!("Running Semi-final {}/2", idx + 1),
|
||||
logs: vec![format!(
|
||||
frames.push(empty_frame(
|
||||
format!("Running Semi-final {}/2", idx + 1),
|
||||
vec![format!(
|
||||
"Semi {}: {} vs {}",
|
||||
idx + 1,
|
||||
display_name(home),
|
||||
display_name(away)
|
||||
)],
|
||||
});
|
||||
));
|
||||
|
||||
let (res, mut semi_frames) = simulate_match(home, away, rng);
|
||||
frames.append(&mut semi_frames);
|
||||
|
||||
let line_text;
|
||||
|
||||
let winner = if res.home_goals == res.away_goals {
|
||||
let (ph, pa, home_wins) = penalties(rng);
|
||||
history.push(format!(
|
||||
line_text = format!(
|
||||
"Semi {}: {} {}-{} {} (pens {}-{})",
|
||||
idx + 1,
|
||||
display_name(home),
|
||||
|
|
@ -579,52 +678,75 @@ pub fn run_knockout4(teams: &[String], rng: &mut Rng) -> PreparedSimulation {
|
|||
display_name(away),
|
||||
ph,
|
||||
pa
|
||||
));
|
||||
);
|
||||
history.push(line_text.clone());
|
||||
if home_wins {
|
||||
home.clone()
|
||||
} else {
|
||||
away.clone()
|
||||
}
|
||||
} else if res.home_goals > res.away_goals {
|
||||
history.push(format!(
|
||||
line_text = format!(
|
||||
"Semi {}: {} {}-{} {}",
|
||||
idx + 1,
|
||||
display_name(home),
|
||||
res.home_goals,
|
||||
res.away_goals,
|
||||
display_name(away)
|
||||
));
|
||||
);
|
||||
history.push(line_text.clone());
|
||||
home.clone()
|
||||
} else {
|
||||
history.push(format!(
|
||||
line_text = format!(
|
||||
"Semi {}: {} {}-{} {}",
|
||||
idx + 1,
|
||||
display_name(home),
|
||||
res.home_goals,
|
||||
res.away_goals,
|
||||
display_name(away)
|
||||
));
|
||||
);
|
||||
history.push(line_text.clone());
|
||||
away.clone()
|
||||
};
|
||||
|
||||
if idx == 0 {
|
||||
semi1_line = Some(line_text.clone());
|
||||
} else {
|
||||
semi2_line = Some(line_text.clone());
|
||||
}
|
||||
|
||||
frames.push(SimFrame {
|
||||
scoreboard: format!("Semi-final {} complete", idx + 1),
|
||||
logs: vec!["Bracket updated".to_string()],
|
||||
stats_lines: Some(match_stats_lines(&res)),
|
||||
competition_lines: Some(knockout_bracket_lines(
|
||||
semi1_line.as_deref(),
|
||||
semi2_line.as_deref(),
|
||||
None,
|
||||
None,
|
||||
)),
|
||||
history_append: vec![line_text],
|
||||
});
|
||||
winners.push(winner);
|
||||
}
|
||||
|
||||
frames.push(SimFrame {
|
||||
scoreboard: "Running Final".to_string(),
|
||||
logs: vec![format!(
|
||||
frames.push(empty_frame(
|
||||
"Running Final".to_string(),
|
||||
vec![format!(
|
||||
"Final: {} vs {}",
|
||||
display_name(&winners[0]),
|
||||
display_name(&winners[1])
|
||||
)],
|
||||
});
|
||||
));
|
||||
|
||||
let (final_res, mut final_frames) = simulate_match(&winners[0], &winners[1], rng);
|
||||
frames.append(&mut final_frames);
|
||||
let last_stats = match_stats_lines(&final_res);
|
||||
|
||||
let final_line;
|
||||
let champion = if final_res.home_goals == final_res.away_goals {
|
||||
let (ph, pa, home_wins) = penalties(rng);
|
||||
history.push(format!(
|
||||
final_line = format!(
|
||||
"Final: {} {}-{} {} (pens {}-{})",
|
||||
display_name(&winners[0]),
|
||||
final_res.home_goals,
|
||||
|
|
@ -632,45 +754,56 @@ pub fn run_knockout4(teams: &[String], rng: &mut Rng) -> PreparedSimulation {
|
|||
display_name(&winners[1]),
|
||||
ph,
|
||||
pa
|
||||
));
|
||||
);
|
||||
history.push(final_line.clone());
|
||||
if home_wins {
|
||||
winners[0].clone()
|
||||
} else {
|
||||
winners[1].clone()
|
||||
}
|
||||
} else if final_res.home_goals > final_res.away_goals {
|
||||
history.push(format!(
|
||||
final_line = format!(
|
||||
"Final: {} {}-{} {}",
|
||||
display_name(&winners[0]),
|
||||
final_res.home_goals,
|
||||
final_res.away_goals,
|
||||
display_name(&winners[1])
|
||||
));
|
||||
);
|
||||
history.push(final_line.clone());
|
||||
winners[0].clone()
|
||||
} else {
|
||||
history.push(format!(
|
||||
final_line = format!(
|
||||
"Final: {} {}-{} {}",
|
||||
display_name(&winners[0]),
|
||||
final_res.home_goals,
|
||||
final_res.away_goals,
|
||||
display_name(&winners[1])
|
||||
));
|
||||
);
|
||||
history.push(final_line.clone());
|
||||
winners[1].clone()
|
||||
};
|
||||
|
||||
history.push(format!("Champion: {} 🏆", display_name(&champion)));
|
||||
let champion_line = format!("Champion: {} 🏆", display_name(&champion));
|
||||
history.push(champion_line.clone());
|
||||
|
||||
frames.push(SimFrame {
|
||||
scoreboard: format!("Knockout complete - {}", display_name(&champion)),
|
||||
logs: vec!["Final complete".to_string()],
|
||||
stats_lines: Some(last_stats.clone()),
|
||||
competition_lines: Some(knockout_bracket_lines(
|
||||
semi1_line.as_deref(),
|
||||
semi2_line.as_deref(),
|
||||
Some(&final_line),
|
||||
Some(&champion_line),
|
||||
)),
|
||||
history_append: vec![final_line.clone(), champion_line.clone()],
|
||||
});
|
||||
|
||||
PreparedSimulation {
|
||||
frames,
|
||||
outcome: SimOutcome::Knockout {
|
||||
champion: champion.clone(),
|
||||
},
|
||||
stats_lines: last_stats,
|
||||
competition_lines: vec![
|
||||
"Bracket: Semi 1 = Team1 vs Team4".to_string(),
|
||||
"Bracket: Semi 2 = Team2 vs Team3".to_string(),
|
||||
format!("Champion: {}", display_name(&champion)),
|
||||
],
|
||||
history_lines: history,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@ pub fn render(f: &mut Frame<'_>, area: Rect, app: &App) {
|
|||
.direction(Direction::Vertical)
|
||||
.constraints([
|
||||
Constraint::Length(3),
|
||||
Constraint::Min(8),
|
||||
Constraint::Length(8),
|
||||
Constraint::Min(6),
|
||||
Constraint::Length(12),
|
||||
])
|
||||
.split(area);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue