Skip to content

Instantly share code, notes, and snippets.

@bjjb
Last active February 2, 2022 16:26
Show Gist options
  • Save bjjb/0b4c6744c0ce609abcdc9f3b8a7bd968 to your computer and use it in GitHub Desktop.
Save bjjb/0b4c6744c0ce609abcdc9f3b8a7bd968 to your computer and use it in GitHub Desktop.
A Rust implementation of the Mars Rover exercise.
use rover::Rover;
use std::{env, io};
fn main() {
let mut r = match Rover::from_args(env::args()) {
Ok(r) => r,
Err(e) => return eprintln!("{}", e),
};
println!("{}", r.report());
loop {
let mut cmd = String::new();
match io::stdin().read_line(&mut cmd) {
Ok(_) => match r.send(&cmd.trim()) {
Ok(position) => println!("{}", position),
Err(error) => eprintln!("error! {}", error),
},
Err(_) => eprintln!("error reading input"),
};
}
}
use std::env;
const COMPASS: [&str; 4] = [ "NORTH", "EAST", "SOUTH", "WEST" ];
const NORTH: &str = COMPASS[0];
const EAST: &str = COMPASS[1];
const SOUTH: &str = COMPASS[2];
const WEST: &str = COMPASS[3];
#[derive(Clone)]
#[derive(Debug)]
pub struct Rover {
x: i64,
y: i64,
h: &'static str,
obstacles: Vec<(i64, i64)>,
stopped: bool,
}
impl Rover {
pub fn new(x: i64, y: i64, h: &str) -> Rover {
let h = COMPASS.iter().find(|&x| *x == h).unwrap();
let obstacles = vec![];
let stopped = false;
Rover { x, y, h, obstacles, stopped }
}
pub fn from_args(mut args: env::Args) -> Result<Rover, &'static str> {
args.next();
let x = match args.next() {
Some(x) => x,
None => return Err("no x provided"),
};
let y = match args.next() {
Some(y) => y,
None => return Err("no y provided"),
};
let h = match args.next() {
Some(h) => h,
None => return Err("no h provided"),
};
let x = match x.parse::<i64>() {
Ok(x) => x,
Err(_) => return Err("invalid value for x"),
};
let y = match y.parse::<i64>() {
Ok(y) => y,
Err(_) => return Err("invalid value for y"),
};
let h = match COMPASS.iter().find(|s| **s == h) {
Some(h) => h,
None => return Err("invalid heading"),
};
Ok(Rover::new(x, y, h))
}
pub fn report(&self) -> String {
let mut s = format!("({}, {}) {}", self.x, self.y, self.h);
if self.stopped {
s = s + " STOPPED";
}
s
}
pub fn send(&mut self, commands: &str) -> Result<String, &'static str>{
for c in commands.chars() {
match c {
'F' => self.mv(1),
'B' => self.mv(-1),
'L' => self.turn(-1),
'R' => self.turn(1),
_ => return Err("invalid command"),
};
};
Ok(self.report())
}
fn mv(&mut self, steps: i64) {
self.stopped = false;
let (mut x, mut y) = (self.x, self.y);
match self.h {
NORTH => y += steps,
SOUTH => y -= steps,
EAST => x += steps,
WEST => x -= steps,
_ => panic!("rover has an invalid direction: {}", self.h),
}
for (ox, oy) in self.obstacles.iter() {
if *ox == x && *oy == y {
self.stopped = true;
return
}
}
self.x = x;
self.y = y;
}
fn turn(&mut self, amount: i8) {
let i = COMPASS.iter().position(|&x| x == self.h).unwrap() as i8;
let h = (i + 4 + amount) % 4;
self.h = COMPASS[h as usize];
}
}
#[test]
fn it_can_be_initialized() {
let r = Rover::new(0, 0, NORTH);
assert_eq!("(0, 0) NORTH", r.report());
}
#[test]
fn it_can_receive_commands() {
let mut r = Rover::new(4, 2, EAST);
r.send("FLFFFRFLB");
assert_eq!("(6, 4) NORTH", r.report());
r.send("BBLL");
assert_eq!("(6, 2) SOUTH", r.report());
}
#[test]
fn it_can_stop_at_abstacles() {
let mut r = Rover::new(0, 0, NORTH);
r.obstacles = vec![(-2, 7), (1, 1)];
r.send("FRF");
assert_eq!("(0, 1) EAST STOPPED", r.report());
r.send("FFF");
assert_eq!("(0, 1) EAST STOPPED", r.report());
r.send("LFRFFRFRF");
assert_eq!("(2, 1) WEST STOPPED", r.report());
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment