Решение на Dungeons and Compilers от Мирослав Фурнаджиев
Към профила на Мирослав Фурнаджиев
Резултати
- 11 точки от тестове
- 0 бонус точки
- 11 точки общо
- 8 успешни тест(а)
- 7 неуспешни тест(а)
Код
use std::collections::HashMap;
use std::collections::VecDeque;
use std::collections::HashSet;
use std::io::BufRead;
#[derive(Debug)]
pub enum Errors {
DuplicateRoom(String),
UnknownRoom(String),
IoError(std::io::Error),
LineParseError { line_number: usize },
DirectionParseError(String),
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub enum Direction {
North,
South,
East,
West,
}
impl Direction {
pub fn opposite_direction(direction: Direction) -> Direction {
match direction {
Direction::North => Direction::South,
Direction::South => Direction::North,
Direction::East => Direction::West,
Direction::West => Direction::East,
}
}
pub fn from_str(direction: &str) -> Result<Direction, Errors> {
match direction {
"North" => Ok(Direction::North),
"South" => Ok(Direction::South),
"East" => Ok(Direction::East),
"West" => Ok(Direction::West),
_ => Err(Errors::DirectionParseError(direction.to_string()))
}
}
}
pub struct Room {
pub name: String,
links: HashMap<Direction, String>
}
impl Room {
pub fn new(name: &str) -> Self {
Room {
name: name.to_string(),
links: HashMap::with_capacity(4)
}
}
pub fn add_link(&mut self, direction: Direction, room_name: &str) {
self.links.insert(direction, room_name.to_string());
}
pub fn get_link(&self, direction: Direction) -> Option<&String> {
self.links.get(&direction)
}
}
pub struct Dungeon {
rooms: HashMap<String, Room>
}
impl Dungeon {
pub fn new() -> Self {
Dungeon {
rooms: HashMap::with_capacity(10)
}
}
pub fn add_room(&mut self, name: &str) -> Result<(), Errors> {
if !self.rooms.contains_key(&name.to_string()) {
self.rooms.insert(
name.to_string(),
Room::new(name)
);
Ok(())
}
else {
Err(Errors::DuplicateRoom(name.to_string()))
}
}
pub fn get_room(&self, room_name: &str) -> Result<&Room, Errors> {
let value = self.rooms.get(room_name);
match value {
Some(room) => Ok(room),
None => Err(Errors::UnknownRoom(room_name.to_string()))
}
}
pub fn set_link(
&mut self,
room_name: &str,
direction: Direction,
other_room_name: &str
) -> Result<(), Errors> {
let mut unknown_room: Option<String> = None;
if !self.rooms.contains_key(&room_name.to_string()) {
unknown_room = Some(room_name.to_string())
}
else if !self.rooms.contains_key(&other_room_name.to_string()) {
unknown_room = Some(other_room_name.to_string())
}
else {
let room = self.rooms.get_mut(room_name).unwrap();
room.add_link(direction, other_room_name);
let other_room = self.rooms.get_mut(other_room_name).unwrap();
other_room.add_link(Direction::opposite_direction(direction), room_name);
}
match unknown_room {
None => Ok(()),
Some(room_name) => Err(Errors::UnknownRoom(room_name.to_string()))
}
}
pub fn get_next_room(
&self, room_name:
&str, direction: Direction
) -> Result<Option<&Room>, Errors> {
let next_room_name = self.get_room(room_name).unwrap().get_link(direction);
let next_room = self.get_room(next_room_name.unwrap()).unwrap();
Ok(Some(next_room))
}
pub fn from_reader<B: BufRead>(reader: B) -> Result<Self, Errors> {
fn match_prefix<'a, 'b>(prefix: &'a str, input: &'b str) -> Option<&'b str> {
if input.starts_with(prefix) {
Some(&input[prefix.len()..])
}
else {
None
}
}
let mut dungeon = Dungeon::new();
let mut lines = reader.lines().enumerate();
let (index, rooms_header) = lines.next().unwrap();
if rooms_header.unwrap() != "## Rooms" {
return Err(Errors::LineParseError{line_number: index});
}
for (index, line) in lines.by_ref() {
if line.as_ref().unwrap().is_empty() {
break;
}
else {
let room_name = match_prefix("- ", &line.as_ref().unwrap());
match room_name {
Some(name) => dungeon.add_room(name),
None => Err(Errors::LineParseError{line_number: index})
};
}
}
let (index, links_header) = lines.next().unwrap();
if links_header.unwrap() != "## Links" {
return Err(Errors::LineParseError{line_number: index});
}
for (index, line) in lines.by_ref() {
let link = line.unwrap();
let mut objs = link.split(" -> ").collect::<Vec<&str>>();
if objs.len() != 3 {
return Err(Errors::LineParseError{line_number: index});
}
objs[0]= match_prefix("- ", &objs[0]).unwrap();
dungeon.set_link(objs[0], Direction::from_str(objs[1]).unwrap(), objs[2]);
}
Ok(dungeon)
}
fn backtrace(
&self,
start_room_name: &str,
end_room_name: &str,
parents: HashMap<String, String>
) -> Option<Vec<&Room>> {
let mut path: Vec<&Room> = Vec::new();
let end = self.get_room(end_room_name).unwrap();
path.push(end);
while path.last().unwrap().name != start_room_name {
let parent = parents.get(&path.last().unwrap().name.to_string());
let parent_room = self.get_room(parent.unwrap()).unwrap();
path.push(parent_room);
}
path.reverse();
Some(path)
}
pub fn find_path(
&self,
start_room_name: &str,
end_room_name: &str
) -> Result<Option<Vec<&Room>>, Errors> {
let directions = vec![
Direction::North, Direction::South,
Direction::East, Direction::West
];
let mut parents: HashMap<String, String> = HashMap::new();
let mut queue: VecDeque<&Room> = VecDeque::new();
let mut visited: HashSet<String> = HashSet::new();
let start = self.get_room(start_room_name).unwrap();
queue.push_back(start);
visited.insert(start_room_name.to_string());
while !queue.is_empty() {
let curr = queue.pop_front().unwrap();
if curr.name == end_room_name {
return Ok(self.backtrace(start_room_name, end_room_name, parents));
}
for dir in &directions {
let next_room_name = curr.get_link(*dir);
if !next_room_name.is_none() {
let next = self.get_room(next_room_name.unwrap()).unwrap();
if !visited.contains(&next.name.to_string()) {
visited.insert(next.name.to_string());
parents.insert(next.name.to_string(), curr.name.to_string());
queue.push_back(next);
}
}
}
}
Ok(None)
}
}
Лог от изпълнението
Compiling solution v0.1.0 (/tmp/d20220116-3533338-17cxm4t/solution) warning: unused `Result` that must be used --> src/lib.rs:169:17 | 169 | / match room_name { 170 | | Some(name) => dungeon.add_room(name), 171 | | None => Err(Errors::LineParseError{line_number: index}) 172 | | }; | |__________________^ | = note: `#[warn(unused_must_use)]` on by default = note: this `Result` may be an `Err` variant, which should be handled warning: unused `Result` that must be used --> src/lib.rs:189:13 | 189 | dungeon.set_link(objs[0], Direction::from_str(objs[1]).unwrap(), objs[2]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: this `Result` may be an `Err` variant, which should be handled warning: `solution` (lib) generated 2 warnings Finished test [unoptimized + debuginfo] target(s) in 3.88s 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 ... FAILED test solution_test::test_finding_no_path ... FAILED test solution_test::test_invalid_parsing ... FAILED test solution_test::test_io_error ... FAILED test solution_test::test_overwriting_a_room_link ... FAILED test solution_test::test_parsing_cyrillic_rooms ... ok test solution_test::test_parsing_no_rooms_or_links ... FAILED test solution_test::test_parsing_rooms ... ok test solution_test::test_room_errors ... ok test solution_test::test_room_links ... FAILED failures: ---- solution_test::test_finding_an_indirect_path stdout ---- thread '<unnamed>' panicked at 'called `Option::unwrap()` on a `None` value', src/lib.rs:138:54 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', tests/solution_test.rs:320:5 ---- solution_test::test_finding_no_path stdout ---- thread '<unnamed>' panicked at 'assertion failed: path.is_err()', tests/solution_test.rs:382:9 thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', tests/solution_test.rs:372:5 ---- solution_test::test_invalid_parsing stdout ---- thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', /tmp/d20220116-3533338-17cxm4t/solution/src/lib.rs:158:50 ---- solution_test::test_io_error stdout ---- thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: Custom { kind: Other, error: "fill_buf error!" }', /tmp/d20220116-3533338-17cxm4t/solution/src/lib.rs:159:25 thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Custom { kind: Other, error: "fill_buf error!" }', tests/solution_test.rs:194:5 ---- solution_test::test_overwriting_a_room_link stdout ---- thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', src/lib.rs:138:54 ---- solution_test::test_parsing_no_rooms_or_links stdout ---- thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', src/lib.rs:138:54 ---- solution_test::test_room_links stdout ---- thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', src/lib.rs:138:54 failures: solution_test::test_finding_an_indirect_path solution_test::test_finding_no_path solution_test::test_invalid_parsing solution_test::test_io_error solution_test::test_overwriting_a_room_link solution_test::test_parsing_no_rooms_or_links solution_test::test_room_links test result: FAILED. 8 passed; 7 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s error: test failed, to rerun pass '--test solution_test'