1#![cfg_attr(feature = "clippy", feature(plugin))]
53#![cfg_attr(feature = "clippy", plugin(clippy))]
54
55extern crate ascii;
56#[macro_use]
57extern crate bitflags;
58#[macro_use]
59extern crate slog;
60extern crate sloggers;
61extern crate termion;
62extern crate vte;
63
64mod term_data;
65
66pub use ascii::AsciiChar;
68pub use sloggers::types::Severity;
69use termion::async_stdin;
70use termion::raw::IntoRawMode;
71use vte::Parser;
72
73use term_data::TermData;
74use std::error::Error;
75use std::fmt::{self, Debug, Formatter};
76use std::io;
77use std::process::{Child, Command, Stdio};
78use std::env;
79use std::io::{BufReader, Read, Write};
80use std::str;
81use std::sync::{Arc, Mutex};
82use std::sync::atomic::{AtomicBool, Ordering};
83use std::sync::mpsc::{self, Receiver, Sender};
84use std::thread::{self, JoinHandle};
85use std::time::Duration;
86
87#[derive(Clone, Debug)]
88struct LogInfo {
89 fname: String,
90 sev: Severity,
91}
92
93impl Default for LogInfo {
94 fn default() -> LogInfo {
95 LogInfo {
96 fname: String::new(),
97 sev: Severity::Debug,
98 }
99 }
100}
101
102#[derive(Copy, Clone, Debug)]
103enum DrawType {
104 Terminal(Duration),
105 Null,
106}
107
108#[derive(Clone, Debug)]
132pub struct GameSetting<'a> {
133 cmdname: String,
134 lines: usize,
135 columns: usize,
136 envs: Vec<(&'a str, &'a str)>,
137 args: Vec<&'a str>,
138 log_info: LogInfo,
139 timeout: Duration,
140 draw_type: DrawType,
141 max_loop: usize,
142}
143impl<'a> GameSetting<'a> {
144 pub fn new(command_name: &str) -> Self {
146 GameSetting {
147 cmdname: String::from(command_name),
148 lines: 24,
149 columns: 80,
150 envs: Vec::new(),
151 args: Vec::new(),
152 log_info: LogInfo::default(),
153 timeout: Duration::from_millis(100),
154 draw_type: DrawType::Null,
155 max_loop: 100,
156 }
157 }
158 pub fn columns(mut self, u: usize) -> Self {
160 self.columns = u;
161 self
162 }
163 pub fn lines(mut self, u: usize) -> Self {
165 self.lines = u;
166 self
167 }
168 pub fn arg(mut self, s: &'a str) -> Self {
170 self.args.push(s);
171 self
172 }
173 pub fn env(mut self, s: &'a str, t: &'a str) -> Self {
175 self.envs.push((s, t));
176 self
177 }
178 pub fn args<I>(mut self, i: I) -> Self
180 where
181 I: IntoIterator<Item = &'a str>,
182 {
183 let v: Vec<_> = i.into_iter().collect();
184 self.args = v;
185 self
186 }
187 pub fn envs<I>(mut self, i: I) -> Self
189 where
190 I: IntoIterator<Item = (&'a str, &'a str)>,
191 {
192 let v: Vec<_> = i.into_iter().collect();
193 self.envs = v;
194 self
195 }
196 pub fn draw_on(mut self, d: Duration) -> Self {
199 self.draw_type = DrawType::Terminal(d);
200 self
201 }
202 pub fn debug_file(mut self, s: &str) -> Self {
205 self.log_info.fname = s.to_owned();
206 self
207 }
208 pub fn debug_level(mut self, s: Severity) -> Self {
211 self.log_info.sev = s;
212 self
213 }
214 pub fn timeout(mut self, d: Duration) -> Self {
217 self.timeout = d;
218 self
219 }
220 pub fn max_loop(mut self, t: usize) -> Self {
223 self.max_loop = t;
224 self
225 }
226 pub fn build(self) -> GameEnv {
228 let dat = TermData::from_setting(&self);
229 let t = self.timeout;
230 let m = self.max_loop;
231 let d = self.draw_type;
232 GameEnv {
233 process: ProcHandler::from_setting(self),
234 term_data: dat,
235 timeout: t,
236 max_loop: m,
237 draw_type: d,
238 }
239 }
240}
241
242#[derive(Clone)]
245pub enum ActionResult {
246 Changed(Vec<Vec<u8>>),
247 NotChanged,
248 GameEnded,
249}
250impl Debug for ActionResult {
251 fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
252 match *self {
253 ActionResult::Changed(ref buf) => {
254 write!(f, "ActionResult::Changed\n")?;
255 write!(f, "--------------------\n")?;
256 for v in buf {
257 let s = str::from_utf8(v).unwrap();
258 write!(f, "{}\n", s)?;
259 }
260 write!(f, "--------------------")
261 }
262 ActionResult::NotChanged => write!(f, "ActionResult::NotChanged"),
263 ActionResult::GameEnded => write!(f, "ActionResult::GameEnded"),
264 }
265 }
266}
267
268pub trait Reactor {
270 fn action(&mut self, action_result: ActionResult, turn: usize) -> Option<Vec<u8>>;
271}
272
273pub struct GameEnv {
303 process: ProcHandler,
304 term_data: TermData,
305 timeout: Duration,
306 max_loop: usize,
307 draw_type: DrawType,
308}
309impl GameEnv {
310 pub fn play<R: Reactor>(mut self, ai: &mut R) {
313 use mpsc::RecvTimeoutError;
314 macro_rules! send_or {
315 ($to:expr, $handle:expr) => (
316 if let Err(why) = $to.send_bytes($handle) {
317 debug!(
318 self.term_data.logger,
319 concat!("can't send to ", stringify!($to), ": {}"),
320 why.description()
321 );
322 }
323 )
324 }
325 let proc_handle = self.process.run();
326 let mut viewer: Box<GameViewer> = match self.draw_type {
327 DrawType::Terminal(d) => Box::new(TerminalViewer::new(d)),
328 DrawType::Null => Box::new(EmptyViewer {}),
329 };
330 let viewer_handle = viewer.run();
331 let mut stdin = async_stdin().bytes();
332 let mut ctrl_c = false;
333
334 let mut parser = Parser::new();
335 let mut proc_dead = false;
336 let mut stored_map = None;
337 let mut cnt = 0;
338 while cnt < self.max_loop {
339 macro_rules! do_action {
340 ($act:expr) => {{
341 cnt += 1;
342 if let Some(bytes) = ai.action($act, cnt) {
343 send_or!(self.process, &bytes);
344 }
345 }}
346 }
347 let action_res = match self.process.rx.recv_timeout(self.timeout) {
348 Ok(rec) => match rec {
349 Handle::Panicked => {
350 send_or!(viewer, Handle::Panicked);
351 panic!("panicked in child thread")
352 }
353 Handle::Zero => {
354 debug!(self.term_data.logger, "read zero bytes");
355 send_or!(viewer, Handle::Zero);
356 proc_dead = true;
357 ActionResult::GameEnded
358 }
359 Handle::Valid(ref r) => {
360 send_or!(viewer, Handle::Valid(r));
361 for c in r {
362 parser.advance(&mut self.term_data, *c);
363 }
364 ActionResult::Changed(self.term_data.ret_screen())
365 }
366 },
367 Err(err) => match err {
368 RecvTimeoutError::Timeout => ActionResult::NotChanged,
369 RecvTimeoutError::Disconnected => panic!("disconnected"),
370 },
371 };
372 trace!(self.term_data.logger, "{:?}, turn: {}", action_res, cnt);
373 match action_res {
374 ActionResult::GameEnded => do_action!(ActionResult::GameEnded),
375 ActionResult::Changed(map) => stored_map = Some(map),
377 ActionResult::NotChanged => if let Some(map) = stored_map {
378 do_action!(ActionResult::Changed(map));
379 stored_map = None;
380 } else {
381 do_action!(ActionResult::NotChanged);
382 },
383 }
384 if proc_dead {
385 trace!(self.term_data.logger, "Game ended in turn {}", cnt);
386 break;
387 }
388 if let Some(Ok(3)) = stdin.next() {
389 ctrl_c = true;
390 break;
391 }
392 }
393 if !proc_dead {
394 debug!(
395 self.term_data.logger,
396 "Game not ended and killed process forcibly"
397 );
398 self.process.kill();
399 send_or!(viewer, Handle::Zero);
400 let _ = ai.action(ActionResult::GameEnded, self.max_loop);
401 }
402 if !ctrl_c {
403 proc_handle.join().unwrap();
404 viewer_handle.join().unwrap();
405 }
406 }
407}
408
409enum Handle<T> {
411 Panicked, Zero, Valid(T), }
415
416trait GameViewer {
417 fn run(&mut self) -> JoinHandle<()>;
418 fn send_bytes(&mut self, bytes: Handle<&[u8]>) -> Result<(), ViewerError>;
419}
420
421#[derive(Debug)]
422struct ViewerError(String);
423impl fmt::Display for ViewerError {
424 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
425 write!(f, "{}", self.0)
426 }
427}
428impl Error for ViewerError {
429 fn description(&self) -> &str {
430 &self.0
431 }
432}
433impl From<mpsc::SendError<Handle<Vec<u8>>>> for ViewerError {
434 fn from(e: mpsc::SendError<Handle<Vec<u8>>>) -> Self {
435 ViewerError(e.description().to_owned())
436 }
437}
438
439struct EmptyViewer {}
440
441impl GameViewer for EmptyViewer {
442 fn run(&mut self) -> JoinHandle<()> {
443 thread::spawn(move || {})
444 }
445 fn send_bytes(&mut self, _bytes: Handle<&[u8]>) -> Result<(), ViewerError> {
446 Ok(())
447 }
448}
449
450#[derive(Debug)]
451struct TerminalViewer {
452 tx: mpsc::Sender<Handle<Vec<u8>>>,
453 rx: Arc<Mutex<Receiver<Handle<Vec<u8>>>>>,
454 sleep_time: Arc<Duration>,
455}
456
457impl TerminalViewer {
458 fn new(d: Duration) -> Self {
459 let (tx, rx) = mpsc::channel();
460 let wrapped_recv = Arc::new(Mutex::new(rx));
461 TerminalViewer {
462 tx: tx,
463 rx: wrapped_recv,
464 sleep_time: Arc::new(d),
465 }
466 }
467}
468impl GameViewer for TerminalViewer {
469 fn run(&mut self) -> JoinHandle<()> {
470 let rx = Arc::clone(&self.rx);
471 let sleep = Arc::clone(&self.sleep_time);
472 env::set_var("TERM", "vt100");
473 thread::spawn(move || {
474 let receiver = rx.lock().unwrap();
475 while let Ok(game_input) = (*receiver).recv() {
476 match game_input {
477 Handle::Valid(ref bytes) => {
478 let s = str::from_utf8(bytes).unwrap();
479 let mut stdout = io::stdout()
480 .into_raw_mode()
481 .expect("Couldn't get raw stdin");
482 write!(stdout, "{}", s).expect("Couldn't write to stdin");
483 stdout.flush().expect("Could not flush stdout");
484 }
485 Handle::Zero => break,
486 Handle::Panicked => panic!("main thread panicked"),
487 }
488 thread::sleep(*sleep);
489 }
490 })
491 }
492 fn send_bytes(&mut self, b: Handle<&[u8]>) -> Result<(), ViewerError> {
493 let txclone = self.tx.clone();
494 let res = match b {
495 Handle::Zero => Handle::Zero,
496 Handle::Panicked => Handle::Panicked,
497 Handle::Valid(b) => Handle::Valid(b.to_owned()),
498 };
499 txclone.send(res)?;
500 Ok(())
501 }
502}
503
504#[derive(Debug)]
505struct ProcessError(String);
506
507impl fmt::Display for ProcessError {
508 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
509 write!(f, "{}", self.0)
510 }
511}
512
513impl Error for ProcessError {
514 fn description(&self) -> &str {
515 &self.0
516 }
517}
518
519impl From<io::Error> for ProcessError {
520 fn from(why: io::Error) -> Self {
521 ProcessError(why.description().to_owned())
522 }
523}
524
525struct ProcHandler {
527 my_proc: Child,
528 tx: Sender<Handle<Vec<u8>>>,
529 rx: Receiver<Handle<Vec<u8>>>,
531 killed: Arc<AtomicBool>,
532}
533
534impl ProcHandler {
535 fn from_setting(g: GameSetting) -> ProcHandler {
536 let mut cmd = Command::new(&g.cmdname);
537 let cmd = cmd.args(g.args);
538 let cmd = cmd.env("LINES", format!("{}", g.lines));
539 let cmd = cmd.env("COLUMNS", format!("{}", g.columns));
540 let cmd = cmd.env("TERM", "vt100"); let cmd = cmd.envs(g.envs);
542 let cmd = cmd.stdin(Stdio::piped()).stdout(Stdio::piped());
543 let process = match cmd.spawn() {
544 Ok(p) => p,
545 Err(why) => panic!("couldn't spawn game: {}", why.description()),
546 };
547 let (tx, rx) = mpsc::channel();
548 ProcHandler {
549 my_proc: process,
550 tx: tx,
551 rx: rx,
552 killed: Arc::new(AtomicBool::new(false)),
553 }
554 }
555
556 fn run(&mut self) -> JoinHandle<()> {
557 let proc_out = self.my_proc.stdout.take().unwrap();
558 let txclone = self.tx.clone();
559 let ac = Arc::clone(&self.killed);
560 thread::spawn(move || {
561 let mut proc_reader = BufReader::new(proc_out);
562 const BUFSIZE: usize = 4096;
563 let mut readbuf = vec![0u8; BUFSIZE];
564 while !ac.load(Ordering::Relaxed) {
565 match proc_reader.read(&mut readbuf) {
566 Err(why) => {
567 txclone.send(Handle::Panicked).ok();
568 panic!("couldn't read child stdout: {}", why.description())
569 }
570 Ok(0) => {
571 txclone.send(Handle::Zero).ok();
572 break;
573 }
574 Ok(BUFSIZE) => {
575 txclone.send(Handle::Panicked).ok();
576 panic!("Buffer is too small.")
577 }
578 Ok(n) => {
579 txclone.send(Handle::Valid(readbuf[0..n].to_owned())).ok();
580 }
581 }
582 }
583 })
584 }
585
586 fn send_bytes(&mut self, buf: &[u8]) -> Result<(), ProcessError> {
587 let stdin = self.my_proc.stdin.as_mut().unwrap();
588 stdin.write_all(buf)?;
589 Ok(())
590 }
591
592 fn kill(&mut self) {
593 self.my_proc.kill().unwrap();
594 let ac = Arc::clone(&self.killed);
595 ac.store(true, Ordering::Relaxed)
596 }
597}
598
599impl Drop for ProcHandler {
601 fn drop(&mut self) {
602 self.my_proc.kill().unwrap();
603 }
604}
605
606#[cfg(test)]
607mod tests {
608 #[test]
609 #[ignore]
610 fn test_gameplay() {
611 use super::*;
612 struct EmptyAI {
613 loopnum: usize,
614 };
615 impl Reactor for EmptyAI {
616 fn action(&mut self, _screen: ActionResult, turn: usize) -> Option<Vec<u8>> {
617 let mut res = Vec::new();
618 match turn {
619 val if val == self.loopnum - 1 => res.push(AsciiChar::CarriageReturn.as_byte()),
620 val if val == self.loopnum - 2 => res.push(b'y'),
621 val if val == self.loopnum - 3 => res.push(b'Q'),
622 _ => res.push(b'j'),
623 };
624 Some(res)
625 }
626 }
627 let loopnum = 10;
628 let gs = GameSetting::new("rogue")
629 .env("ROGUEUSER", "EmptyAI")
630 .lines(24)
631 .columns(80)
632 .debug_file("debug.txt")
633 .debug_level(Severity::Trace)
634 .max_loop(loopnum + 1)
635 .draw_on(Duration::from_millis(100));
636 let game = gs.build();
637 let mut ai = EmptyAI { loopnum: loopnum };
638 game.play(&mut ai);
639 }
640}