Решение на Dungeons and Compilers от Иван Стефанов
Резултати
- 20 точки от тестове
- 0 бонус точки
- 20 точки общо
- 15 успешни тест(а)
- 0 неуспешни тест(а)
Код
//use std::io::Error;
use std::io::prelude::*;
use std::collections::HashMap;
use std::collections::VecDeque;
#[derive(Debug)]
pub enum Errors {
DuplicateRoom(String),
UnknownRoom(String),
IoError(std::io::Error),
LineParseError { line_number: usize },
DirectionParseError(String),
}
#[derive(Debug,Clone,Copy,PartialEq,Eq,Hash)]
pub enum Direction {
North,
South,
East,
West,
}
impl Direction {
pub fn opposite(direction: Direction) -> Self {
match direction {
Direction::North => Direction::South,
Direction::South => Direction::North,
Direction::East => Direction::West,
Direction::West => Direction::East,
}
}
}
#[derive(Clone,Debug)]
pub struct Room {
pub name: String,
pub neighbours : HashMap<Direction,String>,
}
impl Room {
pub fn new(name_: &str) -> Self {
Room {name: name_.to_string(),neighbours : HashMap::new()}
}
}
pub struct Dungeon {
pub rooms : HashMap<String,Room>,
}
impl Dungeon {
pub fn new() -> Self {
Dungeon {rooms: HashMap::new()}
}
pub fn add_room(&mut self, name: &str) -> Result<(), Errors> {
if self.rooms.contains_key(name) {
Err(Errors::DuplicateRoom(name.to_string()))
}
else {
self.rooms.insert(name.to_string(),Room::new(name));
Ok(())
}
}
pub fn get_room(&self, room_name: &str) -> Result<&Room, Errors> {
if self.rooms.contains_key(room_name) {
Ok(self.rooms.get(room_name).unwrap())
}
else {
Err(Errors::UnknownRoom(room_name.to_string()))
}
}
pub fn set_link(
&mut self,
room_name: &str,
direction: Direction,
other_room_name: &str,
) -> Result<(), Errors> {
if !self.rooms.contains_key(room_name) {
Err(Errors::UnknownRoom(room_name.to_string()))
}
else if !self.rooms.contains_key(other_room_name) {
Err(Errors::UnknownRoom(other_room_name.to_string()))
}
else {
let forward : Direction = direction;
let backward : Direction = Direction::opposite(direction);
self.rooms.get_mut(room_name).unwrap().neighbours.insert(forward,other_room_name.to_string());
self.rooms.get_mut(other_room_name).unwrap().neighbours.insert(backward,room_name.to_string());
Ok(())
}
}
pub fn get_next_room(&self, room_name: &str, direction: Direction) -> Result<Option<&Room>, Errors> {
if !self.rooms.contains_key(room_name) {
Err(Errors::UnknownRoom(room_name.to_string()))
}
else {
if self.rooms.get(room_name).unwrap().neighbours.contains_key(&direction) {
Ok(Some(self.rooms.get(self.rooms.get(room_name).unwrap().neighbours.get(&direction).unwrap()).unwrap()))
}
else { Ok(None) }
}
}
}
fn match_prefix<'a, 'b>(prefix: &'a str, input: &'b str) -> Option<&'b str> {
input.strip_prefix(prefix)
}
impl Dungeon {
pub fn from_reader<B: BufRead>(reader: B) -> Result<Self, Errors> {
let mut check;
let mut new_dungeon = Dungeon::new();
let mut line_idx = 0;
let mut lines = reader.lines();
let mut cur = match lines.next() {
None => return Err(Errors::LineParseError{line_number:line_idx }),
Some(Err(e)) => return Err(Errors::IoError(e)),
Some(Ok(s)) => s,
};
line_idx += 1;
if cur != "## Rooms" {
return Err(Errors::LineParseError{line_number:line_idx });
}
loop {
line_idx += 1;
cur = match lines.next() {
None => return Err(Errors::LineParseError{line_number:line_idx }),
Some(Err(e)) => return Err(Errors::IoError(e)),
Some(Ok(s)) => s,
};
if cur == "" {break;}
check = match match_prefix("- ",&cur) {
None => Err(Errors::LineParseError{line_number:line_idx }),
Some(s) => new_dungeon.add_room(s),
};
match check {
Err(e) => return Err(e),
Ok(s) => (s),
}
}
line_idx += 1;
cur = match lines.next() {
None => return Err(Errors::LineParseError{line_number:line_idx }),
Some(Err(e)) => return Err(Errors::IoError(e)),
Some(Ok(s)) => s,
};
if cur != "## Links" {
return Err(Errors::LineParseError{line_number:line_idx });
}
line_idx += 1;
cur = match lines.next() {
None => "".to_string(),
Some(Err(e)) => return Err(Errors::IoError(e)),
Some(Ok(s)) => s,
};
while cur != "" {
cur = match match_prefix("- ",&cur) {
None => return Err(Errors::LineParseError{line_number:line_idx }),
Some(s) => s.to_string(),
};
let split = cur.split(" -> ");
let vec = split.collect::<Vec<_>>();
if vec.len() != 3 {
return Err(Errors::LineParseError{line_number:line_idx })
}
check = match vec[1] {
"North" => new_dungeon.set_link(vec[0],Direction::North,vec[2]),
"South" => new_dungeon.set_link(vec[0],Direction::South,vec[2]),
"East" => new_dungeon.set_link(vec[0],Direction::East,vec[2]),
"West" => new_dungeon.set_link(vec[0],Direction::West,vec[2]),
_ => Err(Errors::DirectionParseError(cur)),
};
match check {
Err(e) => return Err(e),
Ok(s) => (s),
}
line_idx += 1;
cur = match lines.next() {
None => "".to_string(),
Some(Err(e)) => return Err(Errors::IoError(e)),
Some(Ok(s)) => s,
};
}
Ok(new_dungeon)
}
}
impl Dungeon {
pub fn find_path(
&self,
start_room_name: &str,
end_room_name: &str
) -> Result<Option<Vec<&Room>>, Errors> {
if !self.rooms.contains_key(start_room_name) {
Err(Errors::UnknownRoom(start_room_name.to_string()))
}
else if !self.rooms.contains_key(end_room_name) {
Err(Errors::UnknownRoom(end_room_name.to_string()))
}
else {
let mut visited : HashMap<String,String> = HashMap::new();
let mut path : Vec<&Room> = Vec::new();
let mut queue : VecDeque<&Room> = VecDeque::new();
queue.push_back(self.rooms.get(start_room_name).unwrap());
while !queue.is_empty() {
let cur = queue.front().unwrap().clone();
if cur.name.clone() == end_room_name.to_string() {break;}
queue.pop_front();
for (_direction, room_name) in cur.neighbours.iter() {
if !visited.contains_key(room_name) {
queue.push_back(self.rooms.get(room_name).unwrap());
visited.insert(room_name.to_string(),cur.name.clone());
}
}
}
if visited.contains_key(end_room_name) {
let mut cur = end_room_name;
while cur != start_room_name.to_string() {
path.push(self.rooms.get(cur).unwrap());
cur = visited.get(cur).unwrap();
}
path.push(self.rooms.get(cur).unwrap());
path.reverse();
Ok(Some(path))
}
else if start_room_name == end_room_name {
path.push(self.rooms.get(end_room_name).unwrap());
Ok(Some(path))
}
else {
Ok(None)
}
}
}
}
#[test]
fn test_basic_1() {
let mut dungeon = Dungeon::new();
dungeon.add_room("Entrance").unwrap();
dungeon.add_room("Hallway").unwrap();
dungeon.set_link("Entrance", Direction::East, "Hallway").unwrap();
assert_eq!(dungeon.get_room("Entrance").unwrap().name, "Entrance");
assert_eq!(dungeon.get_next_room("Entrance", Direction::East).unwrap().unwrap().name, "Hallway");
}
#[test]
fn test_basic_2() {
const TEST_INPUT_1: &str = "
## Rooms
- Entrance
- Hallway
## Links
- Entrance -> East -> Hallway
";
let dungeon = Dungeon::from_reader(TEST_INPUT_1.trim().as_bytes()).unwrap();
assert_eq!(dungeon.get_room("Entrance").unwrap().name, "Entrance");
assert_eq!(dungeon.get_room("Hallway").unwrap().name, "Hallway");
assert_eq!(dungeon.get_next_room("Entrance", Direction::East).unwrap().unwrap().name, "Hallway");
}
#[test]
fn test_basic_3() {
let mut dungeon = Dungeon::new();
dungeon.add_room("Entrance").unwrap();
dungeon.add_room("Treasure Room").unwrap();
dungeon.set_link("Entrance", Direction::West, "Treasure Room").unwrap();
let path = dungeon.find_path("Entrance", "Treasure Room").unwrap().unwrap();
assert!(path.len() > 0);
}
Лог от изпълнението
Compiling solution v0.1.0 (/tmp/d20220116-3533338-10feti8/solution) Finished test [unoptimized + debuginfo] target(s) in 3.97s Running tests/solution_test.rs (target/debug/deps/solution_test-2e292b23ac75572c) running 15 tests test solution_test::test_adding_rooms_1 ... ok test solution_test::test_adding_rooms_2 ... ok test solution_test::test_cyrillic_room_names ... ok test solution_test::test_finding_a_direct_path ... ok test solution_test::test_finding_a_reflexive_path ... ok test solution_test::test_finding_an_indirect_path ... ok test solution_test::test_finding_no_path ... ok test solution_test::test_invalid_parsing ... ok test solution_test::test_io_error ... ok test solution_test::test_overwriting_a_room_link ... ok test solution_test::test_parsing_cyrillic_rooms ... ok test solution_test::test_parsing_no_rooms_or_links ... ok test solution_test::test_parsing_rooms ... ok test solution_test::test_room_errors ... ok test solution_test::test_room_links ... ok test result: ok. 15 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s