From e4331a6523900a717fd21a2a6efc9dfc9ae14512 Mon Sep 17 00:00:00 2001 From: Volodymyr Patuta <6977238-fiplox@users.noreply.gitlab.com> Date: Sun, 8 Nov 2020 16:26:20 +0100 Subject: [PATCH 1/5] move from Vec to Vec --- src/main.rs | 12 +++---- src/robot.rs | 91 +++++++++++++++++++++++++++++++++++++--------------- 2 files changed, 71 insertions(+), 32 deletions(-) diff --git a/src/main.rs b/src/main.rs index c1bba05..a3e574e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -124,12 +124,12 @@ fn parse_config(conf: String, pool: &mut Vec) -> Result = instructions.chars().rev().collect(); - if !robot::is_instructions(&inst) { + if !robot::is_instructions(instructions) { return Err(String::from("Invalid instructions !")); } + let inst = robot::instructions_from_string(instructions.chars().rev().collect::())?; + let r = robot::Robot::new(r_id, r_o, robot::Position { x: r_x, y: r_y }, inst); // Load robot inside the pool. match check_map(&r, &world) { @@ -284,7 +284,7 @@ mod tests { 0, robot::Orientation::N, robot::Position { x: 2, y: 3 }, - vec!['F'], + vec![robot::Instruction::F], ); let w = world::World { x: 10, y: 10 }; @@ -298,7 +298,7 @@ mod tests { 0, robot::Orientation::N, robot::Position { x: 2, y: 4 }, - vec!['F'], + vec![robot::Instruction::F], ); let w = world::World { x: 3, y: 3 }; @@ -312,7 +312,7 @@ mod tests { 0, robot::Orientation::N, robot::Position { x: 2, y: 3 }, - vec!['F'], + vec![robot::Instruction::F], ); let mut h: HashMap = HashMap::new(); diff --git a/src/robot.rs b/src/robot.rs index 8d7654b..6d12f87 100644 --- a/src/robot.rs +++ b/src/robot.rs @@ -9,37 +9,26 @@ pub struct Robot { pub id: u32, pub o: Orientation, pub p: Position, - i: Vec, + 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 { + 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 { + 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, }, - _ => (), // never happens 😉 }, None => (), } @@ -55,6 +44,44 @@ pub enum Orientation { 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, String> { + let mut v: Vec = 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 for Standard { fn sample(&self, rng: &mut R) -> Orientation { match rng.gen_range(0, 3) { @@ -74,8 +101,8 @@ pub struct Position { } /// Check if instructions list is valid. -pub fn is_instructions(v: &Vec) -> bool { - for c in v { +pub fn is_instructions(s: &str) -> bool { + for c in s.chars() { match c { 'F' => continue, 'R' => continue, @@ -135,13 +162,15 @@ mod tests { 0, Orientation::N, Position { x: 1, y: 2 }, - vec!['L', 'L', 'F', 'R'], + 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, vec!['L', 'L', 'F', 'R']); + assert_eq!(r.i[0], Instruction::L); + assert_eq!(r.i[1], Instruction::F); + assert_eq!(r.i[2], Instruction::R); } #[test] @@ -150,7 +179,12 @@ mod tests { 0, Orientation::N, Position { x: 1, y: 2 }, - vec!['R', 'F', 'L', 'F'], + 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. @@ -167,15 +201,15 @@ mod tests { #[test] fn is_instructions_test() { - let v = vec!['F', 'R', 'L', 'F']; - assert!(is_instructions(&v)); + let s = "FFLRLRF"; + assert!(is_instructions(&s)); } #[test] #[should_panic] fn is_instructions_test_fail() { - let v = vec!['F', 'R', 'L', 'Z']; - assert!(is_instructions(&v)); + let s = "SZDASWQ"; + assert!(is_instructions(&s)); } #[test] @@ -184,7 +218,12 @@ mod tests { 0, Orientation::N, Position { x: 1, y: 2 }, - vec!['R', 'F', 'L', 'F'], + vec![ + Instruction::R, + Instruction::F, + Instruction::L, + Instruction::F, + ], ); r.i.pop(); r.i.pop(); From 6e557051840f859de12b250945ec75908d20228e Mon Sep 17 00:00:00 2001 From: Volodymyr Patuta <6977238-fiplox@users.noreply.gitlab.com> Date: Sun, 8 Nov 2020 19:17:57 +0100 Subject: [PATCH 2/5] rework parser --- conf.pest | 8 ++-- src/main.rs | 132 ++++++++++++++++++++++++--------------------------- src/robot.rs | 2 +- 3 files changed, 69 insertions(+), 73 deletions(-) diff --git a/conf.pest b/conf.pest index 3b8810b..5ed23cc 100644 --- a/conf.pest +++ b/conf.pest @@ -1,3 +1,5 @@ -world = { ASCII_DIGIT+ ~ " " ~ ASCII_DIGIT+ } -robot_init = { ASCII_DIGIT+ ~ " " ~ ASCII_DIGIT+ ~ " " ~ ("S" | "N" | "W" | "E") } -robot_instructions = { ASCII_ALPHA_UPPER+ } +World = { HeaderWorld ~ NEWLINE+ ~ Robot+ } +HeaderWorld = { ASCII_DIGIT+ ~ " " ~ ASCII_DIGIT+ } +Robot = { HeaderRobot ~ NEWLINE ~ Instructions ~ NEWLINE+ } +HeaderRobot = { ASCII_DIGIT+ ~ " " ~ ASCII_DIGIT+ ~ " " ~ ("S" | "N" | "W" | "E") } +Instructions = { ("F" | "L" | "R")+ } diff --git a/src/main.rs b/src/main.rs index a3e574e..68460dc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -75,77 +75,71 @@ fn gen_random_instructions() -> String { /// Parse the config file, generate the world and robot pool. fn parse_config(conf: String, pool: &mut Vec) -> Result { - let mut lines: Vec<&str> = conf.split('\n').collect(); - let raw_world = match ConfParser::parse(Rule::world, lines.remove(0)) { - Ok(s) => s.as_str(), - Err(_) => return Err(String::from("World config is broken.")), + let pairs = match ConfParser::parse(Rule::World, &conf) { + Ok(p) => p, + Err(_) => return Err(String::from("Config is broken.")), }; - let mut w: Vec = Vec::with_capacity(2); - for n in raw_world.split_whitespace() { - let v: i32 = n.parse::().unwrap(); - w.push(v); + + let mut world = world::World { x: 0, y: 0 }; + let mut id = 1; + for pair in pairs { + for inner in pair.into_inner() { + match inner.as_rule() { + Rule::HeaderWorld => { + let mut w: Vec = Vec::with_capacity(2); + for n in inner.as_str().split_whitespace() { + let v: i32 = n.parse::().unwrap(); + w.push(v); + } + world = world::World { x: w[0], y: w[1] }; + } + Rule::Robot => { + let mut vpos: Vec = Vec::new(); + let mut vor: Vec = Vec::new(); + let mut vinst: Vec> = Vec::new(); + for inner_robot in inner.into_inner() { + match inner_robot.as_rule() { + Rule::HeaderRobot => { + let mut setup = inner_robot.as_str().split_whitespace(); + let rx = setup.next().unwrap(); + let ry = setup.next().unwrap(); + let ro = setup.next().unwrap(); + vpos.push(robot::Position { + x: rx.parse::().unwrap(), + y: ry.parse::().unwrap(), + }); + vor.push(match ro { + "N" => robot::Orientation::N, + "E" => robot::Orientation::E, + "S" => robot::Orientation::S, + _ => robot::Orientation::W, + }) + } + Rule::Instructions => { + let instructions = inner_robot.as_str(); + vinst.push(robot::instructions_from_string( + instructions.chars().rev().collect::(), + )?); + let r = robot::Robot::new( + id, + vor.pop().unwrap(), + vpos.pop().unwrap(), + vinst.pop().unwrap(), + ); + match check_map(&r, &world) { + Ok(()) => pool.push(r), + Err(err) => return Err(err), + } + id += 1; + } + _ => unreachable!(), + } + } + } + _ => unreachable!(), + }; + } } - let world = world::World { x: w[0], y: w[1] }; - lines.remove(0); - let mut r_id: u32 = 0; - loop { - r_id += 1; - if lines.len() == 0 { - break; - } - let raw_setup = match ConfParser::parse(Rule::robot_init, lines.remove(0)) { - Ok(s) => s.as_str(), - Err(_) => return Err(String::from("Robot setup is broken.")), - }; - - let rand_instructions = gen_random_instructions(); - let l = lines.remove(0); - - let instructions = match ConfParser::parse(Rule::robot_instructions, l) { - Ok(s) => s.as_str(), - Err(_) => rand_instructions.as_str(), - }; - - let mut setup = raw_setup.split_whitespace(); - let pos_x = setup.next().unwrap(); - let pos_y = setup.next().unwrap(); - let orientation = setup.next().unwrap(); - // Convert values of the setup line - let r_x = pos_x.parse::().unwrap(); - let r_y = pos_y.parse::().unwrap(); - let r_o = match orientation { - "N" => robot::Orientation::N, - "E" => robot::Orientation::E, - "S" => robot::Orientation::S, - "W" => robot::Orientation::W, - _ => { - return Err(String::from( - "The third token of the setup line do not match any orientations !", - )) - } - }; - if !robot::is_instructions(instructions) { - return Err(String::from("Invalid instructions !")); - } - - let inst = robot::instructions_from_string(instructions.chars().rev().collect::())?; - - let r = robot::Robot::new(r_id, r_o, robot::Position { x: r_x, y: r_y }, inst); - // Load robot inside the pool. - match check_map(&r, &world) { - Ok(()) => pool.push(r), - Err(err) => return Err(err), - } - - if lines.len() == 0 { - break; - } - if l.len() == 0 { - continue; - } - lines.remove(0); - } - Ok(world) } diff --git a/src/robot.rs b/src/robot.rs index 6d12f87..79c9877 100644 --- a/src/robot.rs +++ b/src/robot.rs @@ -9,7 +9,7 @@ pub struct Robot { pub id: u32, pub o: Orientation, pub p: Position, - i: Vec, + pub i: Vec, } impl Robot { From 68a23bafb9b7bfc493b3c207f07f17d44d409838 Mon Sep 17 00:00:00 2001 From: Volodymyr Patuta <6977238-fiplox@users.noreply.gitlab.com> Date: Sun, 8 Nov 2020 20:32:23 +0100 Subject: [PATCH 3/5] 0.3.1 --- src/main.rs | 41 +++++++++++++++++++++++++---------------- src/robot.rs | 36 ++++++++++++++++++++++++++---------- 2 files changed, 51 insertions(+), 26 deletions(-) diff --git a/src/main.rs b/src/main.rs index 68460dc..843f0bd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -59,18 +59,15 @@ fn create_hash_map(pool: &Vec, hash: &mut HashMap String { +fn gen_world(pool: &mut Vec) -> Result { + let w = world::random_world(); 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); + let x = rng.gen_range(2, 10); + for i in 0..=x { + let r = robot::Robot::new_random(i + 1, w.x - 2, w.y - 2)?; + pool.push(r); } - - instructions + Ok(w) } /// Parse the config file, generate the world and robot pool. @@ -207,15 +204,27 @@ fn main() -> Result<(), Box> { .takes_value(false) .help("Generate random world"), ) + .arg( + Arg::with_name("random") + .short("r") + .long("random") + .takes_value(false) + .help("Generate random world"), + ) .get_matches(); - let raw_conf = open_file(matches.value_of("file").unwrap_or("two_robots.txt"))?; - let mut robot_pool: Vec = Vec::new(); - let mut world: world::World = parse_config(raw_conf, &mut robot_pool)?; - world = match matches.is_present("random-world") { - false => world, - true => world::random_world(), + let world = match matches.is_present("random") { + true => gen_world(&mut robot_pool)?, + false => { + let raw_conf = open_file(matches.value_of("file").unwrap_or("two_robots.txt"))?; + let mut world: world::World = parse_config(raw_conf, &mut robot_pool)?; + world = match matches.is_present("random-world") { + false => world, + true => world::random_world(), + }; + world + } }; let mut hash: HashMap = HashMap::new(); diff --git a/src/robot.rs b/src/robot.rs index 79c9877..d8c37e2 100644 --- a/src/robot.rs +++ b/src/robot.rs @@ -17,6 +17,21 @@ impl Robot { pub fn new(id: u32, o: Orientation, p: Position, i: Vec) -> Robot { Robot { id, o, p, i } } + /// Create new random `Robot`. + pub fn new_random(id: u32, posx_max: i32, posy_max: i32) -> Result { + 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 = 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() { @@ -100,17 +115,18 @@ pub struct Position { pub y: i32, } -/// Check if instructions list is valid. -pub fn is_instructions(s: &str) -> bool { - for c in s.chars() { - match c { - 'F' => continue, - 'R' => continue, - 'L' => continue, - _ => return false, - } +/// Generate random instructions. +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); } - true + + instructions } /// Check if a robot is piouff. From 72097bc7028bd6a65e56dceb477e1f32dac7bb5a Mon Sep 17 00:00:00 2001 From: Volodymyr Patuta <6977238-fiplox@users.noreply.gitlab.com> Date: Sun, 8 Nov 2020 20:37:21 +0100 Subject: [PATCH 4/5] removed tests --- src/robot.rs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/robot.rs b/src/robot.rs index d8c37e2..33d787d 100644 --- a/src/robot.rs +++ b/src/robot.rs @@ -215,19 +215,6 @@ mod tests { assert_eq!(r.p.y, 3); } - #[test] - fn is_instructions_test() { - let s = "FFLRLRF"; - assert!(is_instructions(&s)); - } - - #[test] - #[should_panic] - fn is_instructions_test_fail() { - let s = "SZDASWQ"; - assert!(is_instructions(&s)); - } - #[test] fn test_piouf() { let mut r: Robot = Robot::new( From 4414478771dd53c9d682a93cffe919db28ef3a56 Mon Sep 17 00:00:00 2001 From: Volodymyr Patuta <6977238-fiplox@users.noreply.gitlab.com> Date: Sun, 8 Nov 2020 20:54:59 +0100 Subject: [PATCH 5/5] working 0.3.0 --- conf.pest | 2 +- src/main.rs | 15 +++++++++++---- src/robot.rs | 2 +- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/conf.pest b/conf.pest index 5ed23cc..cdad2d5 100644 --- a/conf.pest +++ b/conf.pest @@ -2,4 +2,4 @@ World = { HeaderWorld ~ NEWLINE+ ~ Robot+ } HeaderWorld = { ASCII_DIGIT+ ~ " " ~ ASCII_DIGIT+ } Robot = { HeaderRobot ~ NEWLINE ~ Instructions ~ NEWLINE+ } HeaderRobot = { ASCII_DIGIT+ ~ " " ~ ASCII_DIGIT+ ~ " " ~ ("S" | "N" | "W" | "E") } -Instructions = { ("F" | "L" | "R")+ } +Instructions = { (("F" | "L" | "R")+) | "" } diff --git a/src/main.rs b/src/main.rs index 843f0bd..aabe45f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -113,10 +113,17 @@ fn parse_config(conf: String, pool: &mut Vec) -> Result { - let instructions = inner_robot.as_str(); - vinst.push(robot::instructions_from_string( - instructions.chars().rev().collect::(), - )?); + if !(inner_robot.as_str() == "") { + let instructions = inner_robot.as_str(); + vinst.push(robot::instructions_from_string( + instructions.chars().rev().collect::(), + )?); + } else { + let instructions = robot::gen_random_instructions(); + vinst.push(robot::instructions_from_string( + instructions.chars().rev().collect::(), + )?); + } let r = robot::Robot::new( id, vor.pop().unwrap(), diff --git a/src/robot.rs b/src/robot.rs index 33d787d..e48f571 100644 --- a/src/robot.rs +++ b/src/robot.rs @@ -116,7 +116,7 @@ pub struct Position { } /// Generate random instructions. -fn gen_random_instructions() -> String { +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);