use clap::{App, Arg}; use queues::*; use std::fs; use std::io; /// Struct to store the world. struct World { x: u32, y: u32, map: Vec, } impl World { /// Create a new map full of dots. fn create(&mut self) { self.map = vec!['.'; (self.x * self.y) as usize]; } /// Set robot on the map. fn set_robot(&mut self, r: Robot) { self.map[(r.p.x * r.p.y) as usize] = match r.o { Orientation::N => '↑', Orientation::S => '↓', Orientation::E => '→', Orientation::W => '←', } } /// Check if a position is free. fn empty_position(&mut self, p: Position) -> bool { self.map[(p.x * p.y) as usize] == '.' } } /// Struct to store robot position. struct Position { x: i32, y: i32, } impl Position { /// Check if position is in the map. fn is_valid(self, w: World) -> Result<(), String> { if (self.x, self.y) > (0, 0) && (self.x, self.y) < (w.x as i32, w.y as i32) { Ok(()) } else { Err(String::from("Invalid position.")) } } } /// A Robot *aka droid* is represented here. /// Each robot must have a unique id. struct Robot { id: u32, o: Orientation, p: Position, q: Queue, } impl Robot { /// Create a empty Robot. fn new() -> Robot { Robot { id: 0, o: Orientation::N, p: Position { x: 0, y: 0 }, q: queue![], } } /// Initialize a new Robot. fn create(&mut self, id: u32, o: Orientation, p: Position, s: String) { let mut q: Queue = queue![]; for c in s.chars() { q.add(c); } self.id = id; self.o = o; self.p = p; self.q = q; } /// Apply forward instruction to the robot. fn move_forward(&mut self) { match self.o { Orientation::N => self.p.y -= 1, Orientation::S => self.p.y += 1, Orientation::E => self.p.x += 1, Orientation::W => self.p.x -= 1, } } /// Apply right instruction to the robot. fn move_right(&mut self) { match self.o { Orientation::N => self.p.x += 1, Orientation::S => self.p.x -= 1, Orientation::E => self.p.y += 1, Orientation::W => self.p.y -= 1, } } /// Apply left instruction to the robot. fn move_left(&mut self) { match self.o { Orientation::N => self.p.x -= 1, Orientation::S => self.p.x += 1, Orientation::E => self.p.y -= 1, Orientation::W => self.p.y += 1, } } /// Apply North orientation to the robot. fn set_north(&mut self) { self.o = Orientation::N } /// Apply South orientation to the robot. fn set_south(&mut self) { self.o = Orientation::S } /// Apply East orientation to the robot. fn set_east(&mut self) { self.o = Orientation::E } /// Apply West orientation to the robot. fn set_west(&mut self) { self.o = Orientation::W } } /// Enum to store all possible orientations. enum Orientation { N, E, S, W, } /// Enum to store all possible instructions. enum Instruction { L, R, F, } /// Parse char and return corresponding orientation. fn parse_orientation(c: char) -> Result { match c { 'N' => Ok(Orientation::N), 'E' => Ok(Orientation::E), 'S' => Ok(Orientation::S), 'W' => Ok(Orientation::W), _ => Err("Invalid character, does not match any orientations"), } } /// Parse char and return corresponding instruction. fn parse_instruction(c: char) -> Result { match c { 'L' => Ok(Instruction::L), 'R' => Ok(Instruction::R), 'F' => Ok(Instruction::F), _ => Err("Invalid character, does not match any instructions"), } } /// Retrieve the content of a file and return it as a string. fn open_file(filename: &str) -> io::Result { let content = fs::read_to_string(filename)?; Ok(content) } fn main() -> Result<(), Box> { // We handle CLI flags here. let matches = App::new("DancingDroids") .version("0.1.0") .about("When droids dance togethers") .arg( Arg::with_name("file") .short("f") .long("file") .takes_value(true) .help("Configuration file"), ) .get_matches(); let raw_conf = open_file(matches.value_of("file").unwrap_or("two_robots.txt"))?; // We store all the robots inside a pool O_o let mut robot_pool: Vec = Vec::new(); Ok(()) } #[cfg(test)] mod tests { use super::*; #[test] fn test_parse_orientation() { assert!(parse_orientation('N').is_ok()); assert!(parse_orientation('E').is_ok()); assert!(parse_orientation('S').is_ok()); assert!(parse_orientation('W').is_ok()); assert!(parse_orientation('Z').is_err()); } #[test] fn test_parse_instruction() { assert!(parse_instruction('L').is_ok()); assert!(parse_instruction('R').is_ok()); assert!(parse_instruction('F').is_ok()); assert!(parse_instruction('Z').is_err()); } #[test] fn test_open_file() { assert!(open_file("two_robots.txt").is_ok()); assert!(open_file("test_unexisting_file.extension").is_err()); } #[test] fn test_create() { let mut w: World = World { x: 5, y: 5, map: Vec::new(), }; w.create(); assert_eq!(w.map, vec!['.'; 25]); } #[test] fn test_new_robot() { let mut r: Robot = Robot::new(); assert_eq!(r.id, 0); assert!(matches!(r.o, Orientation::N)); assert_eq!(r.p.x, 0); assert_eq!(r.p.y, 0); assert_eq!(r.q.size(), 0); } #[test] fn test_create_robot() { let mut r: Robot = Robot::new(); r.create(1, Orientation::N, Position { x: 1, y: 1 }, "FF".to_string()); assert_eq!(r.id, 1); assert!(matches!(r.o, Orientation::N)); assert_eq!(r.p.x, 1); assert_eq!(r.p.y, 1); assert_eq!(r.q.peek(), Ok('F')); assert_eq!(r.q.peek(), Ok('F')); } #[test] fn test_set_robot() { let r = Robot { id: 0, o: Orientation::N, p: Position { x: 0, y: 0 }, q: Queue::new(), }; let mut w: World = World { x: 2, y: 2, map: Vec::new(), }; w.create(); w.set_robot(r); assert_eq!(w.map, vec!['↑', '.', '.', '.']); } #[test] fn test_move_robot() { let mut r: Robot = Robot { id: 0, o: Orientation::N, p: Position { x: 1, y: 1 }, q: Queue::new(), }; r.move_forward(); assert_eq!(0, r.p.y); r.move_right(); assert_eq!(2, r.p.x); r.move_left(); assert_eq!(1, r.p.x); } #[test] fn test_empty_position() { let mut w: World = World { x: 2, y: 2, map: Vec::new(), }; w.create(); assert!(w.empty_position(Position { x: 0, y: 0 })); } }