dancing_droids/src/robot.rs

125 lines
3.4 KiB
Rust

/// 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,
i: Vec<char>,
}
impl Robot {
/// Create new `Robot` with given id, `Orientation`, `Position` and instructions.
pub fn new(id: u32, o: Orientation, p: Position, i: Vec<char>) -> 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 {
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 => (),
}
}
}
/// Enum to store all possible orientations.
pub enum Orientation {
N,
E,
S,
W,
}
/// Struct to store robot position.
#[derive(PartialEq, Eq, Hash)]
pub struct Position {
pub x: i32,
pub y: i32,
}
/// 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
}
#[cfg(test)]
mod tests {
use super::*;
#[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 },
vec!['R', 'F', 'L', '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 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));
}
}