/// A Robot *aka droid* is represented here. /// Each robot must have a unique id. use rand::{ distributions::{Distribution, Standard}, Rng, }; pub struct Robot { pub id: u32, pub o: Orientation, pub p: Position, i: Vec, } impl Robot { /// Create new `Robot` with given id, `Orientation`, `Position` and instructions. pub fn new(id: u32, o: Orientation, p: Position, i: Vec) -> Robot { Robot { id, o, p, i } } /// Apply given instruction to a `Robot`. pub fn execute_instruction(&mut self) { match self.i.pop() { Some(instruction) => match instruction { 'L' => match self.o { Orientation::N => self.o = Orientation::W, Orientation::E => self.o = Orientation::N, Orientation::S => self.o = Orientation::E, Orientation::W => self.o = Orientation::S, }, 'R' => match self.o { Orientation::N => self.o = Orientation::E, Orientation::E => self.o = Orientation::S, Orientation::S => self.o = Orientation::W, Orientation::W => self.o = Orientation::N, }, '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, }, _ => (), // never happens 😉 }, None => (), } } } /// Enum to store all possible orientations. #[derive(Debug)] pub enum Orientation { N, E, S, W, } impl Distribution for Standard { fn sample(&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, } /// Check if instructions list is valid. pub fn is_instructions(v: &Vec) -> bool { for c in v { match c { 'F' => continue, 'R' => continue, 'L' => continue, _ => return false, } } true } /// Check if a robot is piouff. pub fn is_piouff(r: &Robot) -> bool { if r.i.len() == 0 { return true; } false } /// Print robots id, position and instructions. pub fn print_robots(robot_pool: &Vec) { println!("Robots ["); for r in robot_pool { println!( "{{ id = {}, x = {}; y = {}; orientation: {}, instructions: {:?}, }},", 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 ); } println!("]"); } #[cfg(test)] mod tests { use super::*; use std::any::type_name; fn type_of(_: T) -> &'static str { type_name::() } #[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!['L', 'L', 'F', '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, vec!['L', 'L', 'F', 'R']); } #[test] fn test_execute_instruction() { let mut r: Robot = Robot::new( 0, Orientation::N, Position { x: 1, y: 2 }, vec!['R', 'F', 'L', '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 is_instructions_test() { let v = vec!['F', 'R', 'L', 'F']; assert!(is_instructions(&v)); } #[test] #[should_panic] fn is_instructions_test_fail() { let v = vec!['F', 'R', 'L', 'Z']; assert!(is_instructions(&v)); } #[test] fn test_piouf() { let mut r: Robot = Robot::new( 0, Orientation::N, Position { x: 1, y: 2 }, vec!['R', 'F', 'L', 'F'], ); r.i.pop(); r.i.pop(); r.i.pop(); r.i.pop(); assert!(is_piouff(&r)); } }