From 04d6efc6eaf31a3ba8c304884725d3c5e9bab9ae Mon Sep 17 00:00:00 2001 From: tslil clingman <> Date: Mon, 27 Jan 2020 23:12:51 -0800 Subject: Names, who goes first, date in PTN --- Cargo.lock | 54 +++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/game.rs | 27 +++++++++++++++----- src/gui.rs | 6 ++--- src/main.rs | 85 ++++++++++++++++++++++++++++++++++++++++++------------------- 5 files changed, 137 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 62ff9fa..be79879 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5,6 +5,11 @@ name = "argparse" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "autocfg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "cc" version = "1.0.49" @@ -15,6 +20,16 @@ name = "cfg-if" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "chrono" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "libc" version = "0.2.66" @@ -38,6 +53,23 @@ dependencies = [ "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "num-integer" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-traits" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "pancurses" version = "0.16.1" @@ -64,14 +96,30 @@ name = "pkg-config" version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "redox_syscall" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "takwrap" version = "0.1.0" dependencies = [ "argparse 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", "pancurses 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "time" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "winapi" version = "0.3.8" @@ -101,14 +149,20 @@ dependencies = [ [metadata] "checksum argparse 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3f8ebf5827e4ac4fd5946560e6a99776ea73b596d80898f357007317a7141e47" +"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" "checksum cc 1.0.49 (registry+https://github.com/rust-lang/crates.io-index)" = "e450b8da92aa6f274e7c6437692f9f2ce6d701fb73bacfcf87897b3f89a4c20e" "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +"checksum chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "31850b4a4d6bae316f7a09e691c944c28299298837edc0a03f755618c23cbc01" "checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" "checksum ncurses 5.99.0 (registry+https://github.com/rust-lang/crates.io-index)" = "15699bee2f37e9f8828c7b35b2bc70d13846db453f2d507713b758fabe536b82" +"checksum num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" +"checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" "checksum pancurses 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d3058bc37c433096b2ac7afef1c5cdfae49ede0a4ffec3dfc1df1df0959d0ff0" "checksum pdcurses-sys 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "084dd22796ff60f1225d4eb6329f33afaf4c85419d51d440ab6b8c6f4529166b" "checksum pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" +"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" +"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" "checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml index 4d6172b..9a6c10a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,3 +9,4 @@ edition = "2018" [dependencies] pancurses = "0.16.1" argparse = "0.2.2" +chrono = "0.4.10" diff --git a/src/game.rs b/src/game.rs index 3c15796..5db2c48 100644 --- a/src/game.rs +++ b/src/game.rs @@ -670,14 +670,20 @@ pub struct Game { size: u8, current_player: Player, turn_order: TurnOrder, - white_player_name: String, - black_player_name: String, actions: Vec, states: Vec, + white_player_name: String, + black_player_name: String, + date_string: String, } impl Game { - pub fn new(size: u8, white_player_name: &str, black_player_name: &str) -> (Game, String) { + pub fn new( + size: u8, + white_player_name: &str, + black_player_name: &str, + date_str: &str, + ) -> (Game, String) { let (size, warning) = if (size <= 3) || (size >= 8) { (5, format!("Warning: the requested game size of {}x{} is not supported, defaulting to 5x5.", size, size)) @@ -688,12 +694,13 @@ impl Game { ( Game { size: size, - white_player_name: white_player_name.to_string(), - black_player_name: black_player_name.to_string(), states: vec![GameState::new(size)], actions: Vec::new(), current_player: Player::White, turn_order: TurnOrder::WhitePlacesBlack, + white_player_name: white_player_name.to_string(), + black_player_name: black_player_name.to_string(), + date_string: date_str.to_string(), }, warning, ) @@ -709,6 +716,13 @@ impl Game { self.last_state().query_pos(pos) } + pub fn query_current_player_name(&self) -> &str { + match self.current_player { + Player::Black => &self.black_player_name, + Player::White => &self.white_player_name, + } + } + pub fn query_pieces(&self, player: Player, stone: Stone) -> u8 { self.last_state().remaining_pieces(player, stone) } @@ -826,7 +840,8 @@ impl fmt::Display for Game { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, - "[Date \"\"]\n[Player1 \"{}\"]\n[Player2 \"{}\"]\n[Size \"{}\"]\n{}", + "[Date \"{}\"]\n[Player1 \"{}\"]\n[Player2 \"{}\"]\n[Size \"{}\"]\n{}", + self.date_string, self.white_player_name, self.black_player_name, self.size, diff --git a/src/gui.rs b/src/gui.rs index 1b5f8a2..ed5acb5 100644 --- a/src/gui.rs +++ b/src/gui.rs @@ -33,8 +33,7 @@ impl GameLog { } pub fn update(&self, game: &Game) { - // No need to clear, always stays the same size - self.pieces_window.mv(0, 0); + self.pieces_window.clear(); if self.has_colours { self.pieces_window.attrset(ColorPair(self.white_colour)); @@ -85,15 +84,14 @@ impl GameLog { if self.has_colours { self.pieces_window.attrset(ColorPair(self.black_colour)); } - self.pieces_window.addstr("Black"); } Player::White => { if self.has_colours { self.pieces_window.attrset(ColorPair(self.white_colour)); } - self.pieces_window.addstr("White"); } } + self.pieces_window.addstr(game.query_current_player_name()); self.pieces_window.refresh(); diff --git a/src/main.rs b/src/main.rs index 6a2f70a..e46a855 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ extern crate argparse; +extern crate chrono; mod consulter; mod game; @@ -11,6 +12,8 @@ use crate::gui::*; use crate::parser::*; use argparse::*; +use chrono::Local; +use std::env; use std::io::Error; use std::time::SystemTime; @@ -79,10 +82,10 @@ fn init_gui(size: u8) -> GUI { let action_entry = ActionEntry::new(board_bottom, 0, 13); let message_log = MessageLog::new( - board_bottom + 1, + board_bottom + 2, 0, height - board_bottom - 1, - width, + board_right, has_colours, red_colour, ); @@ -116,9 +119,13 @@ fn human_action(gui: &GUI, game: &Game) -> Result { } fn main() { - let mut p1_name = String::from("Player1"); + let mut p1_name = match env::var("USER") { + Err(_) => String::from("Player1"), + Ok(user) => user, + }; let mut p2_name = String::from("Player2"); - let mut computer_opponent = String::new(); + let mut engine = String::new(); + let mut name = String::new(); let mut size: u8 = 5; { @@ -128,25 +135,32 @@ fn main() { ap.refer(&mut p1_name).add_option( &["--player1"], Store, - "Name of first player, Black/White assignment is still random.", + "Name of first player, defaults to $USER. See --name-white below for Black/White assignment.", ); ap.refer(&mut p2_name).add_option( &["--player2"], Store, - "Name of second player, ignored if playing against a - computer opponent.", + "Name of second player, defaults to ``Player2'' and is + overridden by the arument ENGINE of --engine when applicable.", ); - ap.refer(&mut computer_opponent).add_option( + ap.refer(&mut engine).add_option( &["-e", "--engine"], Store, "Name of process which will be used as a computer opponent. The process must accept a single argument point to a PTN file of the current game state, and must generate a single PTN action - on STDOUT.", + on STDOUT. The engine will replace the second player, and the name + of player 2 is set to this argument.", ); + ap.refer(&mut name).add_option( + &["--name-white"], + Store, + "Force the player named NAME to play white. If not supplied, assignment is ``random''.", + ).metavar("NAME"); + ap.refer(&mut size).add_option( &["-s", "--size"], Store, @@ -161,39 +175,57 @@ fn main() { ap.parse_args_or_exit(); } - let human_first; let call_proc; - if !computer_opponent.is_empty() { - p2_name = computer_opponent.clone(); + if !engine.is_empty() { + p2_name = engine.clone(); call_proc = true; - // Perhaps there's a better way. - human_first = match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) { - Ok(n) => n.as_secs() % 2 == 0, - Err(_) => false, - }; + // Perhaps there's a better way. } else { - human_first = false; call_proc = false; } let gui = init_gui(size); - let (mut game, warning) = Game::new(size, &p1_name, &p2_name); + let p1_white; + if !name.is_empty() { + if name == p1_name { + p1_white = true; + } else if name == p2_name { + p1_white = false; + } else { + panic!( + "Player named as white ({}) is not a player in this game ({} and {}).", + name, p1_name, p2_name + ); + } + } else { + p1_white = match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) { + Ok(n) => n.as_millis() % 2 == 0, + Err(_) => false, + }; + } + + let date_string = Local::now().to_string(); + let (mut game, warning) = if p1_white { + Game::new(size, &p1_name, &p2_name, &date_string) + } else { + Game::new(size, &p2_name, &p1_name, &date_string) + }; gui.message_log.log_error(warning); gui.game_log.update(&game); loop { let inp = match game.query_current_player() { Player::Black => { - if call_proc && human_first { - consult_next_action(&game, &computer_opponent) + if call_proc && p1_white { + consult_next_action(&game, &engine) } else { human_action(&gui, &game) } } Player::White => { - if call_proc && (!human_first) { - consult_next_action(&game, &computer_opponent) + if call_proc && (!p1_white) { + consult_next_action(&game, &engine) } else { human_action(&gui, &game) } @@ -208,10 +240,11 @@ fn main() { Ok(act) => { let stone_owner = game.query_stone_owner(); let player = game.query_current_player(); + let pn = game.query_current_player_name(); gui.message_log.log_message(format!( "{} performs {}{}", - player, + pn, act, if stone_owner != player { format!(" with a {} stone.", stone_owner) @@ -224,8 +257,8 @@ fn main() { gui.message_log.log_error(e); let current = game.query_current_player(); if call_proc - && ((human_first && current == Player::Black) - || ((!human_first) && current == Player::White)) + && ((p1_white && current == Player::Black) + || ((!p1_white) && current == Player::White)) { gui.message_log.log_error(String::from( "Error: external and internal game states disagree.", -- cgit v1.3.1