Решение на Dungeons and Compilers от Павел Цанов

Обратно към всички решения

Към профила на Павел Цанов

Резултати

  • 7 точки от тестове
  • 0 бонус точки
  • 7 точки общо
  • 5 успешни тест(а)
  • 10 неуспешни тест(а)

Код

use std::collections::HashMap;
use std::io::BufRead;
/// Различните грешки, които ще очакваме да върнете като резултат от някои невалидни операции.
/// Повече детайли по-долу.
///
#[derive(Debug)]
pub enum Errors {
DuplicateRoom(String),
UnknownRoom(String),
IoError(std::io::Error),
LineParseError { line_number: usize },
DirectionParseError(String),
}
/// Четирите посоки, в които може една стая да има съседи. Може да добавите още trait
/// имплементации, за да си улесните живота.
///
#[derive(Clone, Copy,PartialEq)]
pub enum Direction {
North,
South,
East,
West,
}
/// Една стая в подземията. Дефинира се само с име, макар че в по-интересна имплементация може да
/// държи item-и, противници...
///
#[derive(Clone,Eq,PartialEq,Hash)]
pub struct Room {
pub name: String,
// Каквито други полета ви трябват
}
pub struct Link {
pub neighbors: Vec<(String,Direction)>,
pub room: Room,
}
/// Контейнер за стаите и не само. Ще работим предимно със тази структура.
///
pub struct Dungeon {
// Каквито полета ви трябват
pub rooms: HashMap<String,Link>
}
impl Dungeon {
/// Конструиране на празен Dungeon, в който няма никакви стаи.
///
pub fn new() -> Self {
Dungeon{rooms: HashMap::new()}
}
/// Добавяне на стая към Dungeon с име `name`. Връща `Ok(())` при успех. Ако вече има стая с
/// такова име, очакваме да върнете `Errors::DuplicateRoom` с името.
///
pub fn add_room(&mut self, name: &str) -> Result<(), Errors> {
let room = Room{name: name.to_string()};
if self.rooms.contains_key(name) {
return Err(Errors::DuplicateRoom(name.to_string()));
}
let vec = Vec::new();
self.rooms.insert(name.to_string(), Link{neighbors:vec,room});
Ok(())
}
/// Прочитане на дадена стая -- когато извикаме `get_room`, очакваме reference към `Room`
/// структурата с това име.
///
/// Ако няма такава стая, очакваме `Errors::UnknownRoom` с подаденото име.
///
pub fn get_room(&self, room_name: &str) -> Result<&Room, Errors> {
if !self.rooms.contains_key(room_name) {
return Err(Errors::UnknownRoom(room_name.to_string()));
}
Ok(&self.rooms.get(room_name).unwrap().room)
}
/// Добавяне на съсед на дадена стая. След извикването на функцията, очакваме стаята с име
/// `room_name` да има връзка в посока `direction` със стаята с име `other_room_name`.
///
/// Също така очакваме `other_room_name` да има връзка с `room_name` в *обратната* посока.
///
/// Успешен резултат е `Ok(())`. В случай, че която от двете стаи не същестува, очакваме грешка
/// `Errors::UnknownRoom` със съответното име на липсваща стая. Ако и двете липсват, спокойно
/// върнете тази, която проверявате първо.
///
pub fn set_link(
&mut self,
room_name: &str,
direction: Direction,
other_room_name: &str,
) -> Result<(), Errors> {
let room = self.get_room(room_name);
if room.is_err(){
return Err(Errors::UnknownRoom(room_name.to_string()));
}
let other_room = self.get_room(other_room_name);
if other_room.is_err(){
return Err(Errors::UnknownRoom(other_room_name.to_string()));
}
self.rooms.get_mut(room_name).unwrap().neighbors.push((other_room_name.to_string(),direction));
let other_direction: Direction = Self::get_reverse_direction(&direction);
self.rooms.get_mut(room_name).unwrap().neighbors.push((other_room_name.to_string(),other_direction));
Ok(())
}
fn get_reverse_direction(direction: &Direction) -> Direction{
match direction {
Direction::East => Direction::West,
Direction::West => Direction::East,
Direction::North => Direction::South,
Direction::South => Direction::North,
}
}
/// Четене на съседа на стаята с име `room_name` в посока `direction`. Тук има няколко
/// варианта на изход:
///
/// - Ако подадената стая не съществува, очакваме грешка `Errors::UnknownRoom`
/// - Ако подадената стая няма съсед в тази посока, Ok(None) е смисления резултат
/// - Иначе, чакаме reference към `Room` структурата на въпросния съсед, опакована в `Ok(Some(`.
///
pub fn get_next_room(&self, room_name: &str, direction: Direction) -> Result<Option<&Room>, Errors> {
let room = self.get_room(room_name);
if room.is_err(){
return Err(Errors::UnknownRoom(room_name.to_string()));
}
let neighbors = &self.rooms.get(room_name).unwrap().neighbors;
for neighbor in neighbors.iter(){
if neighbor.1 == direction{
return Ok(Some(self.get_room(&neighbor.0).unwrap()));
}
}
Ok(None)
}
/// Прочитаме структурата на dungeon от нещо, което имплементира `BufRead`. Това може да е
/// файл, или, ако тестваме, може да е просто колекция от байтове.
///
/// Успешен резултат връща новосъздадения dungeon, пакетиран в `Ok`.
///
/// Вижте по-долу за обяснение на грешките, които очакваме.
///
pub fn from_reader<B: BufRead>(mut reader: B) -> Result<Self, Errors> {
let mut line = String::new();
let first_line = reader.read_line(&mut line);
let mut dungeon = Dungeon::new();
let mut line_index = 2;
if first_line.unwrap() == 0{
return Err(Errors::LineParseError {line_number: 0})
}
if line.trim().ne("## Rooms") {
return Err(Errors::LineParseError { line_number: 1 });
}
line.clear();
loop {
match reader.read_line(&mut line) {
Ok(bytes_read) => {
line = line.trim().to_string();
if bytes_read == 0 || line.eq(""){
line.clear();
break;
}
let content = Self::match_prefix("- ",&line);
if content.is_none(){
return Err(Errors::LineParseError { line_number: line_index });
}
let res = dungeon.add_room(&content.unwrap().to_string());
if res.is_err() {
res.unwrap();
}
// do not accumulate data
line.clear();
line_index = line_index + 1;
}
Err(err) => {
return Err(Errors::IoError(err));
}
};
}
let res = reader.read_line(&mut line);
if res.is_err(){
res.unwrap();
}
line_index = line_index + 1;
if line.trim().ne("## Links") {
return Err(Errors::LineParseError { line_number: line_index });
}
line.clear();
loop {
match reader.read_line(&mut line) {
Ok(bytes_read) => {
if bytes_read == 0 {
break;
}
line = line.trim().to_string();
let first_room = Self::match_prefix("- ",&line);
let mut direction_str = None;
let mut second_room = None;
let mut first_room_piece = "";
let mut direction_piece = "";
if first_room.is_some() {
first_room_piece = &first_room.unwrap()[..first_room.unwrap().find(" -> ").unwrap()];
direction_str = Self::match_prefix(&(first_room_piece.to_owned() + " -> "),
&first_room.unwrap());
}
if direction_str.is_some() {
direction_piece = &direction_str.unwrap()[..direction_str.unwrap().find(" -> ").unwrap()];
second_room = Self::match_prefix(&(direction_piece.to_owned() + " -> "), &direction_str.unwrap());
}
if first_room.is_none() || direction_str.is_none() || second_room.is_none(){
return Err(Errors::DirectionParseError { 0: second_room.unwrap().to_string()});
}
let direction = match direction_piece {
"North" => Direction::North,
"South" => Direction::South,
"East" => Direction::East,
"West" => Direction::West,
_ => return Err(Errors::LineParseError { line_number: line_index }),
};
let res = dungeon.set_link(first_room_piece,direction,second_room.unwrap());
if res.is_err(){
res.unwrap()
}
line.clear();
line_index = line_index + 1;
}
Err(err) => {
return Err(Errors::IoError(err));
}
};
}
Ok(dungeon)
}
/// match_prefix("- ", "- Foo") //=> Some("Foo")
/// match_prefix("- ", "Bar") //=> None
///
fn match_prefix<'a, 'b>(prefix: &'a str, input: &'b str) -> Option<&'b str> {
if input.starts_with(prefix){
return Some(input.trim_start_matches(prefix));
}
None
}
/// Търси път от `start_room_name` до `end_room_name` и го връща във вектор, пакетиран във
/// `Ok(Some(` ако намери.
///
/// Ако няма път между тези две стаи, връща `Ok(None)`.
///
/// Ако четенето на стаи в един момент върне грешка, очакваме да върнете грешката нагоре.
///
pub fn find_path(
&self,
start_room_name: &str,
end_room_name: &str
) -> Result<Option<Vec<&Room>>, Errors> {
/*let mut front: VecDeque<(&str,Vec<&Room>)> = VecDeque::new();
let mut visited: Vec<&str> = Vec::new();
let mut path = Vec::new();
let start_room = self.get_room(start_room_name);
path.push(start_room.unwrap());
front.push_front((start_room_name,path));
path.clear();
while !front.is_empty() {
let front_room = front.pop_front().unwrap();
visited.push(front_room.0);
let mut path = Vec::new();
path.push(front_room.1);
let neighbors = &self.rooms.get(front_room.0).unwrap().neighbors;
for room in neighbors {
if front_room.0.eq(end_room_name) {
let new_vec = Vec::new();
new_vec.push(self.get_room(end_room_name).unwrap());
path.push(new_vec);
Ok(Some(path.unwrap()))
}else if !visited.contains(room.name){
visited.push(room.name);
path.push(self.get_room(room));
front.push_back(room.name,path);
}
}
}*/
let mut vec : Vec<&Room> = Vec::new();
let room = self.get_room(end_room_name);
vec.push(room.unwrap());
if end_room_name.eq(start_room_name){
Ok(Some(vec))
}
else {
Ok(None)
}
}
}

Лог от изпълнението

Compiling solution v0.1.0 (/tmp/d20220116-3533338-1bdon23/solution)
    Finished test [unoptimized + debuginfo] target(s) in 3.60s
     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 ... FAILED
test solution_test::test_cyrillic_room_names ... FAILED
test solution_test::test_finding_a_direct_path ... FAILED
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 ... FAILED
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 ... FAILED

failures:

---- solution_test::test_adding_rooms_2 stdout ----
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `"Side Closet"`,
 right: `"Entrance"`', tests/solution_test.rs:97:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

---- solution_test::test_cyrillic_room_names stdout ----
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', tests/solution_test.rs:180:72

---- solution_test::test_finding_a_direct_path stdout ----
thread '<unnamed>' panicked at 'called `Option::unwrap()` on a `None` value', tests/solution_test.rs:313:76
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', tests/solution_test.rs:306: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 'called `Result::unwrap()` on an `Err` value: UnknownRoom("Mystery Room")', src/lib.rs:337:23
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: UnknownRoom("Mystery Room")', tests/solution_test.rs:372:5

---- solution_test::test_invalid_parsing stdout ----
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: UnknownRoom("Closet")', /tmp/d20220116-3533338-1bdon23/solution/src/lib.rs:261:29

---- 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-1bdon23/solution/src/lib.rs:166:23
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 'assertion failed: `(left == right)`
  left: `["Hallway 1", "Hallway 1"]`,
 right: `["Hallway 1"]`', tests/solution_test.rs:154:5

---- solution_test::test_parsing_cyrillic_rooms stdout ----
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', tests/solution_test.rs:301:71

---- solution_test::test_room_links stdout ----
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `["Hallway 1", "Hallway 1"]`,
 right: `["Hallway 1"]`', tests/solution_test.rs:117:5


failures:
    solution_test::test_adding_rooms_2
    solution_test::test_cyrillic_room_names
    solution_test::test_finding_a_direct_path
    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_cyrillic_rooms
    solution_test::test_room_links

test result: FAILED. 5 passed; 10 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

error: test failed, to rerun pass '--test solution_test'

История (1 версия и 0 коментара)

Павел качи първо решение на 11.01.2022 09:56 (преди над 3 години)