more sophisticated syncronization in tidepool, enables graceful C-c
This commit is contained in:
parent
d1af51e204
commit
cd73e8c592
|
@ -87,6 +87,16 @@ dependencies = [
|
|||
"os_str_bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctrlc"
|
||||
version = "3.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbcf33c2a618cbe41ee43ae6e9f2e48368cd9f9db2896f10167d8d762679f639"
|
||||
dependencies = [
|
||||
"nix",
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.2.8"
|
||||
|
@ -177,7 +187,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
"windows-sys 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -189,7 +199,7 @@ dependencies = [
|
|||
"hermit-abi",
|
||||
"io-lifetimes",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
"windows-sys 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -256,6 +266,18 @@ dependencies = [
|
|||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.26.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-ansi-term"
|
||||
version = "0.46.0"
|
||||
|
@ -412,7 +434,7 @@ dependencies = [
|
|||
"io-lifetimes",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys",
|
||||
"windows-sys 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -476,6 +498,12 @@ version = "1.10.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
|
@ -518,6 +546,7 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"ctrlc",
|
||||
"fish",
|
||||
"mino",
|
||||
"rand",
|
||||
|
@ -696,46 +725,70 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.42.0"
|
||||
name = "windows-sys"
|
||||
version = "0.45.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
|
||||
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.42.0"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
|
||||
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.42.0"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
|
||||
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.42.0"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
|
||||
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.42.0"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
|
||||
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.42.0"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
|
||||
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.42.0"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
|
||||
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
|
|
|
@ -20,3 +20,4 @@ toml = { version = "0.7" }
|
|||
serde_json = { version = "1.0" }
|
||||
clap = { version = "4.0", features = ["derive"] }
|
||||
anyhow = { version = "1.0" }
|
||||
ctrlc = { version = "3.2" }
|
||||
|
|
|
@ -2,6 +2,8 @@ use anyhow::{Context as _, Result};
|
|||
use rand::RngCore as _;
|
||||
use std::io::{Read, Write};
|
||||
use std::path::Path;
|
||||
use std::sync::{atomic, mpsc, Arc};
|
||||
use std::time::Duration;
|
||||
|
||||
use fish::bot;
|
||||
use tidepool::{cli, config, output, sim};
|
||||
|
@ -18,36 +20,81 @@ fn main() -> Result<()> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Msg {
|
||||
Interrupt,
|
||||
Heartbeat(u64),
|
||||
Status(u64, Box<Status>),
|
||||
Done(u64, Box<output::Output>),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Status {
|
||||
pieces: usize,
|
||||
lines_left: usize,
|
||||
time: Duration,
|
||||
}
|
||||
|
||||
fn create_mailbox() -> (mpsc::SyncSender<Msg>, mpsc::Receiver<Msg>) {
|
||||
let (tx, rx) = mpsc::sync_channel(128);
|
||||
|
||||
let tx2 = tx.clone();
|
||||
let on_ctrlc = move || {
|
||||
if tx2.send(Msg::Interrupt).is_err() {
|
||||
tracing::warn!("nobody handled ctrl-c, aborting");
|
||||
std::process::exit(128 + ctrlc::Signal::SIGINT as i32);
|
||||
}
|
||||
};
|
||||
if let Err(err) = ctrlc::set_handler(on_ctrlc) {
|
||||
tracing::warn!("could not set signal handler: {err}");
|
||||
}
|
||||
|
||||
(tx, rx)
|
||||
}
|
||||
|
||||
fn single(args: cli::SingleRun) -> Result<()> {
|
||||
let config = parse_config_file(&args.config_file)?;
|
||||
|
||||
let output = simulation(&args.io, &args.data, args.seed, config);
|
||||
let (tx, rx) = create_mailbox();
|
||||
let exit_early = Arc::new(atomic::AtomicBool::new(false));
|
||||
|
||||
let mut writer: Box<dyn std::io::Write> = match &args.output_file {
|
||||
Some(path) => {
|
||||
if path.is_file() {
|
||||
if prompt_yn(&args.io, "output file already exists, overwrite?") {
|
||||
tracing::debug!("removing {path:?}");
|
||||
std::fs::remove_file(path)?;
|
||||
} else {
|
||||
tracing::warn!("output file already exists, exiting.");
|
||||
return Ok(());
|
||||
let exit_early2 = exit_early.clone();
|
||||
let config2 = config.clone();
|
||||
std::thread::spawn(move || run_simulation(&args.data, config2, args.seed, 0, tx, exit_early2));
|
||||
|
||||
while let Ok(msg) = rx.recv() {
|
||||
tracing::trace!(msg = debug(&msg));
|
||||
|
||||
match msg {
|
||||
Msg::Interrupt => {
|
||||
eprintln!("\ncaught interrupt.");
|
||||
tracing::debug!("interrupted");
|
||||
exit_early.store(true, atomic::Ordering::Relaxed);
|
||||
}
|
||||
Msg::Heartbeat(_id) => {
|
||||
tracing::debug!("heartbeat");
|
||||
}
|
||||
Msg::Status(_id, status) => {
|
||||
if !args.io.quiet {
|
||||
let ps = status.pieces;
|
||||
let ll = status.lines_left;
|
||||
let pps = ps as f64 / status.time.as_secs_f64();
|
||||
eprintln!("#{ps}, {ll} lines left, {pps:.2} pps");
|
||||
}
|
||||
}
|
||||
Box::new(
|
||||
std::fs::File::create(path)
|
||||
.with_context(|| format!("error opening output file '{}'", path.display()))?,
|
||||
)
|
||||
}
|
||||
None => Box::new(std::io::stdout()),
|
||||
};
|
||||
Msg::Done(_id, output) => {
|
||||
std::mem::drop(rx);
|
||||
|
||||
let pretty = args.output_file.is_none() && !args.io.quiet;
|
||||
if pretty {
|
||||
serde_json::to_writer_pretty(&mut writer, &output).context("error writing output")?;
|
||||
writeln!(&mut writer).context("error writing output")?;
|
||||
} else {
|
||||
serde_json::to_writer(&mut writer, &output).context("error writing output")?;
|
||||
if output.cleared < config.game.goal {
|
||||
if !prompt_yn(&args.io, "run did not finish, keep it?") {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
write_output(&args.io, args.output_file.as_deref(), &output)?;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -67,11 +114,41 @@ fn parse_config_file(path: &Path) -> Result<config::Config> {
|
|||
toml::from_str(&contents).context("error parsing config file")
|
||||
}
|
||||
|
||||
fn prompt_yn(io: &cli::IoArgs, msg: &str) -> bool {
|
||||
if io.noninteractive {
|
||||
return false;
|
||||
fn write_output(io_args: &cli::IoArgs, path: Option<&Path>, output: &output::Output) -> Result<()> {
|
||||
let mut writer: Box<dyn std::io::Write> = match &path {
|
||||
Some(path) => {
|
||||
if path.is_file() {
|
||||
if prompt_yn(io_args, "output file already exists, overwrite?") {
|
||||
tracing::debug!("removing {path:?}");
|
||||
std::fs::remove_file(path)?;
|
||||
} else {
|
||||
tracing::warn!("output file already exists, exiting.");
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
Box::new(
|
||||
std::fs::File::create(path)
|
||||
.with_context(|| format!("error opening output file '{}'", path.display()))?,
|
||||
)
|
||||
}
|
||||
None => Box::new(std::io::stdout()),
|
||||
};
|
||||
|
||||
let pretty = path.is_none() && !io_args.quiet;
|
||||
if pretty {
|
||||
serde_json::to_writer_pretty(&mut writer, &output)
|
||||
.context("error writing output")
|
||||
.and_then(|_| writeln!(&mut writer).context("error writing output"))
|
||||
} else {
|
||||
serde_json::to_writer(&mut writer, &output).context("error writing output")
|
||||
}
|
||||
let mut output = std::io::stdout().lock();
|
||||
}
|
||||
|
||||
fn prompt_yn(io_args: &cli::IoArgs, msg: &str) -> bool {
|
||||
if io_args.noninteractive {
|
||||
return true;
|
||||
}
|
||||
let mut output = std::io::stderr().lock();
|
||||
write!(output, "{msg} [y/N] ").unwrap();
|
||||
output.flush().unwrap();
|
||||
let mut user_input = String::new();
|
||||
|
@ -79,15 +156,15 @@ fn prompt_yn(io: &cli::IoArgs, msg: &str) -> bool {
|
|||
user_input.trim().eq_ignore_ascii_case("y")
|
||||
}
|
||||
|
||||
fn simulation(
|
||||
io: &cli::IoArgs,
|
||||
data: &cli::OutputDataArgs,
|
||||
seed: Option<u64>,
|
||||
fn run_simulation(
|
||||
data_args: &cli::OutputDataArgs,
|
||||
config: config::Config,
|
||||
) -> output::Output {
|
||||
// TODO: status signals
|
||||
|
||||
let mut moves = data.list_moves.then(Vec::new);
|
||||
seed: Option<u64>,
|
||||
id: u64,
|
||||
tx: mpsc::SyncSender<Msg>,
|
||||
exit_early: Arc<atomic::AtomicBool>,
|
||||
) {
|
||||
let mut moves = data_args.list_moves.then(Vec::new);
|
||||
let seed = seed.unwrap_or_else(|| rand::thread_rng().next_u64());
|
||||
|
||||
let sim_opts = sim::Options {
|
||||
|
@ -100,9 +177,8 @@ fn simulation(
|
|||
let time_start = std::time::Instant::now();
|
||||
|
||||
while sim.lines_left() > 0 {
|
||||
// TODO: progress bar
|
||||
if !io.quiet {
|
||||
eprintln!("#{}, {} left", sim.pieces(), sim.lines_left());
|
||||
if exit_early.load(atomic::Ordering::Relaxed) {
|
||||
break;
|
||||
}
|
||||
|
||||
let mut bot = bot::Bot::new(
|
||||
|
@ -112,34 +188,53 @@ fn simulation(
|
|||
|
||||
for i in 0..config.bot.iters {
|
||||
bot.think();
|
||||
if i % 1000 == 999 {
|
||||
tracing::debug!("iteration {i}");
|
||||
|
||||
// periodically check back with the main thread to see if we should exit
|
||||
if i % 4096 == 4095 {
|
||||
if exit_early.load(atomic::Ordering::Relaxed) {
|
||||
break;
|
||||
}
|
||||
if tx.send(Msg::Heartbeat(id)).is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(placement) = bot.suggest() {
|
||||
sim.play(placement);
|
||||
let Some(placement) = bot.suggest() else {
|
||||
tracing::warn!("gave up :( pieces={}, id={id}", sim.pieces());
|
||||
break;
|
||||
};
|
||||
|
||||
if let Some(moves) = moves.as_mut() {
|
||||
moves.push(output::Move { placement })
|
||||
}
|
||||
} else {
|
||||
sim.play(placement);
|
||||
if let Some(moves) = moves.as_mut() {
|
||||
moves.push(output::Move { placement })
|
||||
}
|
||||
|
||||
let status = Status {
|
||||
lines_left: sim.lines_left(),
|
||||
pieces: sim.pieces(),
|
||||
time: std::time::Instant::now() - time_start,
|
||||
};
|
||||
if tx.send(Msg::Status(id, status.into())).is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let time_end = std::time::Instant::now();
|
||||
|
||||
let profile = data.profile.then(|| output::Profile {
|
||||
let profile = data_args.profile.then(|| output::Profile {
|
||||
time: time_end - time_start,
|
||||
});
|
||||
|
||||
output::Output {
|
||||
let output = output::Output {
|
||||
seed,
|
||||
cleared: config.game.goal - sim.lines_left(),
|
||||
pieces: sim.pieces(),
|
||||
config: config.game,
|
||||
moves,
|
||||
profile,
|
||||
};
|
||||
if tx.send(Msg::Done(id, output.into())).is_err() {
|
||||
tracing::debug!("nobody received output id={id}");
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue