@@ -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
122139pub 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}
0 commit comments