use crate::game::*; fn parse_move(mv: &str) -> Result { let mut chars = mv.chars(); let c = chars.next(); if c.is_none() { return Err(String::from("Empty string cannot be parsed.")); } let x; let picked_up; let dc = c.unwrap(); if (dc >= 'a') && (dc <= 'h') { // Optional drop number omitted, assume everything 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 stones picked up.", )); } picked_up = dc.to_digit(10).unwrap() as u8; let c = chars.next(); if c.is_none() { return Err(String::from("Moves must specify a stack position.")); } x = c.unwrap(); } if (x < 'a') || (x > 'h') { return Err(String::from( "Moves must specify a valid position on the game board.", )); } let x: u8 = x as u8 - 'a' as u8; let c = chars.next(); if c.is_none() { return Err(String::from("Moves must specify a stack position.")); } let y = c.unwrap(); if (y < '1') || (y > '8') { return Err(String::from( "Moves must specify a position on the game board.", )); } let y: u8 = (y.to_digit(10).unwrap() - 1) as u8; let c = chars.next(); if c.is_none() { return Err(String::from("Moves must specify a move direction.")); } let c = c.unwrap(); // This is so cool! Rust supports some very nice patterns, and // checks for uninitialised variables too! let dir; match c { '+' => dir = Direction::Up, '-' => dir = Direction::Down, '<' => dir = Direction::Left, '>' => dir = Direction::Right, _ => return Err(String::from("The valid directions are +,-,<, and >.")), } let mut drops: Vec = Vec::new(); for d in chars { if let Some(u) = d.to_digit(10) { if u > 9 { return Err(String::from( "No more than 8 pieces may be dropped on a given square.", )); } else { drops.push(u as u8) } } else { return Err(String::from( "A move must specify a number of pieces dropped for each square.", )); } } let pos = Position { x: x, y: y }; let sum = drops.iter().map(|&d| d as u32).sum::(); if sum == 0 { // Omitted all the drops, it's a total stack move drops.push(picked_up); } else if sum != picked_up as u32 { return Err(format!( "The move called for {} stones, but {} were dropped.", picked_up, sum )); } Ok(Action::Move(pos, dir, picked_up, drops)) } fn parse_place(pl: &str, stone_owner: Player) -> Result { let mut chars = pl.chars(); let c = chars.next(); if c.is_none() { return Err(String::from("An action cannot be blank.")); } let stone_c = c.unwrap(); let stone; let x; if (stone_c == 'F') || (stone_c == 'C') || (stone_c == 'S') { match stone_c { 'C' => stone = Stone::Capstone, 'S' => stone = Stone::Standing, _ => stone = Stone::Flat, } let c = chars.next(); if c.is_none() { return Err(String::from( "Placements must specify a position on the game board.", )); } x = c.unwrap(); } else { stone = Stone::Flat; x = stone_c; } if (x < 'a') || (x > 'h') { return Err(if (x >= 'A') && (x <= 'Z') { format!("Unrecognised stone type '{}' in placement.", x) } else { String::from("Placements must specify a valid position on the game board.") }); } let x: u8 = x as u8 - 'a' as u8; let c = chars.next(); if c.is_none() { return Err(String::from( "Placements must specify a position on the game board.", )); } let y = c.unwrap(); if (y < '1') || (y > '8') { return Err(String::from( "Placements must specify a valid position on the game board.", )); } let y: u8 = (y.to_digit(10).unwrap() - 1) as u8; Ok(Action::Place(stone_owner, Position { x: x, y: y }, stone)) } pub fn parse_action(stone_owner: Player, act: &str) -> Result { if act .chars() .any(|c| c == '+' || c == '-' || c == '>' || c == '<') { parse_move(act) } else { parse_place(act, stone_owner) } } pub fn parse_start_type(st: &str) -> Result { let st = st.trim().to_uppercase(); if st == StartType::FCS.to_string() { return Ok(StartType::FCS); }; if st == StartType::FES.to_string() { return Ok(StartType::FES); }; if st == StartType::TPS.to_string() { return Ok(StartType::TPS); }; if st == StartType::CZS.to_string() { return Ok(StartType::CZS); }; if st.len() < 4 { return Err(String::from( "Expecting CPSn as alternatives failed to match, but variant string was too short.", )); } else { if st[..3] == String::from("CPS") { let num = st[3..].parse::(); match num { Err(e) => { return Err(format!( "Failed to parse number, {}. CPS must be immediately followed by a positive integer < 256, e.g., CPS1.", e.to_string() )) } Ok(n) => return Ok(StartType::CPS(n)), } } } return Err(format!("Unknown start type ``{}''", st)); }