summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/consulter.rs2
-rw-r--r--src/game.rs139
-rw-r--r--src/gui.rs2
-rw-r--r--src/main.rs10
-rw-r--r--src/parser.rs44
5 files changed, 88 insertions, 109 deletions
diff --git a/src/consulter.rs b/src/consulter.rs
index 1f27124..a8e666f 100644
--- a/src/consulter.rs
+++ b/src/consulter.rs
@@ -16,7 +16,7 @@ pub fn consult_next_action(game: &Game, proc: &str) -> Result<Action> {
match String::from_utf8(output.stdout) {
Err(e) => Err(Error::new(ErrorKind::InvalidData, e)),
- Ok(string) => match parse_action(game, &string) {
+ Ok(string) => match parse_action(game.query_stone_owner(), &string.trim()) {
Err(string) => Err(Error::new(ErrorKind::InvalidData, string)),
Ok(action) => Ok(action),
},
diff --git a/src/game.rs b/src/game.rs
index a6c79a4..3c15796 100644
--- a/src/game.rs
+++ b/src/game.rs
@@ -78,21 +78,28 @@ impl fmt::Display for Direction {
pub enum Action {
Place(Player, Position, Stone),
- Move(Player, Position, Direction, Vec<u8>),
+ Move(Position, Direction, u8, Vec<u8>),
}
impl fmt::Display for Action {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Action::Place(_, pos, stone) => write!(f, "{}{}", stone, pos),
- Action::Move(_, pos, direction, drops) => write!(
- f,
- "{}{}{}",
- // drops.iter().map(|&d| d as u32).sum::<u32>(),
- pos,
- direction,
- drops.into_iter().map(|q| q.to_string()).collect::<String>()
- ),
+ Action::Move(pos, direction, picked_up, drops) => {
+ match (*picked_up > 1, drops.len() > 1) {
+ (true, true) => write!(
+ f,
+ "{}{}{}{}",
+ picked_up,
+ pos,
+ direction,
+ drops.into_iter().map(|q| q.to_string()).collect::<String>(),
+ ),
+ (true, false) => write!(f, "{}{}{}", picked_up, pos, direction),
+ // (false, true) should be impossible
+ _ => write!(f, "{}{}", pos, direction),
+ }
+ }
}
}
}
@@ -254,9 +261,8 @@ impl GameState {
pos: &Position,
stone: Stone,
) -> Result<(Vec<Position>, GameState), String> {
- if let Err(e) = self.is_legal_place(player, pos, stone) {
- return Err(e);
- }
+ self.is_legal_place(player, pos, stone)?;
+
// Place stone
let mut copy = self.copy();
let idx = self.pos_to_idx(&pos);
@@ -264,6 +270,7 @@ impl GameState {
player: player,
stone: stone,
});
+
// Decrease count
match player {
Player::Black => {
@@ -289,6 +296,7 @@ impl GameState {
player: Player,
pos: &Position,
direction: Direction,
+ picked_up: u8,
drops: &Vec<u8>,
) -> Result<(), String> {
if self.within_bounds(pos) {
@@ -297,10 +305,8 @@ impl GameState {
- Drops have been specified
- Must actually move at least one stone
- Top stone belongs to player
- - Zero or more dropped on starting square
- One or more on each subsequent square
- - Total number of stones moved does not exceed the carry capacity
- - Moved the entire stack (up to the carry capacity)
+ - Total number of stones picked up does not exceed the carry capacity
- Direction does not contain a capstone
- Wall may only appear on last spot if it's capstone alone that covers
- All stones are used up before then end of the board is met
@@ -319,7 +325,7 @@ impl GameState {
));
};
- if drops_len == 1 && drops[0] == 1 {
+ if picked_up == 0 {
return Err(String::from(
"A valid move must change the position of at least a single stone.",
));
@@ -335,34 +341,35 @@ impl GameState {
};
for i in 0..drops_len {
- if i > 0 && drops[i] == 0 {
+ if drops[i] == 0 {
return Err(String::from(
- "A move may not drop 0 stones past the first square.",
+ "A move may not drop 0 stones on subsequent squares.",
));
}
}
+ let picked_up = picked_up as usize;
let sum_dropped = drops.iter().map(|&d| d as usize).sum::<usize>();
- let carry_capacity = self.size as usize;
- if sum_dropped > carry_capacity {
+ if sum_dropped != picked_up {
return Err(format!(
- "A move may not exceed the carry capacity of {} stones.",
- self.size
+ "Move intended to pick up {} stones but drop {}.",
+ picked_up, sum_dropped
));
}
- if sum_dropped != std::cmp::min(carry_capacity, stack_len) {
- return Err(String::from(
- "A move must effect the whole stack, up to the carry limit.",
+ let carry_capacity = self.size as usize;
+ if picked_up > carry_capacity {
+ return Err(format!(
+ "A move may not pick up more than the carry capacity of {} stones.",
+ self.size
));
}
- let mut steps: usize = drops.len() - 1;
+ let mut steps: usize = drops.len();
let cap: bool = stack[0].stone == Stone::Capstone;
let mut pos_new = Position { x: pos.x, y: pos.y };
while steps > 0 {
- steps -= 1;
if {
match direction {
Direction::Up => pos_new.y + 1 >= self.size,
@@ -404,6 +411,7 @@ impl GameState {
));
}
}
+ steps -= 1;
}
return Ok(());
} else {
@@ -422,11 +430,10 @@ impl GameState {
player: Player,
pos: &Position,
direction: Direction,
+ picked_up: u8,
drops: &Vec<u8>,
) -> Result<(Vec<Position>, GameState), String> {
- if let Err(e) = self.is_legal_move(player, pos, direction, drops) {
- return Err(e);
- }
+ self.is_legal_move(player, pos, direction, picked_up, drops)?;
let mut copy = self.copy();
let mut pos_vec: Vec<Position> = vec![pos.clone()];
@@ -436,21 +443,35 @@ impl GameState {
let stack_height = copy.board[idx].len();
- let mut offset = 0;
- let carry_capacity = self.size as usize;
- if stack_height > carry_capacity {
- for _i in 0..carry_capacity {
- copy.board[idx].pop();
- }
- offset = stack_height - carry_capacity;
- } else {
- copy.board[idx].clear();
+ let mut offset = stack_height - (picked_up as usize);
+
+ for _i in offset..stack_height {
+ copy.board[idx].pop();
}
- let num_drops = drops.len();
- for drop_idx in 0..num_drops {
- let pos = pos_vec[pos_vec.len() - 1];
- let idx = copy.pos_to_idx(&pos);
+ for drop_idx in 0..drops.len() {
+ let new_pos = pos_vec[pos_vec.len() - 1];
+ pos_vec.push(match direction {
+ Direction::Up => Position {
+ x: new_pos.x,
+ y: new_pos.y + 1,
+ },
+ Direction::Down => Position {
+ x: new_pos.x,
+ y: new_pos.y - 1,
+ },
+ Direction::Left => Position {
+ x: new_pos.x - 1,
+ y: new_pos.y,
+ },
+ Direction::Right => Position {
+ x: new_pos.x + 1,
+ y: new_pos.y,
+ },
+ });
+
+ let new_pos = pos_vec[pos_vec.len() - 1];
+ let idx = copy.pos_to_idx(&new_pos);
let num = drops[drop_idx] as usize;
for i in 0..num {
@@ -466,27 +487,6 @@ impl GameState {
copy.board[idx].push(stack[offset + i]);
}
offset += num;
-
- if drop_idx + 1 < num_drops {
- pos_vec.push(match direction {
- Direction::Up => Position {
- x: pos.x,
- y: pos.y + 1,
- },
- Direction::Down => Position {
- x: pos.x,
- y: pos.y - 1,
- },
- Direction::Left => Position {
- x: pos.x - 1,
- y: pos.y,
- },
- Direction::Right => Position {
- x: pos.x + 1,
- y: pos.y,
- },
- });
- }
}
Ok((pos_vec, copy))
}
@@ -787,16 +787,9 @@ impl Game {
}
TurnOrder::Normal => state.place_stone(*player, pos, *stone),
},
- Action::Move(player, pos, direction, drops) => match self.turn_order {
+ Action::Move(pos, direction, picked_up, drops) => match self.turn_order {
TurnOrder::Normal => {
- if *player == self.current_player {
- state.move_stack(*player, pos, *direction, drops)
- } else {
- Err(format!(
- "{} may not take actions on {}'s turn.",
- player, self.current_player
- ))
- }
+ state.move_stack(self.current_player, pos, *direction, *picked_up, drops)
}
_ => Err(String::from(
"At the start of the game only placing flats is allowed.",
diff --git a/src/gui.rs b/src/gui.rs
index bd7ca7c..1b5f8a2 100644
--- a/src/gui.rs
+++ b/src/gui.rs
@@ -204,7 +204,7 @@ impl BoardGUI {
for i in 0..height {
let piece = &stack[i];
self.draw_stone(win, i + 1 == height, &piece.player, &piece.stone);
- if tall && i + 2 == carry_capacity {
+ if tall && i + 1 == height - carry_capacity {
win.attrset(Attribute::Normal);
win.addch(')');
}
diff --git a/src/main.rs b/src/main.rs
index 8335422..6a2f70a 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -101,10 +101,12 @@ fn init_gui(size: u8) -> GUI {
fn human_action(gui: &GUI, game: &Game) -> Result<Action, Error> {
loop {
let inp = gui.action_entry.read_action();
- match parse_action(&game, &inp) {
- Err(err) => gui
- .message_log
- .log_error(format!("Input \"{}\": {}", inp, err)),
+ match parse_action(game.query_stone_owner(), &inp) {
+ Err(err) => {
+ gui.message_log
+ .log_error(format!("Input \"{}\": {}", inp, err));
+ gui.action_entry.clear_entry_area();
+ }
Ok(action) => {
gui.action_entry.clear_entry_area();
return Ok(action);
diff --git a/src/parser.rs b/src/parser.rs
index 9d50377..68e49a8 100644
--- a/src/parser.rs
+++ b/src/parser.rs
@@ -1,6 +1,6 @@
use crate::game::*;
-fn parse_move(mv: &str, game: &Game) -> Result<Action, String> {
+fn parse_move(mv: &str) -> Result<Action, String> {
let mut chars = mv.chars();
let c = chars.next();
@@ -9,21 +9,19 @@ fn parse_move(mv: &str, game: &Game) -> Result<Action, String> {
}
let x;
- let fill_in_number;
+ let picked_up;
let dc = c.unwrap();
- let mut sum_drops: u8 = 1;
if (dc >= 'a') && (dc <= 'h') {
// Optional drop number omitted, assume everything
- fill_in_number = true;
x = dc;
+ picked_up = 1;
} else {
if (dc < '1') || ('9' < dc) {
return Err(String::from(
- "Moves must begin with a digit in 1-8 indicating the number of drops.",
+ "Moves must begin with a digit in 1-8 indicating the stones picked up.",
));
}
- fill_in_number = false;
- sum_drops = dc.to_digit(10).unwrap() as u8;
+ picked_up = dc.to_digit(10).unwrap() as u8;
let c = chars.next();
if c.is_none() {
@@ -88,32 +86,22 @@ fn parse_move(mv: &str, game: &Game) -> Result<Action, String> {
}
let pos = Position { x: x, y: y };
- if fill_in_number {
- if let Some(stack) = game.query_square(&pos) {
- sum_drops = std::cmp::min(stack.len() as u8, game.get_size());
- } else {
- return Err(String::from(
- "Moves must specify a valid position on the game board.",
- ));
- }
- }
let sum = drops.iter().map(|&d| d as u32).sum::<u32>();
if sum == 0 {
// Omitted all the drops, it's a total stack move
- drops.push(0);
- drops.push(sum_drops);
- } else if sum != sum_drops as u32 {
+ drops.push(picked_up);
+ } else if sum != picked_up as u32 {
return Err(format!(
"The move called for {} stones, but {} were dropped.",
- sum_drops, sum
+ picked_up, sum
));
}
- Ok(Action::Move(game.query_stone_owner(), pos, dir, drops))
+ Ok(Action::Move(pos, dir, picked_up, drops))
}
-fn parse_place(pl: &str, game: &Game) -> Result<Action, String> {
+fn parse_place(pl: &str, stone_owner: Player) -> Result<Action, String> {
let mut chars = pl.chars();
let c = chars.next();
@@ -167,20 +155,16 @@ fn parse_place(pl: &str, game: &Game) -> Result<Action, String> {
}
let y: u8 = (y.to_digit(10).unwrap() - 1) as u8;
- Ok(Action::Place(
- game.query_stone_owner(),
- Position { x: x, y: y },
- stone,
- ))
+ Ok(Action::Place(stone_owner, Position { x: x, y: y }, stone))
}
-pub fn parse_action(game: &Game, act: &str) -> Result<Action, String> {
+pub fn parse_action(stone_owner: Player, act: &str) -> Result<Action, String> {
if act
.chars()
.any(|c| c == '+' || c == '-' || c == '>' || c == '<')
{
- parse_move(act, game)
+ parse_move(act)
} else {
- parse_place(act, game)
+ parse_place(act, stone_owner)
}
}