use std::sync::{atomic::Ordering, Arc};
use super::SearchState;
use crate::game::{moves::legal_moves, state::GameState};
const LEGAL_WEIGHT: f64 = 0.5;
pub fn run(initial_state: &GameState, search: Arc<SearchState>, width: usize) {
search.reset();
search.running.store(true, Ordering::Relaxed);
let mut beam: Vec<GameState> = vec![initial_state.clone()];
loop {
if !search.running.load(Ordering::Relaxed) {
break;
}
let mut next: Vec<GameState> = Vec::with_capacity(beam.len() * 4);
let mut all_terminal = true;
for state in &beam {
search.nodes_explored.fetch_add(1, Ordering::Relaxed);
let moves = legal_moves(state);
if moves.is_empty() {
let score = state.score() as u32;
search.record_best(score, state.history.clone());
continue;
}
all_terminal = false;
for mv in moves {
let mut child = state.clone();
child.apply(mv);
next.push(child);
}
}
if all_terminal || next.is_empty() {
break;
}
next.sort_by(|a, b| {
let sa = beam_score(a);
let sb = beam_score(b);
sb.partial_cmp(&sa).unwrap_or(std::cmp::Ordering::Equal)
});
next.truncate(width);
beam = next;
}
search.running.store(false, Ordering::Relaxed);
}
fn beam_score(state: &GameState) -> f64 {
let legal = legal_moves(state);
state.score() as f64 + LEGAL_WEIGHT * legal.len() as f64
}
use crate::search::plugin::{Method, OptionKind, OptionSpec, Plugin, Registry, Scope, StartCtx};
struct BeamMethod;
impl Method for BeamMethod {
fn id(&self) -> &'static str {
"beam"
}
fn label_key(&self) -> &'static str {
"algo-beam"
}
fn spawn(&self, ctx: StartCtx, search: Arc<SearchState>) {
let StartCtx { initial, width, .. } = ctx;
std::thread::spawn(move || run(&initial, search, width));
}
fn method_desc(&self, ctx: &StartCtx) -> String {
format!("beam w={}", ctx.width)
}
fn checkpoint_kind(&self) -> Option<&'static str> {
None
}
}
static BEAM: BeamMethod = BeamMethod;
pub struct BeamPlugin;
impl Plugin for BeamPlugin {
fn id(&self) -> &'static str {
"beam"
}
fn register(&self, reg: &mut Registry) {
reg.add_method(&BEAM);
reg.add_option(OptionSpec {
key: "width",
label_key: "opt-width",
help_key: "opt-width-hint",
help: "Beam width (kept candidates per depth).",
kind: OptionKind::Int {
default: 64,
min: 1,
max: 100_000,
},
scope: Scope::Methods(&["beam"]),
});
}
}
pub static BEAM_PLUGIN: BeamPlugin = BeamPlugin;