summaryrefslogtreecommitdiff
path: root/src/game.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/game.rs')
-rw-r--r--src/game.rs165
1 files changed, 117 insertions, 48 deletions
diff --git a/src/game.rs b/src/game.rs
index 1d61732..2922121 100644
--- a/src/game.rs
+++ b/src/game.rs
@@ -228,7 +228,7 @@ impl GameState {
player: Player,
pos: &Position,
stone: Stone,
- ) -> Result<GameState, String> {
+ ) -> Result<(Vec<Position>, GameState), String> {
if let Err(e) = self.is_legal_place(player, pos, stone) {
return Err(e);
}
@@ -256,7 +256,7 @@ impl GameState {
}
}
}
- Ok(copy)
+ Ok((vec![pos.clone()], copy))
}
fn is_legal_move(
@@ -268,37 +268,70 @@ impl GameState {
) -> Result<(), String> {
if self.within_bounds(pos) {
/* Rules for moving a stack:
- 0. There are stones
- 1. Top stone belongs to player
- 2. Zero or One stones dropped on starting square
- 3. Total number of stones moved does not exceed the carry capacity
- 4. Direction does not contain a capstone
- 5. Wall may only appear on last spot if it's capstone alone that covers
- 6. All stones are used up before then end of the board is met
+ - There are stones
+ - 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)
+ - 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
*/
if let Some(stack) = &self.board.get(self.pos_to_idx(pos)) {
- if stack.len() == 0 {
+ let stack_len = stack.len();
+ let drops_len = drops.len();
+
+ if stack_len == 0 {
return Err(format!("{} has no stones to move.", pos));
- } else if drops.len() == 0 {
+ };
+
+ if drops_len == 0 {
return Err(String::from(
"A drop sequence for must be specified for a move.",
));
- } else if stack[0].player != player {
- return Err(format!(
- "{} may not move the stack at {} as it belongs to {}.",
- player, pos, stack[0].player
+ };
+
+ if drops_len == 1 && drops[0] == 1 {
+ return Err(String::from(
+ "A valid move must change the position of at least a single stone.",
));
- } else if drops[0] > 1 {
+ }
+
+ if stack[stack_len - 1].player != player {
return Err(format!(
- "A move may only drop 0 or 1 stones at it's origin square."
+ "{} may not move the stack at {} as it belongs to {}.",
+ player,
+ pos,
+ stack[stack_len - 1].player
));
- // Do the sum in u32 just in case ?
- } else if drops.iter().map(|&d| d as u32).sum::<u32>() > self.size as u32 {
+ };
+
+ for i in 0..drops_len {
+ if i > 0 && drops[i] == 0 {
+ return Err(String::from(
+ "A move may not drop 0 stones past the first square.",
+ ));
+ }
+ }
+
+ let num_dropped = drops.iter().map(|&d| d as usize).sum::<usize>();
+ let carry_capacity = self.size as usize;
+ if num_dropped > carry_capacity {
return Err(format!(
- "A move may not exceed the carry limit of {} stones.",
+ "A move may not exceed the carry capacity of {} stones.",
self.size
));
}
+
+ if num_dropped != std::cmp::max(carry_capacity, stack_len) {
+ return Err(String::from(
+ "A move must effect the whole stack, up to the carry limit.",
+ ));
+ }
+
let mut steps: usize = drops.len() - 1;
let cap: bool = stack[0].stone == Stone::Capstone;
let mut pos_new = Position { x: pos.x, y: pos.y };
@@ -309,8 +342,8 @@ impl GameState {
match direction {
Direction::Up => pos_new.y + 1 >= self.size,
Direction::Down => pos_new.y == 0,
- Direction::Left => pos_new.x + 1 >= self.size,
- Direction::Right => pos_new.x == 0,
+ Direction::Left => pos_new.x == 0,
+ Direction::Right => pos_new.x + 1 >= self.size,
}
} {
return Err(String::from("A move may not extend past the board."));
@@ -318,8 +351,8 @@ impl GameState {
match direction {
Direction::Up => pos_new.y += 1,
Direction::Down => pos_new.y -= 1,
- Direction::Left => pos_new.x += 1,
- Direction::Right => pos_new.x -= 1,
+ Direction::Left => pos_new.x -= 1,
+ Direction::Right => pos_new.x += 1,
}
if let Some(stack) = &self.board.get(self.pos_to_idx(&pos_new)) {
if stack.len() > 0 {
@@ -365,30 +398,53 @@ impl GameState {
pos: &Position,
direction: Direction,
drops: &Vec<u8>,
- ) -> Result<GameState, String> {
+ ) -> Result<(Vec<Position>, GameState), String> {
if let Err(e) = self.is_legal_move(player, pos, direction, drops) {
return Err(e);
}
let mut copy = self.copy();
- let mut pos_new = Position { x: pos.x, y: pos.y };
+ let mut pos_vec: Vec<Position> = vec![pos.clone()];
+
+ let idx = copy.pos_to_idx(&pos);
let stack: &Stack = &self.board[self.pos_to_idx(pos)];
- let mut k = 0;
- for &d in drops {
- let idx = copy.pos_to_idx(&pos_new);
- for i in 0..d {
- copy.board[idx].push(stack[(k + i) as usize]);
+ copy.board[idx].clear();
+
+ let num_drops = drops.len();
+ let mut offset = 0;
+ for drop_idx in 0..num_drops {
+ let pos = pos_vec[pos_vec.len() - 1];
+ let idx = copy.pos_to_idx(&pos);
+ let num = drops[drop_idx];
+
+ for i in 0..num {
+ copy.board[idx].push(stack[(offset + i) as usize]);
+ }
+ 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,
+ },
+ });
}
- k += d;
- match direction {
- Direction::Up => pos_new.y += 1,
- Direction::Down => pos_new.y -= 1,
- Direction::Left => pos_new.x -= 1,
- Direction::Right => pos_new.x += 1,
- };
}
- Ok(copy)
+ Ok((pos_vec, copy))
}
}
@@ -449,6 +505,14 @@ impl Game {
self.current_player
}
+ pub fn query_stone_owner(&self) -> Player {
+ match self.turn_order {
+ TurnOrder::BlackPlacesWhite => Player::White,
+ TurnOrder::WhitePlacesBlack => Player::Black,
+ TurnOrder::Normal => self.current_player,
+ }
+ }
+
// pub fn query_action(&self, turn: u16) -> Option<&Action> {
// self.actions.get(turn as usize)
// }
@@ -459,15 +523,15 @@ impl Game {
pub fn query_action_lines(&self) -> String {
let mut result = String::new();
- let mut newline = false;
+ let mut newline = true;
result += "0. ";
for i in 0..self.actions.len() {
- if newline {
+ if i > 1 && newline {
// TODO: Is placing the opponent's first stone the zeroeth action?
result += &format!("{}. ", i);
}
result += &format!("{} ", self.actions[i]);
- if !newline {
+ if i > 0 && !newline {
result.push('\n');
}
newline = !newline;
@@ -475,7 +539,7 @@ impl Game {
result
}
- pub fn perform_action(&mut self, act: Action) -> Result<(), String> {
+ pub fn perform_action(&mut self, act: Action) -> Result<Vec<Position>, String> {
let maybe_state = self.states.last();
if maybe_state.is_none() {
return Err(String::from("Internal error: cannot find last game state"));
@@ -528,19 +592,24 @@ impl Game {
match new_state_either {
Err(e) => return Err(e),
- Ok(new_state) => {
+ Ok((pos_vec, new_state)) => {
self.states.push(new_state);
self.actions.push(act);
- self.current_player = match self.current_player {
- Player::Black => Player::White,
- Player::White => Player::Black,
+ self.current_player = match self.turn_order {
+ TurnOrder::BlackPlacesWhite => Player::White,
+ TurnOrder::WhitePlacesBlack => Player::White,
+ TurnOrder::Normal => match self.current_player {
+ Player::Black => Player::White,
+ Player::White => Player::Black,
+ },
};
+
self.turn_order = match self.turn_order {
TurnOrder::BlackPlacesWhite => TurnOrder::WhitePlacesBlack,
TurnOrder::WhitePlacesBlack => TurnOrder::Normal,
TurnOrder::Normal => TurnOrder::Normal,
};
- return Ok(());
+ return Ok(pos_vec);
}
}
}