Skip to content

Commit 5c83e08

Browse files
committed
Agregar métricas de victorias por partida y empates en la simulación, y actualizar la salida de resumen en la UI.
1 parent 7a808a5 commit 5c83e08

File tree

5 files changed

+220
-115
lines changed

5 files changed

+220
-115
lines changed

src/auto.rs

Lines changed: 141 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ pub struct Summary {
88
pub reps: u32,
99
pub num_players: usize,
1010
pub wins: Vec<u32>,
11+
pub wins_per_game: Vec<u32>,
12+
pub per_game_ties: u32,
1113
pub busts: Vec<u32>,
1214
pub total_points: Vec<u64>,
1315
pub ties: u32,
@@ -35,9 +37,13 @@ pub(crate) fn simulate(reps: u32, num_players: usize, strategies: Vec<Strategy>)
3537
strat_labels.push(strategy_label(&strat_vec[i]));
3638
}
3739

40+
// Only per-partida metrics: wins_per_game counts sole victories; per_game_ties counts games with multiple winners
3841
let mut wins = vec![0u32; num_players];
42+
let mut wins_per_game = vec![0u32; num_players];
43+
let mut per_game_ties = 0u32;
3944
let mut busts = vec![0u32; num_players];
4045
let mut total_points = vec![0u64; num_players];
46+
// `ties` (per-enfrentamiento) is deprecated in this mode; keep for compatibility but will remain 0
4147
let mut ties = 0u32;
4248

4349
for _ in 0..reps {
@@ -47,11 +53,7 @@ pub(crate) fn simulate(reps: u32, num_players: usize, strategies: Vec<Strategy>)
4753
let mut jugadores: Vec<Jugador> = (0..num_players)
4854
.map(|i| {
4955
let mut j = Jugador::nuevo();
50-
j.nombre = if i == num_players - 1 {
51-
"Banca".to_string()
52-
} else {
53-
format!("Jugador {}", i + 1)
54-
};
56+
j.nombre = format!("P{}", i + 1);
5557
j
5658
})
5759
.collect();
@@ -82,36 +84,51 @@ pub(crate) fn simulate(reps: u32, num_players: usize, strategies: Vec<Strategy>)
8284
}
8385
}
8486

85-
// evaluate
86-
let bank_idx = num_players - 1;
87-
let bank_points = jugadores[bank_idx].puntaje();
88-
total_points[bank_idx] += bank_points as u64;
89-
if bank_points > 21 {
90-
busts[bank_idx] += 1;
87+
// evaluate: all-vs-all pairwise comparisons
88+
// total_points and bust counters per player
89+
for i in 0..num_players {
90+
let pts = jugadores[i].puntaje();
91+
total_points[i] += pts as u64;
92+
if pts > 21 {
93+
busts[i] += 1;
94+
}
9195
}
9296

93-
for i in 0..num_players - 1 {
94-
let p_points = jugadores[i].puntaje();
95-
total_points[i] += p_points as u64;
96-
if p_points > 21 {
97-
busts[i] += 1;
98-
wins[bank_idx] += 1;
99-
} else if bank_points > 21 {
100-
wins[i] += 1;
101-
} else if p_points > bank_points {
102-
wins[i] += 1;
103-
} else if bank_points > p_points {
104-
wins[bank_idx] += 1;
97+
// per-game outcome only (no enfrentamientos): determine top scorers ≤21
98+
let mut best_pts: i32 = -1;
99+
for i in 0..num_players {
100+
let pts = jugadores[i].puntaje() as i32;
101+
if pts <= 21 && pts > best_pts {
102+
best_pts = pts;
103+
}
104+
}
105+
if best_pts >= 0 {
106+
// collect winners (could be multiple -> tie)
107+
let mut winners: Vec<usize> = Vec::new();
108+
for i in 0..num_players {
109+
if jugadores[i].puntaje() as i32 == best_pts {
110+
winners.push(i);
111+
}
112+
}
113+
if winners.len() == 1 {
114+
wins_per_game[winners[0]] += 1;
115+
wins[winners[0]] += 1; // keep wins aligned with per-game wins
105116
} else {
106-
ties += 1;
117+
// multiple winners -> count as per-game tie
118+
per_game_ties += 1;
107119
}
120+
} else {
121+
// all busted -> count as tied game
122+
per_game_ties += 1;
108123
}
109124
}
110125

111126
Summary {
112127
reps,
113128
num_players,
114129
wins,
130+
wins_per_game,
131+
per_game_ties,
115132
busts,
116133
total_points,
117134
ties,
@@ -120,20 +137,107 @@ pub(crate) fn simulate(reps: u32, num_players: usize, strategies: Vec<Strategy>)
120137
}
121138

122139
pub fn print_summary(summary: &Summary) {
123-
println!("Resumen final después de {} partidas:", summary.reps);
140+
for line in summary_lines(summary) {
141+
println!("{}", line);
142+
}
143+
}
144+
145+
pub fn summary_lines(summary: &Summary) -> Vec<String> {
146+
let mut lines: Vec<String> = Vec::new();
147+
lines.push(format!(
148+
"Resumen final después de {} partidas:",
149+
summary.reps
150+
));
151+
lines.push(String::new());
152+
153+
// Table header: per-partida victories and percentage
154+
lines.push(format!(
155+
"{:<3} {:<30} {:>10} {:>8} {:>10}",
156+
"ID", "Estrategia", "Vict(part)", "%P", "PtsAvg"
157+
));
158+
lines.push("-".repeat(70));
159+
124160
for i in 0..summary.num_players {
125-
println!(
126-
"{} | {} | Victorias: {} | Busts: {} | Puntos avg: {:.2}",
127-
if i == summary.num_players - 1 {
128-
"Banca".to_string()
129-
} else {
130-
format!("Jugador {}", i + 1)
131-
},
132-
summary.strat_labels.get(i).cloned().unwrap_or_default(),
133-
summary.wins[i],
134-
summary.busts[i],
161+
let id = format!("{}", i + 1);
162+
let strat = summary.strat_labels.get(i).cloned().unwrap_or_default();
163+
let vict_part = summary.wins_per_game[i];
164+
let pct = if summary.reps > 0 {
165+
(vict_part as f64) / (summary.reps as f64) * 100.0
166+
} else {
167+
0.0
168+
};
169+
let pts_avg = if summary.reps > 0 {
135170
(summary.total_points[i] as f64) / (summary.reps as f64)
136-
);
171+
} else {
172+
0.0
173+
};
174+
lines.push(format!(
175+
"{:<3} {:<30} {:>10} {:>7.2} {:>10.2}",
176+
id, strat, vict_part, pct, pts_avg
177+
));
178+
}
179+
180+
lines.push(String::new());
181+
lines.push(format!(
182+
"Partidas empatadas (múltiples ganadores): {}",
183+
summary.per_game_ties
184+
));
185+
186+
// Determine best strategies by metric
187+
let mut best_by_partida = 0usize;
188+
let mut best_by_partida_pct = -1.0f64;
189+
let mut best_by_enf = 0usize;
190+
let mut best_by_enf_pct = -1.0f64;
191+
for i in 0..summary.num_players {
192+
let pct_partida = if summary.reps > 0 {
193+
(summary.wins_per_game[i] as f64) / (summary.reps as f64) * 100.0
194+
} else {
195+
0.0
196+
};
197+
if pct_partida > best_by_partida_pct {
198+
best_by_partida_pct = pct_partida;
199+
best_by_partida = i;
200+
}
201+
// per-opponent percentage: each player has (num_players-1) opponents per game
202+
let denom = (summary.reps as f64) * ((summary.num_players - 1) as f64);
203+
let pct_enf = if denom > 0.0 {
204+
(summary.wins[i] as f64) / denom * 100.0
205+
} else {
206+
0.0
207+
};
208+
if pct_enf > best_by_enf_pct {
209+
best_by_enf_pct = pct_enf;
210+
best_by_enf = i;
211+
}
137212
}
138-
println!("Empates totales: {}", summary.ties);
213+
214+
let label_partida = &summary.strat_labels[best_by_partida];
215+
let label_enf = &summary.strat_labels[best_by_enf];
216+
lines.push(String::new());
217+
lines.push(format!(
218+
"Mejor por partidas: {} ({:.2}%)",
219+
label_partida, best_by_partida_pct
220+
));
221+
lines.push(format!(
222+
"Mejor por enfrentamientos: {} ({:.2}%)",
223+
label_enf, best_by_enf_pct
224+
));
225+
if best_by_partida == best_by_enf {
226+
lines.push(format!(
227+
"Conclusión: estrategia consistente — '{}' domina ambas métricas.",
228+
label_partida
229+
));
230+
} else {
231+
lines.push(format!("Conclusión:"));
232+
lines.push(format!(
233+
" - Si te interesa ganar partidas completas (mayoría en cada partida) prioriza '{}' ({:.2}%).",
234+
label_partida, best_by_partida_pct
235+
));
236+
lines.push(format!(
237+
" - Si prefieres maximizar victorias por enfrentamiento (cada mano contra la banca) prioriza '{}' ({:.2}%).",
238+
label_enf, best_by_enf_pct
239+
));
240+
}
241+
242+
lines
139243
}

src/cli.rs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -123,12 +123,20 @@ pub fn parse_args() -> Config {
123123
}
124124

125125
pub fn print_help() {
126-
println!("RatJack - blackjack simulator/ui\n");
127-
println!("Usage:");
128-
println!(" --auto-ui <reps> <players> [--strategies \"s1,s2\"] Run automated simulations inside the UI");
129-
println!(" --ui [--ui-strategies \"s1,s2\"] Start interactive UI");
130-
println!(" -h, --help Show this help");
131-
println!("\nStrategies format examples: 'threshold:16,prob:0.35,random:0.5'\n");
126+
println!("RatJack - simulador/UI de blackjack\n");
127+
println!("Uso:");
128+
println!(" --auto-ui <reps> <players> [--strategies \"s1,s2\"] Ejecuta simulaciones automáticas dentro de la UI");
129+
println!(" --ui [--ui-strategies \"s1,s2\"] Inicia la UI interactiva");
130+
println!(" -h, --help Muestra esta ayuda");
131+
println!("\nFormato de estrategias y ejemplos: 'threshold:16,prob:0.35,random:0.5,count:hi-lo'\n");
132+
133+
println!("Estrategias disponibles (formato clave:valor):");
134+
println!(" threshold:<N> - Planta (stand) si el total de la mano >= N; pide (hit) en caso contrario.");
135+
println!(" prob:<P> - En cada decisión, planta con probabilidad P (0.0 - 1.0).");
136+
println!(" random:<P> - Similar a prob: decisión aleatoria de plantarse con probabilidad P.");
137+
println!(" count:<N> - Usa conteo de cartas (p. ej. 'hi-lo') para ajustar decisiones según el conteo.");
138+
println!("\nNotas:");
139+
println!(" - Las estrategias se combinan en una lista separada por comas, p. ej. --strategies \"threshold:16,random:0.5\".");
132140
}
133141

134142
#[cfg(test)]

src/strategies.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ pub fn strategy_label(strategy: &Strategy) -> String {
176176
Strategy::Threshold(n) => format!("Threshold({})", n),
177177
Strategy::Random(p) => format!("Random({:.2})", p),
178178
Strategy::Prob(p) => format!("Prob({:.2})", p),
179-
Strategy::Count { base } => format!("Count(base={})", base),
179+
Strategy::Count { base } => format!("Count({})", base),
180180
}
181181
}
182182

0 commit comments

Comments
 (0)