Spaces:
Sleeping
Sleeping
File size: 6,977 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 | use engine_rust::core::logic::{CardDatabase, GameState};
use engine_rust::core::enums::Phase;
use serde_json::json;
use smallvec::SmallVec;
use engine_rust::core::{ACTION_BASE_PASS};
use std::fs;
fn main() {
// Load vanilla database
let db = load_vanilla_db();
let mut state = GameState::default();
// Build decks from actual cards in database
let mut dummy_deck = Vec::new();
let mut dummy_lives = Vec::new();
// Collect real member cards
for &id in db.members.keys() {
dummy_deck.push(id);
if dummy_deck.len() >= 48 {
break;
}
}
// Pad if needed
while dummy_deck.len() < 48 {
if let Some(&id) = db.members.keys().next() {
dummy_deck.push(id);
} else {
break;
}
}
// Collect real live cards
for &id in db.lives.keys() {
dummy_lives.push(id);
if dummy_lives.len() >= 12 {
break;
}
}
// Pad if needed
while dummy_lives.len() < 12 {
if let Some(&id) = db.lives.keys().next() {
dummy_lives.push(id);
} else {
break;
}
}
// Collect energy cards
let dummy_energy: Vec<i32> = db.energy_db.keys().take(12).cloned().collect();
state.initialize_game(
dummy_deck.clone(),
dummy_deck.clone(),
dummy_energy.clone(),
dummy_energy.clone(),
dummy_lives.clone(),
dummy_lives.clone(),
);
// Step through mulligan etc to reach Main phase
while state.phase != Phase::Main {
let legal = state.get_legal_action_ids(&db);
if !legal.is_empty() {
let action = legal[0];
if state.step(&db, action).is_err() {
break;
}
} else {
break;
}
}
// Get initial player to search from
let root_player = state.current_player as usize;
println!("Initial state - Current player: {}", root_player);
let mut test_actions = SmallVec::<[i32; 64]>::new();
state.generate_legal_actions(&db, state.current_player as usize, &mut test_actions);
println!("Legal actions at start: {} actions", test_actions.len());
println!("\nEnumerating ALL legal sequences from root state...");
// Single DFS pass: count all sequences at each depth
let mut stats = SequenceStats::new();
enumerate_sequences(&state, &db, root_player, 0, &mut stats);
// Output results
println!("\n=== SEQUENCE ENUMERATION RESULTS ===");
println!("Total sequences explored (leaf nodes): {}", stats.total_sequences);
println!("\nSequences by depth:");
for (depth, count) in &stats.by_depth {
let ratio = if *depth == 0 {
1.0
} else {
*count as f32 / stats.by_depth.get(&(depth - 1)).copied().unwrap_or(1) as f32
};
println!(" Depth {}: {} sequences (branching ratio from prev: {:.2}x)", depth, count, ratio);
}
// Compute average branching factor
if stats.by_depth.len() >= 2 {
let mut total_ratio = 0.0;
let mut count = 0;
for (depth, seq_count) in &stats.by_depth {
if *depth > 0 {
let prev_count = stats.by_depth.get(&(depth - 1)).copied().unwrap_or(1);
if prev_count > 0 {
total_ratio += *seq_count as f32 / prev_count as f32;
count += 1;
}
}
}
if count > 0 {
let avg_branching = total_ratio / count as f32;
println!("\nAverage branching factor (seq_ratio): {:.2}x", avg_branching);
}
}
// JSON output
let json_stats = json!({
"total_sequences": stats.total_sequences,
"max_depth_explored": stats.by_depth.keys().max().copied().unwrap_or(0),
"by_depth": stats.by_depth,
});
println!("\nJSON output:");
println!("{}", serde_json::to_string_pretty(&json_stats).unwrap());
}
#[derive(Default)]
struct SequenceStats {
total_sequences: usize,
by_depth: std::collections::BTreeMap<usize, usize>,
}
impl SequenceStats {
fn new() -> Self {
Self::default()
}
fn record_sequence(&mut self, depth: usize) {
self.total_sequences += 1;
*self.by_depth.entry(depth).or_insert(0) += 1;
}
}
/// Pure DFS: count all legal sequences without evaluation or pruning
fn enumerate_sequences(
state: &GameState,
db: &CardDatabase,
root_player: usize,
depth: usize,
stats: &mut SequenceStats,
) {
if depth == 0 {
println!("DEBUG: Entering with depth=0");
}
// Base case: hard depth limit
if depth > 15 {
stats.record_sequence(depth);
return;
}
// Check if current player HAS legal Main actions (excluding Pass means no real moves)
let mut actions = SmallVec::<[i32; 64]>::new();
state.generate_legal_actions(db, state.current_player as usize, &mut actions);
if depth == 0 {
println!("DEBUG: Total actions = {}", actions.len());
}
// Filter out Pass - if only Pass is legal, turn ends
let main_actions: Vec<i32> = actions.into_iter()
.filter(|&a| a != ACTION_BASE_PASS)
.collect();
if depth == 0 {
println!("DEBUG: Non-Pass actions = {}", main_actions.len());
}
// If no real Main phase actions, sequence ends here
if main_actions.is_empty() || state.phase != Phase::Main {
if depth == 0 {
println!("DEBUG: Hitting base case - main_actions.is_empty()={}, phase check would be needed", main_actions.is_empty());
}
stats.record_sequence(depth);
return;
}
if depth == 0 {
println!("DEBUG: Will recurse into {} actions", main_actions.len());
}
// Recursive case: try all non-Pass legal actions
for action in main_actions {
let mut next_state = state.clone();
if next_state.step(db, action).is_ok() {
// Continue exploring
enumerate_sequences(&next_state, db, root_player, depth + 1, stats);
}
}
}
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");
} |