aboutsummaryrefslogtreecommitdiff
path: root/src/game.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/game.rs')
-rw-r--r--src/game.rs172
1 files changed, 127 insertions, 45 deletions
diff --git a/src/game.rs b/src/game.rs
index 2745073..c851e3d 100644
--- a/src/game.rs
+++ b/src/game.rs
@@ -230,12 +230,49 @@ impl GameState {
self.board.get(self.pos_to_idx(pos))
}
- fn is_legal_place(&self, player: Player, pos: &Position, stone: Stone) -> Result<(), String> {
+ fn is_legal_place(
+ &self,
+ player: Player,
+ pos: &Position,
+ stone: Stone,
+ check_start: Option<StartType>,
+ ) -> Result<(), String> {
/* In order to legally place a piece:
1. The desired square must be empty
2. The player must have sufficient pieces
+ 3. We must account for F{C,E}S
*/
if self.within_bounds(pos) {
+ if let Some(start_type) = check_start {
+ match start_type {
+ StartType::FCS => {
+ if ((pos.x == 0) && (pos.y == 0))
+ || ((pos.x + 1 == self.size) && (pos.y == 0))
+ || ((pos.x == 0) && (pos.y + 1 == self.size))
+ || ((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 {}).",
+ player, pos
+ ));
+ }
+ }
+ StartType::FES => {
+ if (pos.x == 0)
+ || (pos.y == 0)
+ || (pos.x + 1 == self.size)
+ || (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).",
+ player, pos
+ ));
+ }
+ }
+ _ => (),
+ }
+ }
+
if let Some(stack) = &self.board.get(self.pos_to_idx(pos)) {
if stack.len() > 0 {
return Err(format!("{} is already occupied.", pos));
@@ -263,8 +300,9 @@ impl GameState {
player: Player,
pos: &Position,
stone: Stone,
+ check_start: Option<StartType>,
) -> Result<(Vec<Position>, GameState), String> {
- self.is_legal_place(player, pos, stone)?;
+ self.is_legal_place(player, pos, stone, check_start)?;
// Place stone
let mut copy = self.copy();
@@ -666,27 +704,78 @@ enum TurnOrder {
Normal,
}
-/*
+#[derive(Clone, Copy)]
pub enum StartType {
CPS(u8),
FCS,
+ FES,
TPS,
CZS,
}
-impl TurnOrder {
- pub fn next_turn(&self, turn_num: usize, st: StartType) {
- match st {
- StartType::CPS(c) => {
+impl fmt::Display for StartType {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ StartType::CPS(n) => write!(f, "CPS{}", n),
+ StartType::FCS => write!(f, "FCS"),
+ StartType::FES => write!(f, "FES"),
+ StartType::TPS => write!(f, "TPS"),
+ StartType::CZS => write!(f, "CZS"),
+ }
+ }
+}
+impl StartType {
+ fn player_turnorder_at(&self, turn_num: usize) -> (Player, TurnOrder) {
+ match self {
+ StartType::CPS(c) => {
+ if turn_num + 1 > (*c as usize) {
+ if turn_num % 2 == 0 {
+ (Player::White, TurnOrder::Normal)
+ } else {
+ (Player::Black, TurnOrder::Normal)
+ }
+ } else {
+ if turn_num % 2 == 0 {
+ (Player::White, TurnOrder::WhitePlacesBlack)
+ } else {
+ (Player::Black, TurnOrder::BlackPlacesWhite)
+ }
+ }
}
+ StartType::FCS => StartType::CPS(2).player_turnorder_at(turn_num),
+ StartType::FES => StartType::CPS(2).player_turnorder_at(turn_num),
+ StartType::TPS => match turn_num {
+ 0 => (Player::White, TurnOrder::WhitePlacesBlack),
+ 1 => (Player::White, TurnOrder::WhitePlacesBlack),
+ 2 => (Player::Black, TurnOrder::BlackPlacesWhite),
+ n => (
+ if n % 2 == 0 {
+ Player::Black
+ } else {
+ Player::White
+ },
+ TurnOrder::Normal,
+ ),
+ },
+ StartType::CZS => match turn_num {
+ 0 => (Player::White, TurnOrder::WhitePlacesBlack),
+ n => (
+ if n % 2 == 0 {
+ Player::White
+ } else {
+ Player::Black
+ },
+ TurnOrder::Normal,
+ ),
+ },
}
}
}
-*/
pub struct Game {
size: u8,
+ start_type: StartType,
current_player: Player,
turn_order: TurnOrder,
actions: Vec<Action>,
@@ -703,6 +792,7 @@ impl Game {
white_player_name: &str,
black_player_name: &str,
date_str: &str,
+ start_type: StartType,
) -> (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.",
@@ -711,17 +801,19 @@ impl Game {
(size, String::new())
};
+ let (current_player, turn_order) = start_type.player_turnorder_at(0);
(
Game {
- size: size,
+ size,
+ start_type,
states: vec![GameState::new(size)],
actions: Vec::new(),
- current_player: Player::White,
- turn_order: TurnOrder::WhitePlacesBlack,
+ current_player,
+ turn_order,
+ win_type: None,
white_player_name: white_player_name.to_string(),
black_player_name: black_player_name.to_string(),
date_string: date_str.to_string(),
- win_type: None,
},
warning,
)
@@ -813,10 +905,11 @@ impl Game {
&& (*player == Player::Black)
&& (*stone == Stone::Flat)
{
- state.place_stone(*player, pos, *stone)
+ state.place_stone(*player, pos, *stone, Some(self.start_type))
} else {
- Err(String::from(
- "At the start of the game, W must place a B flat.",
+ Err(format!(
+ "At the start of the game in {}, W must place a B flat.",
+ self.start_type
))
}
}
@@ -825,21 +918,23 @@ impl Game {
&& (*player == Player::White)
&& (*stone == Stone::Flat)
{
- state.place_stone(*player, pos, *stone)
+ state.place_stone(*player, pos, *stone, Some(self.start_type))
} else {
- Err(String::from(
- "At the start of the game, B must place a W flat.",
+ Err(format!(
+ "At the start of the game in {}, B must place a W flat.",
+ self.start_type
))
}
}
- TurnOrder::Normal => state.place_stone(*player, pos, *stone),
+ TurnOrder::Normal => state.place_stone(*player, pos, *stone, None),
},
Action::Move(pos, direction, picked_up, drops) => match self.turn_order {
TurnOrder::Normal => {
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.",
+ _ => Err(format!(
+ "At the start of the game in {} only placing flats is allowed.",
+ self.start_type
)),
},
};
@@ -849,20 +944,11 @@ impl Game {
Ok((pos_vec, new_state)) => {
self.states.push(new_state);
self.actions.push(act);
- self.current_player = match self.turn_order {
- TurnOrder::WhitePlacesBlack => Player::Black,
- TurnOrder::BlackPlacesWhite => Player::White,
- TurnOrder::Normal => match self.current_player {
- Player::Black => Player::White,
- Player::White => Player::Black,
- },
- };
-
- self.turn_order = match self.turn_order {
- TurnOrder::WhitePlacesBlack => TurnOrder::BlackPlacesWhite,
- TurnOrder::BlackPlacesWhite => TurnOrder::Normal,
- TurnOrder::Normal => TurnOrder::Normal,
- };
+ let (current_player, turn_order) =
+ self.start_type.player_turnorder_at(self.states.len());
+ self.current_player = current_player;
+ self.turn_order = turn_order;
+ // This is safe as we have just added to the states vector
self.win_type = self.last_state().check_win();
return Ok((pos_vec, self.win_type));
}
@@ -873,15 +959,10 @@ impl Game {
if self.actions.len() > 0 {
self.actions.pop();
self.states.pop();
- self.turn_order = match self.actions.len() {
- 0 => TurnOrder::WhitePlacesBlack,
- 1 => TurnOrder::BlackPlacesWhite,
- _ => TurnOrder::Normal,
- };
- self.current_player = match self.current_player {
- Player::Black => Player::White,
- Player::White => Player::Black,
- };
+ let (current_player, turn_order) =
+ self.start_type.player_turnorder_at(self.states.len());
+ self.turn_order = turn_order;
+ self.current_player = current_player;
// I'm too lazy to work out exactly which squares must be
// redrawn when an action is undone
let mut redraw_all: Vec<Position> = Vec::new();
@@ -901,11 +982,12 @@ 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 \"{}\"][Start \"{}\"]\n{}",
self.date_string,
self.white_player_name,
self.black_player_name,
self.size,
+ self.start_type,
self.query_action_lines().join("\n"),
)
}