use std::{ env, net::TcpStream, path::{Path, PathBuf}, process::{Child, Command}, thread::sleep, time::Duration, }; type DynError = Box; fn main() { if let Err(e) = try_main() { eprintln!("{e}"); std::process::exit(-1) } } fn try_main() -> Result<(), DynError> { let task = env::args().nth(1); match task.as_deref() { Some("local") => local()?, Some("local2") => local2()?, _ => print_help(), } Ok(()) } fn print_help() { eprintln!( "Tasks: local runs the snake on a local game local2 runs the snake twice on a local game " ) } fn local() -> Result<(), DynError> { let mut snake = run_snake(8000)?; let game = Command::new("./battlesnake-cli") .current_dir(project_root()) .args([ "play", "-W", "11", "-H", "11", "--name", "local test", "--url", "http://localhost:8000", "-g", "solo", "--browser", ]) .status(); game.and(snake.kill())?; Ok(()) } fn local2() -> Result<(), DynError> { let mut snake1 = run_snake(8000)?; let mut snake2 = match run_snake(8001) { Ok(snake) => snake, Err(e) => { let _ = snake1.kill(); return Err(e); } }; let game = Command::new("./battlesnake-cli") .current_dir(project_root()) .args([ "play", "-W", "11", "-H", "11", "--name", "local test1", "--url", "http://localhost:8000", "--name", "local test2", "--url", "http://localhost:8001", "-g", "duel", "--browser", ]) .status(); game.and(snake1.kill()).and(snake2.kill())?; Ok(()) } fn run_snake(port: u16) -> Result { let cargo = env::var("CARGO").unwrap_or_else(|_| "cargo".to_string()); let mut snake = Command::new(cargo) .current_dir(project_root()) .env("PORT", port.to_string()) .args( ["run", "--bin", "battlesnake"] .map(str::to_string) .into_iter() .chain(env::args().skip(2)), ) .spawn()?; loop { if let Some(status) = snake.try_wait()? { Err(format!("{status}"))?; } if TcpStream::connect(("127.0.0.1", port)).is_ok() { break Ok(snake); } sleep(Duration::from_secs(1)); } } fn project_root() -> PathBuf { Path::new(&env!("CARGO_MANIFEST_DIR")) .ancestors() .nth(1) .unwrap() .to_path_buf() }