From 84898338d0875c258a4325d517dd7b7f9d0f83e1 Mon Sep 17 00:00:00 2001 From: tslil clingman <> Date: Sun, 9 Feb 2020 13:42:15 -0800 Subject: Implemented variant start types, no check for bugfree yet --- src/game.rs | 172 ++++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 127 insertions(+), 45 deletions(-) (limited to 'src/game.rs') 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, + ) -> 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, ) -> Result<(Vec, 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, @@ -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 = 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"), ) } -- cgit v1.3.1