dancing_droids/src/robot.rs

199 lines
5.1 KiB
Rust
Raw Normal View History

2020-11-03 14:36:20 +01:00
use rand::{
distributions::{Distribution, Standard},
Rng,
};
2020-11-03 15:11:50 +01:00
/// A Robot *aka droid* is represented here.
/// Each robot must have a unique id.
2020-10-27 22:55:31 +01:00
pub struct Robot {
2020-10-28 21:38:30 +01:00
pub id: u32,
pub o: Orientation,
pub p: Position,
2020-10-28 17:30:45 +01:00
i: Vec<char>,
}
impl Robot {
/// Create new `Robot` with given id, `Orientation`, `Position` and instructions.
2020-10-28 21:38:30 +01:00
pub fn new(id: u32, o: Orientation, p: Position, i: Vec<char>) -> Robot {
2020-10-28 17:30:45 +01:00
Robot { id, o, p, i }
}
/// Apply given instruction to a `Robot`.
2020-10-28 21:38:30 +01:00
pub fn execute_instruction(&mut self) {
2020-10-28 17:30:45 +01:00
match self.i.pop() {
Some(instruction) => match instruction {
2020-10-28 19:12:30 +01:00
'L' => match self.o {
2020-10-28 17:30:45 +01:00
Orientation::N => self.o = Orientation::W,
Orientation::E => self.o = Orientation::N,
Orientation::S => self.o = Orientation::E,
Orientation::W => self.o = Orientation::S,
2020-10-28 19:12:30 +01:00
},
'R' => match self.o {
2020-10-28 17:30:45 +01:00
Orientation::N => self.o = Orientation::E,
Orientation::E => self.o = Orientation::S,
Orientation::S => self.o = Orientation::W,
Orientation::W => self.o = Orientation::N,
2020-10-28 19:12:30 +01:00
},
'F' => match self.o {
2020-10-28 17:30:45 +01:00
Orientation::N => self.p.y += 1,
Orientation::E => self.p.x += 1,
Orientation::S => self.p.y -= 1,
Orientation::W => self.p.x -= 1,
2020-10-28 19:12:30 +01:00
},
2020-10-28 19:18:31 +01:00
_ => (), // never happens 😉
2020-10-28 17:30:45 +01:00
},
2020-10-28 19:12:30 +01:00
None => (),
2020-10-28 17:30:45 +01:00
}
}
2020-10-27 22:55:31 +01:00
}
/// Enum to store all possible orientations.
2020-11-03 14:36:20 +01:00
#[derive(Debug)]
2020-10-28 21:38:30 +01:00
pub enum Orientation {
2020-10-27 22:55:31 +01:00
N,
E,
S,
W,
}
2020-11-03 14:36:20 +01:00
impl Distribution<Orientation> for Standard {
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,
}
}
}
2020-10-27 22:55:31 +01:00
/// Struct to store robot position.
2020-10-28 18:20:37 +01:00
#[derive(PartialEq, Eq, Hash)]
2020-10-28 21:38:30 +01:00
pub struct Position {
pub x: i32,
pub y: i32,
2020-10-27 22:55:31 +01:00
}
2020-10-28 17:30:45 +01:00
2020-10-30 16:34:25 +01:00
/// Check if instructions list is valid.
pub fn is_instructions(v: &Vec<char>) -> bool {
for c in v {
match c {
'F' => continue,
'R' => continue,
'L' => continue,
_ => return false,
}
}
true
}
2020-10-31 18:44:23 +01:00
/// 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.
2020-11-07 13:38:05 +01:00
pub fn print_robots(robot_pool: &Vec<Robot>) -> String {
let mut res = format!("Robots [\n");
for r in robot_pool {
2020-11-07 13:38:05 +01:00
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
);
}
2020-11-07 13:38:05 +01:00
res = format!("{}]\n", res);
res
}
2020-10-28 17:30:45 +01:00
#[cfg(test)]
mod tests {
use super::*;
2020-11-03 14:36:20 +01:00
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");
}
2020-10-28 17:30:45 +01:00
#[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 },
2020-10-28 19:12:30 +01:00
vec!['R', 'F', 'L', 'F'],
2020-10-28 17:30:45 +01:00
);
2020-10-28 19:12:30 +01:00
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.
2020-10-28 17:30:45 +01:00
assert_eq!(r.p.x, 1);
assert_eq!(r.p.y, 3);
2020-10-28 19:12:30 +01:00
r.execute_instruction();
r.execute_instruction();
assert_eq!(r.p.x, 0);
assert_eq!(r.p.y, 3);
2020-10-28 17:30:45 +01:00
}
2020-10-30 16:34:25 +01:00
#[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));
}
2020-10-31 18:44:23 +01:00
#[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));
}
2020-10-28 17:30:45 +01:00
}