File size: 5,392 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
use std::fs;
use engine_rust::core::logic::{GameState, CardDatabase};
use engine_rust::core::enums::Phase;

fn load_vanilla_db() -> CardDatabase {
    let candidates = [
        "data/cards_vanilla.json",
        "../data/cards_vanilla.json",
        "../../data/cards_vanilla.json",
    ];

    for path in &candidates {
        if !std::path::Path::new(path).exists() {
            continue;
        }
        let json = fs::read_to_string(path).expect("Failed to read DB");
        let mut db = CardDatabase::from_json(&json).expect("Failed to parse DB");
        db.is_vanilla = true;
        return db;
    }
    panic!("cards_vanilla.json not found");
}

fn load_deck(path: &str, db: &CardDatabase) -> (Vec<i32>, Vec<i32>) {
    let content = fs::read_to_string(path).expect("Failed to read deck");
    let mut members = Vec::new();
    let mut lives = Vec::new();

    for line in content.lines() {
        let line = line.trim();
        if line.is_empty() || line.starts_with('#') {
            continue;
        }

        let card_no = line;
        if let Some(id) = db.id_by_no(card_no) {
            if db.lives.contains_key(&id) {
                lives.push(id);
            } else {
                members.push(id);
            }
        }
    }

    while members.len() < 48 {
        if let Some(&id) = db.members.keys().next() {
            members.push(id);
        } else {
            break;
        }
    }
    while lives.len() < 12 {
        if let Some(&id) = db.lives.keys().next() {
            lives.push(id);
        } else {
            break;
        }
    }

    members.truncate(48);
    lives.truncate(12);

    (members, lives)
}

fn main() {
    let db = load_vanilla_db();
    let deck_path = if std::path::Path::new("ai/decks/liella_cup.txt").exists() {
        "ai/decks/liella_cup.txt"
    } else {
        "../ai/decks/liella_cup.txt"
    };

    let (p0_members, p0_lives) = load_deck(deck_path, &db);
    let energy: Vec<i32> = db.energy_db.keys().take(12).cloned().collect();

    let mut state = GameState::default();
    state.initialize_game(
        p0_members.clone(),
        p0_members.clone(),
        energy.clone(),
        energy.clone(),
        p0_lives.clone(),
        p0_lives.clone(),
    );
    state.ui.silent = true;

    // Advance to first Main phase
    while state.phase != Phase::Main && !state.is_terminal() {
        state.auto_step(&db);
    }

    println!("\n╔═══════════════════════════════════════════════════╗");
    println!("β•‘  Move Sequence Enumeration & State Explosion   β•‘");
    println!("β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•\n");

    let legal_root = state.get_legal_action_ids(&db);
    
    println!("Initial State:");
    println!("  Current player: P{}", state.current_player);
    println!("  Phase: {:?}", state.phase);
    println!("  Hand size: {}", state.players[0].hand.len());
    println!("  Stage: {} cards", state.players[0].stage.iter().filter(|&&c| c >= 0).count());
    println!("  Legal actions: {}\n", legal_root.len());

    // Theoretical analysis
    println!("Move Space Analysis:\n");
    println!("Depth | Branching | Est. Tree Size | Est. Compressed (AB)");
    println!("────────────────────────────────────────────────────────");

    let branching = legal_root.len() as f64;
    for depth in 1..=10 {
        let tree_size = branching.powi(depth as i32);
        // Alpha-beta roughly reduces by factor of sqrt(branching)
        let compressed = (branching.sqrt()).powi(depth as i32);
        
        println!(
            "{:>5} | {:>9.1} | {:>14.0} | {:>19.0}",
            depth, branching, tree_size, compressed
        );
    }

    println!("\n╔═══════════════════════════════════════════════════╗");
    println!("β•‘  Key Insight: Equivalent Moves                  β•‘");
    println!("β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•\n");

    println!("If all 3 stage slots are empty:");
    println!("  Placing card A then B then C");
    println!("  Placing card C then B then A");
    println!("  Placing card B then A then C");
    println!("  β†’ These explore 3! = 6 sequences");
    println!("  β†’ But lead to SAME board state if no abilities trigger\n");

    println!("With abilities that trigger on placement:");
    println!("  Order MATTERS because effects can change what's legal next\n");

    println!("Conclusion:");
    println!("  β€’ Legal branching factor: ~6-8 actions per state (realistic)");
    println!("  β€’ Depth N explores 6^N states without pruning");
    println!("  β€’ Move ordering + pruning: reduces to ~6^(N/3) effective nodes");
    println!("  β€’ Alpha-beta effectiveness depends on ability-triggered order changes\n");
}