use rand::{ distributions::{Distribution, Standard}, Rng, }; /// A Robot *aka droid* is represented here. /// Each robot must have a unique id. pub struct Robot { pub id: u32, pub o: Orientation, pub p: Position, pub i: Vec<Instruction>, } impl Robot { /// Create new `Robot` with given id, `Orientation`, `Position` and instructions. pub fn new(id: u32, o: Orientation, p: Position, i: Vec<Instruction>) -> Robot { Robot { id, o, p, i } } /// Create new random `Robot`. pub fn new_random(id: u32, posx_max: i32, posy_max: i32) -> Result<Robot, String> { let mut rng = rand::thread_rng(); let x = rng.gen_range(2, posx_max); let y = rng.gen_range(2, posy_max); let inst = gen_random_instructions(); let instructions: Vec<Instruction> = instructions_from_string(inst)?; let o: Orientation = rand::random(); Ok(Robot { id, o, p: Position { x, y }, i: instructions, }) } /// Apply given instruction to a `Robot`. pub fn execute_instruction(&mut self) { match self.i.pop() { Some(instruction) => match instruction { Instruction::L => self.o = turn_left(&self.o), Instruction::R => self.o = turn_right(&self.o), Instruction::F => match self.o { Orientation::N => self.p.y += 1, Orientation::E => self.p.x += 1, Orientation::S => self.p.y -= 1, Orientation::W => self.p.x -= 1, }, }, None => (), } } } /// Enum to store all possible orientations. #[derive(Debug)] pub enum Orientation { N, E, S, W, } fn turn_left(o: &Orientation) -> Orientation { match o { Orientation::N => Orientation::W, Orientation::E => Orientation::N, Orientation::S => Orientation::E, Orientation::W => Orientation::S, } } fn turn_right(o: &Orientation) -> Orientation { match o { Orientation::N => Orientation::E, Orientation::E => Orientation::S, Orientation::S => Orientation::W, Orientation::W => Orientation::N, } } /// Enum to store all possible instructions. #[derive(Debug, Eq, PartialEq)] pub enum Instruction { L, R, F, } pub fn instructions_from_string(s: String) -> Result<Vec<Instruction>, String> { let mut v: Vec<Instruction> = Vec::new(); for c in s.chars() { match c { 'L' => v.push(Instruction::L), 'R' => v.push(Instruction::R), 'F' => v.push(Instruction::F), _ => return Err(String::from("Not an instruction.")), } } Ok(v) } impl Distribution<Orientation> for Standard { /// Generating random orientation. fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Orientation { match rng.gen_range(0, 3) { 0 => Orientation::N, 1 => Orientation::E, 2 => Orientation::S, _ => Orientation::W, } } } /// Struct to store robot position. #[derive(PartialEq, Eq, Hash)] pub struct Position { pub x: i32, pub y: i32, } /// Generate random instructions. pub fn gen_random_instructions() -> String { let mut rng = rand::thread_rng(); let n = rng.gen_range(5, 10); let mut instructions = String::with_capacity(n); const CHARSET: &[u8] = b"LRF"; for _ in 0..n { let l = rng.gen_range(0, CHARSET.len()); instructions.push(CHARSET[l] as char); } instructions } /// Check if a robot is piouff. pub fn is_piouff(r: &Robot) -> bool { r.i.len() == 0 } /// Print robots id, position and instructions. pub fn print_robots(robot_pool: &Vec<Robot>) -> String { let mut res = format!("Robots [\n"); for r in robot_pool { res = format!( "{}{{ id = {}, x = {}; y = {}; orientation: {}, instructions: {:?}, }},\n", res, r.id, r.p.x, r.p.y, match r.o { Orientation::N => "Norts", Orientation::S => "South", Orientation::E => "East", Orientation::W => "West", }, r.i ); } res = format!("{}]\n", res); res } #[cfg(test)] mod tests { use super::*; use std::any::type_name; fn type_of<T>(_: T) -> &'static str { type_name::<T>() } #[test] fn test_rand_orientation() { let o: Orientation = rand::random(); assert_eq!(type_of(o), "dancing_droid::robot::Orientation"); } #[test] fn test_new_robot() { let r: Robot = Robot::new( 0, Orientation::N, Position { x: 1, y: 2 }, vec![Instruction::L, Instruction::F, Instruction::R], ); assert_eq!(r.id, 0); assert!(matches!(r.o, Orientation::N)); assert_eq!(r.p.x, 1); assert_eq!(r.p.y, 2); assert_eq!(r.i[0], Instruction::L); assert_eq!(r.i[1], Instruction::F); assert_eq!(r.i[2], Instruction::R); } #[test] fn test_execute_instruction() { let mut r: Robot = Robot::new( 0, Orientation::N, Position { x: 1, y: 2 }, vec![ Instruction::R, Instruction::F, Instruction::L, Instruction::F, ], ); let mut hash = std::collections::HashMap::new(); //hash.insert(&r.p, &r.id); // first insert while initializing. hash.remove(&r.p); // remove before execute_instruction(). r.execute_instruction(); hash.insert(&r.p, &r.id); // second insert after moving. assert_eq!(r.p.x, 1); assert_eq!(r.p.y, 3); r.execute_instruction(); r.execute_instruction(); assert_eq!(r.p.x, 0); assert_eq!(r.p.y, 3); } #[test] fn test_piouf() { let mut r: Robot = Robot::new( 0, Orientation::N, Position { x: 1, y: 2 }, vec![ Instruction::R, Instruction::F, Instruction::L, Instruction::F, ], ); r.i.pop(); r.i.pop(); r.i.pop(); r.i.pop(); assert!(is_piouff(&r)); } }