Решение на Dungeons and Compilers от Петър Павлов
Резултати
- 15 точки от тестове
- 0 бонус точки
- 15 точки общо
- 11 успешни тест(а)
- 4 неуспешни тест(а)
Код
use std::collections::{HashMap, VecDeque};
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,
}
pub struct Room {
pub name: String,
pub neighbours: HashMap<Direction, String>
}
pub struct Dungeon {
rooms: HashMap<String, Room>
}
impl Dungeon {
pub fn new() -> Self {
Dungeon {rooms: HashMap::new()}
}
pub fn add_room(&mut self, name: &str) -> Result<(), Errors> {
match self.rooms.get(name) {
Some(room) => Err(Errors::DuplicateRoom(room.name.clone())),
None => {
self.rooms.insert(name.to_string(), Room {name: name.to_string(), neighbours: HashMap::new()});
Ok(())
}
}
}
pub fn get_room(&self, room_name: &str) -> Result<&Room, Errors> {
match self.rooms.get(room_name) {
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 _ = self.get_room(room_name)?;
let _ = self.get_room(other_room_name)?;
self.rooms.get_mut(room_name).unwrap().neighbours.insert(direction, other_room_name.to_string());
let other_dir = match direction {
Direction::East => Direction::West,
Direction::West => Direction::East,
Direction::North => Direction::South,
Direction::South => Direction::North
};
self.rooms.get_mut(other_room_name).unwrap().neighbours.insert(other_dir, room_name.to_string());
Ok(())
}
pub fn get_next_room(&self, room_name: &str, direction: Direction) -> Result<Option<&Room>, Errors> {
let room = self.get_room(room_name)?;
match room.neighbours.get(&direction) {
Some(name) => Ok(Some(self.get_room(name).unwrap())),
None => Ok(None)
}
}
pub fn from_reader<B: BufRead>(reader: B) -> Result<Self, Errors> {
let lines_iter = reader.lines();
let mut strings: Vec<String> = Vec::new();
for line in lines_iter {
match line {
Err(e) => {return Err(Errors::IoError(e));},
Ok(string) => {strings.push(string);}
}
}
let mut strins_iter = strings.iter();
match strins_iter.next() {
None => return Err(Errors::LineParseError{line_number: 0}),
Some(line) => {
if line != "## Rooms" {
return Err(Errors::LineParseError{line_number: 1});
}
}
}
let mut dungeon = Dungeon::new();
let mut line_number = 2;
loop {
match strins_iter.next() {
None => {return Err(Errors::LineParseError{line_number});},
Some(line) => {
if line == "" {
line_number += 1;
break;
}
match line.strip_prefix("- ") {
None => {return Err(Errors::LineParseError{line_number});},
Some(rest) => {let _ = dungeon.add_room(rest)?;}
}
}
};
line_number += 1;
}
line_number += 1;
match strins_iter.next() {
None => return Err(Errors::LineParseError{line_number}),
Some(line) => {
if line != "## Links" {
return Err(Errors::LineParseError{line_number});
}
}
}
line_number += 1;
loop {
match strins_iter.next() {
None => {break;},
Some(line) => {
match line.strip_prefix("- ") {
None => {return Err(Errors::LineParseError{line_number});},
Some(rest) => {
let args: Vec<&str> = rest.split(" -> ").collect();
if args.len() != 3 {return Err(Errors::LineParseError{line_number});}
let dir;
if args[1] == "West" {dir = Direction::West;}
else if args[1] == "East" {dir = Direction::East;}
else if args[1] == "North" {dir = Direction::North;}
else if args[1] == "South" {dir = Direction::South;}
else {return Err(Errors::DirectionParseError(args[1].to_string()));}
dungeon.set_link(args[0], dir, args[2])?;
}
}
}
};
line_number += 1;
}
Ok(dungeon)
}
pub fn find_path(
&self,
start_room_name: &str,
end_room_name: &str
) -> Result<Option<Vec<&Room>>, Errors> {
let mut queue: VecDeque<String> = VecDeque::new();
let mut visited: HashMap<String, bool> = HashMap::new();
let mut parents: HashMap<String, String> = HashMap::new();
queue.push_back(start_room_name.to_string());
visited.insert(start_room_name.to_string(), true);
while !queue.is_empty() {
let current = self.get_room(&queue.pop_front().unwrap())?;
if current.name == end_room_name.to_string() {break;}
for (_, room) in ¤t.neighbours {
if let None = visited.get(room) {
parents.insert(room.to_string(), current.name.clone());
visited.insert(room.to_string(), true);
}
}
}
if let None = parents.get(end_room_name) {
return Ok(None);
}
let mut path: Vec<&Room> = Vec::new();
path.push(self.get_room(end_room_name)?);
let mut last = end_room_name;
while let Some(parent) = parents.get(last){
path.push(self.get_room(parent)?);
last = parent;
}
path.reverse();
Ok(Some(path))
}
}
Лог от изпълнението
Compiling solution v0.1.0 (/tmp/d20220116-3533338-rdonn7/solution) Finished test [unoptimized + debuginfo] target(s) in 3.96s 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 ... FAILED 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 ... 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 failures: ---- solution_test::test_finding_a_reflexive_path stdout ---- thread '<unnamed>' panicked at 'called `Option::unwrap()` on a `None` value', tests/solution_test.rs:360:71 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:354:5 ---- solution_test::test_finding_an_indirect_path stdout ---- thread '<unnamed>' panicked at 'called `Option::unwrap()` on a `None` value', tests/solution_test.rs:336:76 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 'assertion failed: matches!(Dungeon :: from_reader(TEST_INPUT_5.trim().as_bytes()),\n Err(Errors :: LineParseError { line_number : 3 }))', tests/solution_test.rs:278:5 failures: solution_test::test_finding_a_reflexive_path solution_test::test_finding_an_indirect_path solution_test::test_finding_no_path solution_test::test_invalid_parsing test result: FAILED. 11 passed; 4 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s error: test failed, to rerun pass '--test solution_test'
История (2 версии и 0 коментара)
Петър качи решение на 11.01.2022 15:53 (преди над 3 години)
use std::collections::{HashMap, VecDeque};
-use std::hash::Hash;
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,
}
pub struct Room {
pub name: String,
pub neighbours: HashMap<Direction, String>
}
pub struct Dungeon {
rooms: HashMap<String, Room>
}
impl Dungeon {
pub fn new() -> Self {
Dungeon {rooms: HashMap::new()}
}
pub fn add_room(&mut self, name: &str) -> Result<(), Errors> {
match self.rooms.get(name) {
Some(room) => Err(Errors::DuplicateRoom(room.name.clone())),
None => {
self.rooms.insert(name.to_string(), Room {name: name.to_string(), neighbours: HashMap::new()});
Ok(())
}
}
}
pub fn get_room(&self, room_name: &str) -> Result<&Room, Errors> {
match self.rooms.get(room_name) {
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 _ = self.get_room(room_name)?;
let _ = self.get_room(other_room_name)?;
self.rooms.get_mut(room_name).unwrap().neighbours.insert(direction, other_room_name.to_string());
let other_dir = match direction {
Direction::East => Direction::West,
Direction::West => Direction::East,
Direction::North => Direction::South,
Direction::South => Direction::North
};
self.rooms.get_mut(other_room_name).unwrap().neighbours.insert(other_dir, room_name.to_string());
Ok(())
}
pub fn get_next_room(&self, room_name: &str, direction: Direction) -> Result<Option<&Room>, Errors> {
let room = self.get_room(room_name)?;
match room.neighbours.get(&direction) {
Some(name) => Ok(Some(self.get_room(name).unwrap())),
None => Ok(None)
}
}
pub fn from_reader<B: BufRead>(reader: B) -> Result<Self, Errors> {
let lines_iter = reader.lines();
let mut strings: Vec<String> = Vec::new();
for line in lines_iter {
match line {
Err(e) => {return Err(Errors::IoError(e));},
Ok(string) => {strings.push(string);}
}
}
let mut strins_iter = strings.iter();
match strins_iter.next() {
None => return Err(Errors::LineParseError{line_number: 0}),
Some(line) => {
if line != "## Rooms" {
return Err(Errors::LineParseError{line_number: 1});
}
}
}
let mut dungeon = Dungeon::new();
let mut line_number = 2;
loop {
match strins_iter.next() {
None => {return Err(Errors::LineParseError{line_number});},
Some(line) => {
if line == "" {
line_number += 1;
break;
}
match line.strip_prefix("- ") {
None => {return Err(Errors::LineParseError{line_number});},
Some(rest) => {let _ = dungeon.add_room(rest)?;}
}
}
};
line_number += 1;
}
line_number += 1;
match strins_iter.next() {
None => return Err(Errors::LineParseError{line_number}),
Some(line) => {
if line != "## Links" {
return Err(Errors::LineParseError{line_number});
}
}
}
line_number += 1;
loop {
match strins_iter.next() {
None => {break;},
Some(line) => {
match line.strip_prefix("- ") {
None => {return Err(Errors::LineParseError{line_number});},
Some(rest) => {
let args: Vec<&str> = rest.split(" -> ").collect();
if args.len() != 3 {return Err(Errors::LineParseError{line_number});}
let dir;
if args[1] == "West" {dir = Direction::West;}
else if args[1] == "East" {dir = Direction::East;}
else if args[1] == "North" {dir = Direction::North;}
else if args[1] == "South" {dir = Direction::South;}
else {return Err(Errors::DirectionParseError(args[1].to_string()));}
dungeon.set_link(args[0], dir, args[2])?;
}
}
}
};
line_number += 1;
}
Ok(dungeon)
}
pub fn find_path(
&self,
start_room_name: &str,
end_room_name: &str
) -> Result<Option<Vec<&Room>>, Errors> {
let mut queue: VecDeque<String> = VecDeque::new();
let mut visited: HashMap<String, bool> = HashMap::new();
let mut parents: HashMap<String, String> = HashMap::new();
queue.push_back(start_room_name.to_string());
visited.insert(start_room_name.to_string(), true);
while !queue.is_empty() {
let current = self.get_room(&queue.pop_front().unwrap())?;
if current.name == end_room_name.to_string() {break;}
for (_, room) in ¤t.neighbours {
if let None = visited.get(room) {
parents.insert(room.to_string(), current.name.clone());
visited.insert(room.to_string(), true);
}
}
}
if let None = parents.get(end_room_name) {
return Ok(None);
}
let mut path: Vec<&Room> = Vec::new();
path.push(self.get_room(end_room_name)?);
let mut last = end_room_name;
while let Some(parent) = parents.get(last){
path.push(self.get_room(parent)?);
last = parent;
}
path.reverse();
Ok(Some(path))
}
}