new paser + generate random instructions
This commit is contained in:
		
							parent
							
								
									f424f66650
								
							
						
					
					
						commit
						3f9e231ebf
					
				| @ -9,3 +9,5 @@ edition = "2018" | ||||
| [dependencies] | ||||
| clap = "2.33.3" | ||||
| rand = "0.7.3" | ||||
| pest = "2.0" | ||||
| pest_derive = "2.0" | ||||
|  | ||||
							
								
								
									
										3
									
								
								src/conf.pest
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/conf.pest
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| world = { ASCII_DIGIT* ~ " " ~ ASCII_DIGIT* } | ||||
| robot_init = { ASCII_DIGIT* ~ " " ~ ASCII_DIGIT* ~ " " ~ ("S" | "N" | "W" | "E") } | ||||
| robot_instructions = { ASCII_ALPHA_UPPER+ } | ||||
							
								
								
									
										138
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										138
									
								
								src/main.rs
									
									
									
									
									
								
							| @ -13,7 +13,13 @@ | ||||
| // You should have received a copy of the GNU General Public License
 | ||||
| // along with this program. If not, see <http://www.gnu.org/licenses/>.
 | ||||
| 
 | ||||
| extern crate pest; | ||||
| #[macro_use] | ||||
| extern crate pest_derive; | ||||
| 
 | ||||
| use clap::{App, Arg}; | ||||
| use pest::Parser; | ||||
| use rand::Rng; | ||||
| use std::collections::HashMap; | ||||
| use std::fs; | ||||
| use std::io; | ||||
| @ -21,6 +27,10 @@ use std::io; | ||||
| mod robot; | ||||
| mod world; | ||||
| 
 | ||||
| #[derive(Parser)] | ||||
| #[grammar = "conf.pest"] | ||||
| pub struct ConfParser; | ||||
| 
 | ||||
| /// Check if the robot is in the map.
 | ||||
| fn check_map(r: &robot::Robot, w: &world::World) -> Result<(), String> { | ||||
|     if r.p.x < 0 || r.p.y < 0 || r.p.x > w.x || r.p.y > w.y { | ||||
| @ -48,119 +58,96 @@ fn create_hash_map(pool: &Vec<robot::Robot>, hash: &mut HashMap<robot::Position, | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 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 | ||||
| } | ||||
| 
 | ||||
| /// Parse the config file, generate the world and robot pool.
 | ||||
| fn parse_config(conf: String, pool: &mut Vec<robot::Robot>) -> Result<world::World, String> { | ||||
|     let mut lines = conf.lines(); | ||||
|     // The first line of the config file should be the World.
 | ||||
|     let raw_line: &str = match lines.next() { | ||||
|         Some(raw) => raw, | ||||
|         None => { | ||||
|             return Err(String::from( | ||||
|                 "Could not read the first line of the config file !", | ||||
|             )) | ||||
|         } | ||||
|     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(e) => return Err(format!("{}", e)), | ||||
|     }; | ||||
|     let mut tokens = raw_line.split_whitespace(); | ||||
|     let token1 = match tokens.next() { | ||||
|         Some(raw) => raw, | ||||
|         None => { | ||||
|             return Err(String::from( | ||||
|                 "Could not read the first token of the first line !", | ||||
|             )) | ||||
|         } | ||||
|     }; | ||||
|     let token2 = match tokens.next() { | ||||
|         Some(raw) => raw, | ||||
|         None => { | ||||
|             return Err(String::from( | ||||
|                 "Could not read the second token of the first line !", | ||||
|             )) | ||||
|         } | ||||
|     }; | ||||
|     let x: i32 = match token1.parse::<i32>() { | ||||
|         Ok(x) => x, | ||||
|         Err(_) => { | ||||
|             return Err(String::from( | ||||
|                 "Could not convert token one from the first string to i32", | ||||
|             )) | ||||
|         } | ||||
|     }; | ||||
|     let y: i32 = match token2.parse::<i32>() { | ||||
|         Ok(x) => x, | ||||
|         Err(_) => { | ||||
|             return Err(String::from( | ||||
|                 "Could not convert token two from the first string to i32", | ||||
|             )) | ||||
|         } | ||||
|     }; | ||||
|     let w = world::World { x, y }; | ||||
|     let mut w: Vec<i32> = Vec::with_capacity(2); | ||||
|     for n in raw_world.split_whitespace() { | ||||
|         let v: i32 = match n.parse::<i32>() { | ||||
|             Ok(x) => x, | ||||
|             Err(_) => return Err(String::from("World config is broken.")), | ||||
|         }; | ||||
|         w.push(v); | ||||
|     } | ||||
|     let world = world::World { x: w[0], y: w[1] }; | ||||
|     lines.remove(0); | ||||
|     let mut r_id: u32 = 0; | ||||
|     loop { | ||||
|         r_id += 1; | ||||
|         // This line should be empty.
 | ||||
|         let empty_line = match lines.next() { | ||||
|             None => break, | ||||
|             Some(x) => x, | ||||
|         }; | ||||
|         if !empty_line.is_empty() { | ||||
|             return Err(String::from("This line should be empty !")); | ||||
|         if lines.len() == 0 { | ||||
|             break; | ||||
|         } | ||||
|         let raw_setup = match lines.next() { | ||||
|             None => return Err(String::from("This line should be the config !")), | ||||
|             Some(raw) => raw, | ||||
|         let raw_setup = match ConfParser::parse(Rule::robot_init, lines.remove(0)) { | ||||
|             Ok(s) => s.as_str(), | ||||
|             Err(e) => return Err(format!("{}", e)), | ||||
|         }; | ||||
|         if raw_setup.is_empty() { | ||||
|             return Err(String::from("This line should not be empty !")); | ||||
|         } | ||||
|         let raw_inst = match lines.next() { | ||||
|             None => return Err(String::from("This line should be the instruction !")), | ||||
|             Some(raw) => raw, | ||||
| 
 | ||||
|         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(), | ||||
|         }; | ||||
|         if raw_inst.is_empty() { | ||||
|             return Err(String::from("This line should not be empty !")); | ||||
|         } | ||||
|         // Parse the setup line of the robot.
 | ||||
| 
 | ||||
|         let mut setup = raw_setup.split_whitespace(); | ||||
|         let pos_x = match setup.next() { | ||||
|             Some(raw) => raw, | ||||
|             None => { | ||||
|                 return Err(String::from( | ||||
|                     "Could not read the first token of the setup line !", | ||||
|                 )) | ||||
|             } | ||||
|             Some(raw) => raw, | ||||
|         }; | ||||
|         let pos_y = match setup.next() { | ||||
|             Some(raw) => raw, | ||||
|             None => { | ||||
|                 return Err(String::from( | ||||
|                     "Could not read the second token of the setup line !", | ||||
|                 )) | ||||
|             } | ||||
|             Some(raw) => raw, | ||||
|         }; | ||||
|         let orientation = match setup.next() { | ||||
|             Some(raw) => raw, | ||||
|             None => { | ||||
|                 return Err(String::from( | ||||
|                     "Could not read the third token of the setup line !", | ||||
|                 )) | ||||
|             } | ||||
|             Some(raw) => raw, | ||||
|         }; | ||||
|         // Convert values of the setup line
 | ||||
|         let r_x = match pos_x.parse::<i32>() { | ||||
|             Ok(raw) => raw, | ||||
|             Err(_) => { | ||||
|                 return Err(String::from( | ||||
|                     "Could not convert the first token of the setup ligne to i32 !", | ||||
|                 )) | ||||
|             } | ||||
|             Ok(raw) => raw, | ||||
|         }; | ||||
|         let r_y = match pos_y.parse::<i32>() { | ||||
|             Ok(raw) => raw, | ||||
|             Err(_) => { | ||||
|                 return Err(String::from( | ||||
|                     "Could not convert the second token of the setup ligne to i32 !", | ||||
|                 )) | ||||
|             } | ||||
|             Ok(raw) => raw, | ||||
|         }; | ||||
|         let r_o = match orientation { | ||||
|             "N" => robot::Orientation::N, | ||||
| @ -173,9 +160,8 @@ fn parse_config(conf: String, pool: &mut Vec<robot::Robot>) -> Result<world::Wor | ||||
|                 )) | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         // Convert instructions line.
 | ||||
|         let inst: Vec<char> = raw_inst.chars().rev().collect(); | ||||
|         let inst: Vec<char> = instructions.chars().rev().collect(); | ||||
|         if !robot::is_instructions(&inst) { | ||||
|             return Err(String::from("Invalid instructions !")); | ||||
|         } | ||||
| @ -183,13 +169,21 @@ fn parse_config(conf: String, pool: &mut Vec<robot::Robot>) -> Result<world::Wor | ||||
|         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, &w) { | ||||
|         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(w) | ||||
|     Ok(world) | ||||
| } | ||||
| 
 | ||||
| /// Retrieve the content of a file and return it as a string.
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Volodymyr Patuta
						Volodymyr Patuta