239 lines
6.3 KiB
Rust
239 lines
6.3 KiB
Rust
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));
|
|
}
|
|
}
|