summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authortslil clingman <>2020-02-10 17:36:50 -0800
committertslil clingman <>2020-02-10 17:36:50 -0800
commit15f64980573085e9ab6a9d47fc2a6e8092d6d109 (patch)
treee0b6e1119d642592326deaad2f2634d027a98c2d /src
parent942c13ee26b180316f46921531c737fa8eb4197e (diff)
Feature completion for 0.2.0
Diffstat (limited to 'src')
-rw-r--r--src/consulter.rs8
-rw-r--r--src/game.rs62
-rw-r--r--src/gui.rs92
-rw-r--r--src/main.rs53
4 files changed, 129 insertions, 86 deletions
diff --git a/src/consulter.rs b/src/consulter.rs
index 091eec0..250fd1c 100644
--- a/src/consulter.rs
+++ b/src/consulter.rs
@@ -1,11 +1,11 @@
-use std::fs::File;
+use std::fs::{remove_file, File};
use std::io::{Error, ErrorKind, Result, Write};
use std::process::Command;
use crate::game::*;
use crate::gui::*;
-const FILE_NAME: &str = "/tmp/takwrap_ptn_consult.ptn";
+pub const FILE_NAME: &str = "takwrap_ptn_consult.ptn";
pub fn consult_next_action(game: &Game, proc: &str) -> Result<GUIResult> {
let file = File::create(FILE_NAME)?;
@@ -19,3 +19,7 @@ pub fn consult_next_action(game: &Game, proc: &str) -> Result<GUIResult> {
Ok(raw) => Ok(GUIResult::Raw(raw.trim().to_string())),
}
}
+
+pub fn consulter_clean_up() -> Result<()> {
+ remove_file(FILE_NAME)
+}
diff --git a/src/game.rs b/src/game.rs
index b49f626..18e6fe5 100644
--- a/src/game.rs
+++ b/src/game.rs
@@ -252,7 +252,7 @@ impl GameState {
|| ((pos.x + 1 == self.size) && (pos.y + 1 == self.size))
{
return Err(format!(
- "At the start of an FCS game, {} may not place in a corner (such as {}).",
+ "At the start of game in FCS, {} may not be placed in a corner (such as {}).",
player, pos
));
}
@@ -264,7 +264,7 @@ impl GameState {
|| (pos.y + 1 == self.size)
{
return Err(format!(
- "At the start of an FES game, {} may not place along any edge (and {} is on an edge).",
+ "At the start of a game in FES, {} may not be placed along any edge (and {} is on an edge).",
player, pos
));
}
@@ -726,26 +726,26 @@ impl fmt::Display for StartType {
}
impl StartType {
- fn player_turnorder_at(&self, turn_num: usize) -> (Player, TurnOrder) {
+ fn player_turnorder_at(&self, ply: usize) -> (Player, TurnOrder) {
match self {
StartType::CPS(c) => {
- if turn_num * 2 > (*c as usize) {
- if turn_num % 2 == 0 {
+ if ply / 2 >= (*c as usize) {
+ if ply % 2 == 0 {
(Player::White, TurnOrder::Normal)
} else {
(Player::Black, TurnOrder::Normal)
}
} else {
- if turn_num % 2 == 0 {
+ if ply % 2 == 0 {
(Player::White, TurnOrder::WhitePlacesBlack)
} else {
(Player::Black, TurnOrder::BlackPlacesWhite)
}
}
}
- StartType::FCS => StartType::CPS(1).player_turnorder_at(turn_num),
- StartType::FES => StartType::CPS(1).player_turnorder_at(turn_num),
- StartType::TPS => match turn_num {
+ StartType::FCS => StartType::CPS(1).player_turnorder_at(ply),
+ StartType::FES => StartType::CPS(1).player_turnorder_at(ply),
+ StartType::TPS => match ply {
0 => (Player::White, TurnOrder::WhitePlacesBlack),
1 => (Player::White, TurnOrder::WhitePlacesBlack),
2 => (Player::Black, TurnOrder::BlackPlacesWhite),
@@ -758,10 +758,10 @@ impl StartType {
TurnOrder::Normal,
),
},
- StartType::CZS => match turn_num {
+ StartType::CZS => match ply {
0 => (Player::White, TurnOrder::WhitePlacesBlack),
n => (
- if n % 2 == 0 {
+ if n % 2 == 1 {
Player::White
} else {
Player::Black
@@ -819,32 +819,26 @@ impl Game {
)
}
- // Relying on only ::new(...) being used to make instances
-
- fn last_state(&self) -> &GameState {
- &self.states[self.states.len() - 1]
- }
-
pub fn query_square(&self, pos: &Position) -> Option<&Stack> {
self.last_state().query_pos(pos)
}
- pub fn query_current_player_name(&self) -> &str {
+ pub fn get_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 {
+ pub fn get_pieces(&self, player: Player, stone: Stone) -> u8 {
self.last_state().remaining_pieces(player, stone)
}
- pub fn query_current_player(&self) -> Player {
+ pub fn get_current_player(&self) -> Player {
self.current_player
}
- pub fn query_stone_owner(&self) -> Player {
+ pub fn get_stone_owner(&self) -> Player {
match self.turn_order {
TurnOrder::WhitePlacesBlack => Player::Black,
TurnOrder::BlackPlacesWhite => Player::White,
@@ -852,15 +846,28 @@ impl Game {
}
}
+ pub fn get_start_type(&self) -> StartType {
+ self.start_type
+ }
+
pub fn get_size(&self) -> u8 {
self.size
}
+ pub fn get_ply(&self) -> usize {
+ self.states.len() - 1
+ }
+
+ // Relying on only ::new(...) being used to make instances
+ fn last_state(&self) -> &GameState {
+ &self.states[self.states.len() - 1]
+ }
+
pub fn get_date_string(&self) -> &str {
&self.date_string
}
- pub fn query_action_lines(&self) -> Vec<String> {
+ pub fn get_action_lines(&self) -> Vec<String> {
let mut result = Vec::new();
let mut newline = true;
let num_actions = self.actions.len();
@@ -918,7 +925,7 @@ impl Game {
&& (*player == Player::White)
&& (*stone == Stone::Flat)
{
- state.place_stone(*player, pos, *stone, Some(self.start_type))
+ state.place_stone(*player, pos, *stone, None)
} else {
Err(format!(
"At the start of the game in {}, B must place a W flat.",
@@ -945,7 +952,7 @@ impl Game {
self.states.push(new_state);
self.actions.push(act);
let (current_player, turn_order) =
- self.start_type.player_turnorder_at(self.states.len());
+ self.start_type.player_turnorder_at(self.get_ply());
self.current_player = current_player;
self.turn_order = turn_order;
// This is safe as we have just added to the states vector
@@ -959,8 +966,7 @@ impl Game {
if self.actions.len() > 0 {
self.actions.pop();
self.states.pop();
- let (current_player, turn_order) =
- self.start_type.player_turnorder_at(self.states.len());
+ let (current_player, turn_order) = self.start_type.player_turnorder_at(self.get_ply());
self.turn_order = turn_order;
self.current_player = current_player;
// I'm too lazy to work out exactly which squares must be
@@ -982,13 +988,13 @@ impl fmt::Display for Game {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
- "[Date \"{}\"]\n[Player1 \"{}\"]\n[Player2 \"{}\"]\n[Size \"{}\"][Start \"{}\"]\n{}",
+ "[Date \"{}\"]\n[Player1 \"{}\"]\n[Player2 \"{}\"]\n[Size \"{}\"]\n[Variant \"{}\"]\n{}",
self.date_string,
self.white_player_name,
self.black_player_name,
self.size,
self.start_type,
- self.query_action_lines().join("\n"),
+ self.get_action_lines().join("\n"),
)
}
}
diff --git a/src/gui.rs b/src/gui.rs
index 74f55eb..3a828b6 100644
--- a/src/gui.rs
+++ b/src/gui.rs
@@ -14,6 +14,7 @@ use crate::parser::*;
pub enum GUIResult {
Undo,
Quit,
+ Save,
SwitchElement,
Raw(String),
}
@@ -63,7 +64,7 @@ impl GameLog {
}
self.pieces_window.addstr(format!(
"W: {:2}",
- game.query_pieces(Player::White, Stone::Flat)
+ game.get_pieces(Player::White, Stone::Flat)
));
self.pieces_window.attrset(Attribute::Normal);
@@ -74,10 +75,14 @@ impl GameLog {
}
self.pieces_window.addstr(format!(
"B: {:2}",
- game.query_pieces(Player::Black, Stone::Flat),
+ game.get_pieces(Player::Black, Stone::Flat),
));
self.pieces_window.attrset(Attribute::Normal);
+
+ self.pieces_window
+ .addstr(format!(" Variant: {}", game.get_start_type().to_string()));
+
self.pieces_window.mv(1, 0);
if self.has_colours {
@@ -85,7 +90,7 @@ impl GameLog {
}
self.pieces_window.addstr(format!(
"WC: {}",
- game.query_pieces(Player::White, Stone::Capstone)
+ game.get_pieces(Player::White, Stone::Capstone)
));
self.pieces_window.attrset(Attribute::Normal);
@@ -96,13 +101,16 @@ impl GameLog {
}
self.pieces_window.addstr(format!(
"BC: {}",
- game.query_pieces(Player::Black, Stone::Capstone),
+ game.get_pieces(Player::Black, Stone::Capstone),
));
self.pieces_window.attrset(Attribute::Normal);
+ self.pieces_window
+ .addstr(format!(" Ply: {}", game.get_ply() + 1));
+
self.pieces_window.mv(3, 0);
- self.pieces_window.addstr("Turn: ");
- match game.query_current_player() {
+ self.pieces_window.addstr("Active player: ");
+ match game.get_current_player() {
Player::Black => {
if self.has_colours {
self.pieces_window.attrset(ColorPair(self.black_colour));
@@ -114,9 +122,9 @@ impl GameLog {
}
}
}
- self.pieces_window.addstr(game.query_current_player_name());
+ self.pieces_window.addstr(game.get_current_player_name());
- let new_lines = game.query_action_lines();
+ let new_lines = game.get_action_lines();
if new_lines.len() > self.num_lines {
self.offset = new_lines.len() - self.num_lines;
} else {
@@ -161,6 +169,7 @@ impl GameLog {
Character('\t') => return GUIResult::SwitchElement,
Character('U') => return GUIResult::Undo,
Character('Q') => return GUIResult::Quit,
+ Character('S') => return GUIResult::Save,
_ => (),
}
}
@@ -285,6 +294,7 @@ impl BoardGUI {
self.board_window.untouch();
}
+ /*
pub fn read_action(&mut self) -> GUIResult {
loop {
if let Some(key) = self.board_window.getch() {
@@ -322,7 +332,7 @@ impl BoardGUI {
}
}
}
- }
+ }*/
pub fn new(
ypos: i32,
@@ -509,12 +519,12 @@ enum InputSource {
}
const ACTION_HEADER: &str = "Enter action in PTN";
-const LOG_HEADER: &str = "U to undo a turn | <UP>/<DOWN> to scroll | Q to quit";
+const LOG_HEADER: &str = "U to undo a turn | <UP>/<DOWN> to scroll | S to save PTN | Q to quit";
pub struct GUI {
screen: Window,
game_log: GameLog,
- message_log: MessageLog,
+ pub message_log: MessageLog,
action_entry: ActionEntry,
board_gui: BoardGUI,
input_source: InputSource,
@@ -533,6 +543,25 @@ impl GUI {
self.screen.refresh();
}
+ fn save(&self, game: &Game) {
+ let file = File::create(format!("Game_{}.ptn", game.get_date_string()));
+
+ match file {
+ Err(e) => {
+ self.message_log
+ .log_error(format!("Unable to open PTN file for writing: {}", e));
+ }
+ Ok(file) => {
+ if let Err(e) = write!(&file, "{}", game.to_string()) {
+ self.message_log
+ .log_error(format!("Unable to write PTN to file: {}", e));
+ } else {
+ self.message_log.log_message(String::from("PTN saved."));
+ }
+ }
+ }
+ }
+
pub fn new(game: &Game, warning: String) -> GUI {
let screen = initscr();
cbreak();
@@ -623,7 +652,7 @@ impl GUI {
) -> Result<GUIResult, String> {
match self.input_source {
InputSource::Player => {
- let player = game.query_current_player();
+ let player = game.get_current_player();
let inp = match player {
Player::Black => {
@@ -662,7 +691,7 @@ impl GUI {
p1_white: bool,
input: Result<GUIResult, String>,
) -> bool {
- let player = game.query_current_player();
+ let player = game.get_current_player();
let engine_turn = call_proc
&& ((p1_white && player == Player::Black) || ((!p1_white) && player == Player::White));
@@ -681,6 +710,11 @@ impl GUI {
self.screen.getch();
return false;
}
+ GUIResult::Save => {
+ self.save(game);
+ self.screen.getch();
+ return false;
+ }
GUIResult::SwitchElement => match self.input_source {
InputSource::Player => {
self.input_source = InputSource::Log;
@@ -703,7 +737,7 @@ impl GUI {
}
},
GUIResult::Raw(raw) => {
- let stone_owner = game.query_stone_owner();
+ let stone_owner = game.get_stone_owner();
let act = parse_action(stone_owner, &raw);
match act {
Err(err) => {
@@ -714,7 +748,7 @@ impl GUI {
}
}
Ok(act) => {
- let pn = game.query_current_player_name();
+ let pn = game.get_current_player_name();
self.message_log.log_message(format!(
"{} performs {}{}",
pn,
@@ -736,7 +770,7 @@ impl GUI {
return false;
} else {
self.message_log.log_error(format!(
- "Cannot perform \"{}\": {}",
+ "Cannot perform \"{}\": p{}",
act_string, e
));
}
@@ -754,31 +788,7 @@ impl GUI {
if let Some(inp) = self.screen.getch() {
match inp {
Character('S') => {
- let file = File::create(format!(
- "Game_{}.ptn",
- game.get_date_string()
- ));
-
- match file {
- Err(e) => {
- self.message_log.log_error(format!(
- "Unable to open PTN file for writing: {}",
- e
- ));
- }
- Ok(file) => {
- if let Err(e) = write!(
- &file,
- "{}",
- game.to_string()
- ) {
- self.message_log.log_error(format!(
- "Unable to write PTN to file: {}",
- e
- ));
- }
- }
- }
+ self.save(game);
return false;
}
Character('Q') => {
diff --git a/src/main.rs b/src/main.rs
index 50bfcf4..31414b8 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -6,6 +6,7 @@ mod game;
mod gui;
mod parser;
+use crate::consulter::consulter_clean_up;
use crate::game::*;
use crate::gui::*;
use crate::parser::parse_start_type;
@@ -28,45 +29,59 @@ fn main() {
{
let mut ap = ArgumentParser::new();
- ap.set_description("Test description.");
+ ap.set_description(
+ "A TUI
+ implementation of and wrapper for engines for the game of
+ Tak.",
+ );
ap.refer(&mut p1_name).add_option(
&["--player1"],
Store,
- "Name of first player, defaults to $USER. See --name-white below for Black/White assignment.",
+ "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, defaults to ``Player2'' and is
- overridden by the arument ENGINE of --engine when applicable.",
+ overridden by the arument ENGINE of --engine when
+ applicable.",
);
ap.refer(&mut start_type_string)
.add_option(
&["-v", "--variant-start"],
Store,
- "Select the rule variant for the start of play. Valid choices are CPSn, FCS, FES, TPS, and CZS.
- The default starting type, which matches the original rules, is CPS1.",
+ "Select the rule variant for
+ the start of play. Valid choices are CPSn, FCS, FES, TPS,
+ and CZS. The default starting type, which matches the
+ original rules, is CPS1. See ENGINE below for a warning on
+ this.",
)
.metavar("VRNT");
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. The engine will replace the second player, and the name
- of player 2 is set to this argument.",
+ "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. The engine will replace the
+ second player, and the name of player 2 is set to this
+ argument. Note: ensure that your engine understands the
+ [Variant XXX] directive if you choose a variant (above).",
);
- 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 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"],
@@ -136,6 +151,14 @@ fn main() {
}
}
+ // Clean up file
+ if call_proc {
+ if let Err(e) = consulter_clean_up() {
+ gui.message_log
+ .log_error(format!("Error in cleaning up temporary files: {}", e));
+ }
+ }
+
// board_gui.read_action();
endwin();
}