use captchaforge::solver::bandit::Bandit;
use captchaforge::solver::{CaptchaType, SolveMethod};
use rand::{rngs::StdRng, Rng, SeedableRng};
fn main() {
let true_p = [0.9, 0.6, 0.4, 0.2, 0.1];
let methods = [
SolveMethod::AutoPass,
SolveMethod::BehavioralBypass,
SolveMethod::VisionLLM,
SolveMethod::AudioBypass,
SolveMethod::CrowdSourced,
];
let bandit = Bandit::new();
let ct = CaptchaType::CloudflareTurnstile;
let mut rng = StdRng::seed_from_u64(42);
let mut counts = [0usize; 5];
println!("Simulating 1000 rounds...");
for round in 0..1000 {
let chosen = bandit.choose(&ct, &methods).unwrap();
let idx = methods.iter().position(|m| m == &chosen).unwrap();
counts[idx] += 1;
let succeeded: bool = rng.gen_bool(true_p[idx]);
bandit.observe(&ct, &chosen, succeeded);
if round == 99 || round == 499 || round == 999 {
println!(" after {} rounds: counts={:?}", round + 1, counts);
}
}
println!();
println!("FINAL POSTERIOR");
println!("─────────────────────────────────────────────────────");
for arm in bandit.snapshot() {
println!(
" {:>20} α={:>5.1} β={:>5.1} mean={:.3} variance={:.5}",
arm.solver_method,
arm.alpha,
arm.beta,
arm.posterior_mean(),
arm.posterior_variance(),
);
}
println!();
println!("SELECTION FREQUENCY");
println!("─────────────────────────────────────────────────────");
for (i, m) in methods.iter().enumerate() {
println!(
" {:?}: {} pulls ({:.1}%) true_p={:.2}",
m,
counts[i],
100.0 * (counts[i] as f64) / 1000.0,
true_p[i]
);
}
}