File size: 10,591 Bytes
463f868
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9bd4ce5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
use engine_rust::core::analysis::performance_solver::{
    AbilityAdjustments, PerformanceProbabilitySolver,
};
use engine_rust::core::logic::{GameState, CardDatabase};
use engine_rust::test_helpers::load_real_db;

fn parse_deck(path: &str, db: &CardDatabase) -> Vec<i32> {
    let content = std::fs::read_to_string(path).expect("Failed to read deck file");
    let mut ids = Vec::new();
    for line in content.lines() {
        let line = line.trim();
        if line.is_empty() || line.starts_with('#') {
            continue;
        }
        // Format: NO x COUNT or just NO
        let parts: Vec<&str> = line.split('x').map(|s| s.trim()).collect();
        let no = parts[0];
        let count = if parts.len() > 1 {
            parts[1].parse::<usize>().unwrap_or(1)
        } else {
            1
        };

        if let Some(&id) = db.card_no_to_id.get(no) {
            for _ in 0..count {
                ids.push(id);
            }
        } else {
            println!("[WARN] Card not found in DB: {}", no);
        }
    }
    ids
}

fn main() {
    println!("Loading Card Database...");
    let db = load_real_db();
    println!("DB Loaded successfully.");

    let p_idx = 0;
    let mut state = GameState::default();

    // Load Real AI Deck
    let deck_path = if std::path::Path::new("ai/decks/muse_cup.txt").exists() {
        "ai/decks/muse_cup.txt"
    } else {
        "../ai/decks/muse_cup.txt"
    };
    println!("Loading AI Deck from: {}", deck_path);
    let master_deck = parse_deck(deck_path, &db);

    // Split into main and energy (if applicable, but muse_cup is mostly main)
    let member_ids: Vec<i32> = master_deck
        .iter()
        .filter(|&&id| db.get_member(id).is_some())
        .cloned()
        .collect();
    let live_ids: Vec<i32> = master_deck
        .iter()
        .filter(|&&id| db.get_live(id).is_some())
        .cloned()
        .collect();

    // Base Yells: 10
    state.players[p_idx].cheer_mod_count = 10;

    // Initial Deck for Scenario 1/2
    state.players[p_idx].deck = member_ids.clone().into();

    // Stage: 1 member to provide a base
    if !member_ids.is_empty() {
        state.players[p_idx].stage[0] = member_ids[0];
    }

    // SCENARIO 1: LOW STAGE (1 Weak Member, 5 Yells)
    println!("\n=======================================================");
    println!("           SCENARIO 1: LOW STAGE, LOW YELLS            ");
    println!("     (1 Basic Member, 0 Blades, 5 Yells / Deck: 10)    ");
    println!("=======================================================\n");

    for &live_id in &live_ids {
        let live_card = db.get_live(live_id).unwrap();
        println!(
            ">>> Evaluating Live Card: {} (Base Score: {}, Hearts Required: {:?})",
            live_card.name, live_card.score, live_card.required_hearts
        );

        let mut state = GameState::default();
        let p_idx = 0;

        // Stage has 1 member
        state.players[p_idx].stage[0] = member_ids[0]; // Eli
                                                            // Deck
        state.players[p_idx]
            .deck
            .extend_from_slice(&member_ids);

        let chance =
            PerformanceProbabilitySolver::calculate_win_chance(&state, &db, p_idx, live_id);

        println!(
            "  - Expected Hearts across {} Yells: {:.2?}",
            chance.k_yells, chance.expected_hearts
        );
        println!("  - Expected Score: {:.2}", chance.expected_score);
        println!(
            "  - Win Probability: {:.2}%",
            chance.success_probability * 100.0
        );
        println!("-------------------------------------------------------");
    }

    // SCENARIO 2: HIGH STAGE (3 Strong Members, 20 Yells, 3 Blades)
    println!("\n=======================================================");
    println!("           SCENARIO 2: HIGH STAGE, HIGH YELLS           ");
    println!("    (3 Strong Members, 3 Blades, 15 Yells / Deck: 10)   ");
    println!("=======================================================\n");

    for &live_id in &live_ids {
        let live_card = db.get_live(live_id).unwrap();
        println!(
            ">>> Evaluating Live Card: {} (Base Score: {}, Hearts Required: {:?})",
            live_card.name, live_card.score, live_card.required_hearts
        );

        let mut state = GameState::default();
        let p_idx = 0;

        // Stage has 3 members
        if member_ids.len() >= 3 {
            state.players[p_idx].stage[0] = member_ids[1];
            state.players[p_idx].stage[1] = member_ids[2];
            state.players[p_idx].stage[2] = member_ids[3];
        }

        // Let's add blades to these members (simulating +1 blade per member)
        state.players[p_idx].blade_buffs[0] += 1;
        state.players[p_idx].blade_buffs[1] += 1;
        state.players[p_idx].blade_buffs[2] += 1;

        // 15 Yells
        state.players[p_idx].cheer_mod_count = 15;
        // Deck
        state.players[p_idx]
            .deck
            .extend_from_slice(&member_ids);

        let chance =
            PerformanceProbabilitySolver::calculate_win_chance(&state, &db, p_idx, live_id);

        println!(
            "  - Expected Hearts across {} Yells: {:.2?}",
            chance.k_yells, chance.expected_hearts
        );
        println!(
            "  - Expected Score: {:.2} (Note: Volume Icons give bonus!)",
            chance.expected_score
        );
        println!(
            "  - Win Probability: {:.2}%",
            chance.success_probability * 100.0
        );
        println!("-------------------------------------------------------");
    }

    // SCENARIO 3: HAND EVALUATION (Ability Awareness)
    println!("\n=======================================================");
    println!("           SCENARIO 3: HAND EVALUATION                 ");
    println!("    (Comparing cards in hand to boost a Live)          ");
    println!("=======================================================\n");

    let live_id = live_ids[0]; // SENTIMENTAL StepS (Score 2)
    let live_card = db.get_live(live_id).unwrap();
    println!(
        ">>> Target Live: {} (Requires: {:?})",
        live_card.name, live_card.required_hearts
    );

    let mut state = GameState::default();
    state.current_player = 0;
    let p_idx = 0;
    // state.players[p_idx].energy = 10; // DEPRECATED
    // Instead, add 10 dummy energy cards
    for _ in 0..10 {
        state.players[p_idx].energy_zone.push(1);
    }
    // Base Yells: 10
    state.players[p_idx].cheer_mod_count = 10;
    state.players[p_idx]
        .deck
        .extend_from_slice(&member_ids);
    // Stage: 1 member to provide a base
    state.players[p_idx].stage[0] = member_ids[0];

    // Hand setup for Scenario 3:
    // 1. PL!-sd1-002-SD (Eri - Activated, no immediate play boost)
    // 2. PL!HS-PR-019-PR (Ginko - Adds 2 Pink Hearts on play)
    // 3. PL!HS-bp2-008-P (Kosuzu - Adds 2 Blades on play)
    // 4. PL!-pb1-004-R (Umi - Boosts score on play)
    let hand_nos = vec![
        "PL!-sd1-002-SD",
        "PL!HS-PR-019-PR",
        "PL!HS-bp2-008-P",
        "PL!SP-pb1-004-R",
    ];
    let mut hand_ids = Vec::new();
    for no in hand_nos {
        if let Some(&id) = db.card_no_to_id.get(no) {
            hand_ids.push(id);
        }
    }

    state.players[p_idx].hand = hand_ids.clone().into();
    // Add multiple members to success pile/deck to ensure variety and satisfy conditions
    state.players[p_idx]
        .success_lives
        .extend_from_slice(&member_ids);

    // Ensure deck has enough variety to meet live requirements, but keep it readable (unique-ish)
    // We'll take all unique members from the DB to form a truly diverse deck
    let mut diverse_deck: Vec<i32> = db.members.keys().cloned().collect();
    // Shuffle or sort? Let's just use the first 40 unique cards for stability
    diverse_deck.sort();
    diverse_deck.truncate(40);
    state.players[p_idx].deck = diverse_deck.into();

    let evaluations = PerformanceProbabilitySolver::evaluate_hand_contributions(
        &state,
        &db,
        &state.players[p_idx].hand,
        live_card,
    );

    println!(">>> Evaluations Found: {}", evaluations.len());

    for (cid, chance) in evaluations {
        let card = db.get_member(cid).unwrap();
        // Predict specifically for the Center slot (slot 1) for this demonstration
        let _adj = PerformanceProbabilitySolver::predict_adjustments(&state, &db, card, 1);
        println!(
            ">>> Resulting Win Probability: {:.2}%",
            chance.success_probability * 100.0
        );
        println!("  - Expected Score: {:.2}", chance.expected_score);
        println!("-------------------------------------------------------");
    }

    // SCENARIO 4: BATCH LIVE EVALUATION (Success Heatmap)
    println!("\n=======================================================");
    println!("        SCENARIO 4: BATCH LIVE EVALUATION              ");
    println!("    (Win Chance for ALL unique lives in Database)      ");
    println!("=======================================================\n");

    let mut live_map = std::collections::BTreeMap::new();
    let all_live_ids: Vec<i32> = db.lives.keys().cloned().collect();

    for &lid in &all_live_ids {
        let l_card = db.get_live(lid).unwrap();
        let chance = PerformanceProbabilitySolver::calculate_performance_chance(
            &state,
            &db,
            l_card,
            &AbilityAdjustments::default(),
        );

        // Group by name - keep the maximum probability found for this name
        let entry = live_map.entry(l_card.name.clone()).or_insert(0.0f32);
        if chance.success_probability > *entry {
            *entry = chance.success_probability;
        }
    }

    let mut live_results: Vec<_> = live_map.into_iter().collect();

    // Sort by success probability descending
    live_results.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));

    println!("{:<40} | {:<10}", "Live Card Name", "Win Chance");
    println!("------------------------------------------------------------------");
    for (name, prob) in live_results {
        if prob > 0.0 {
            println!("{:<40} | {:>9.2}%", name, prob * 100.0);
        }
    }
}