Решение на Dungeons and Compilers от Марио Лалов

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

Към профила на Марио Лалов

Резултати

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

Код

use std::collections::HashMap;
use std::collections::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)]
pub enum Direction {
North,
South,
East,
West,
}
#[derive(Clone)]
pub struct Room {
pub name: String,
pub n_room: String,
pub s_room: String,
pub e_room: String,
pub w_room: String,
}
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> {
match self.get_room(name) {
//check if there is a room with the same name
Ok(_) => Err(Errors::DuplicateRoom(String::from(name))),
Err(_) => {
self.rooms.insert(
String::from(name),
Room {
name: String::from(name),
n_room: String::new(),
s_room: String::new(),
e_room: String::new(),
w_room: String::new(),
},
);
Ok(())
}
}
}
pub fn get_room(&self, room_name: &str) -> Result<&Room, Errors> {
for (name, mut room) in self.rooms.iter() {
if room_name == name {
return Ok(&mut room);
}
}
Err(Errors::UnknownRoom(String::from(room_name)))
}
pub fn set_link(
&mut self,
room_name: &str,
direction: Direction,
other_room_name: &str,
) -> Result<(), Errors> {
//make sure that the input rooms exist
match self.get_room(room_name) {
Ok(_) => (),
Err(err) => return Err(err),
}
match self.get_room(other_room_name) {
Ok(_) => (),
Err(err) => return Err(err),
}
match direction {
Direction::North => {
self.rooms.get_mut(room_name).unwrap().n_room = String::from(other_room_name);
self.rooms.get_mut(other_room_name).unwrap().s_room = String::from(room_name);
}
Direction::South => {
self.rooms.get_mut(room_name).unwrap().s_room = String::from(other_room_name);
self.rooms.get_mut(other_room_name).unwrap().n_room = String::from(room_name);
}
Direction::East => {
self.rooms.get_mut(room_name).unwrap().e_room = String::from(other_room_name);
self.rooms.get_mut(other_room_name).unwrap().w_room = String::from(room_name);
}
Direction::West => {
self.rooms.get_mut(room_name).unwrap().w_room = String::from(other_room_name);
self.rooms.get_mut(other_room_name).unwrap().e_room = String::from(room_name);
}
}
Ok(())
}
pub fn get_next_room(
&self,
room_name: &str,
direction: Direction,
) -> Result<Option<&Room>, Errors> {
//validate input
match self.get_room(room_name) {
Ok(_) => (),
Err(err) => return Err(err),
};
match direction {
Direction::North => {
let n_name = self.get_room(room_name).unwrap().n_room.clone();
if n_name.is_empty() {
Ok(None)
} else {
Ok(Some(self.get_room(&n_name).unwrap()))
}
}
Direction::South => {
let s_name = self.get_room(room_name).unwrap().s_room.clone();
if s_name.is_empty() {
Ok(None)
} else {
Ok(Some(self.get_room(&s_name).unwrap()))
}
}
Direction::East => {
let e_name = self.get_room(room_name).unwrap().e_room.clone();
if e_name.is_empty() {
Ok(None)
} else {
Ok(Some(self.get_room(&e_name).unwrap()))
}
}
Direction::West => {
let w_name = self.get_room(room_name).unwrap().w_room.clone();
if w_name.is_empty() {
Ok(None)
} else {
Ok(Some(self.get_room(&w_name).unwrap()))
}
}
}
}
pub fn from_reader<B: BufRead>(reader: B) -> Result<Self, Errors> {
//line counter
let mut cnt = 0;
let mut dungeon = Dungeon::new();
let lines = reader.lines().map(|l| l.unwrap());
//markers
let mut first_half = true;
let mut first = true;
for l in lines {
cnt += 1;
if first_half {
if first {
if l != "## Rooms" {
return Err(Errors::LineParseError { line_number: cnt });
} else {
first = false;
continue;
}
}
//end of #rooms section
if l.is_empty() {
first_half = false;
first = true;
continue;
}
match match_prefix("- ", &l) {
Some(s) => match dungeon.add_room(&s) {
Ok(()) => (),
Err(err) => return Err(err),
},
None => return Err(Errors::LineParseError { line_number: cnt }),
};
} else {
if first {
if l != "## Links" {
return Err(Errors::LineParseError { line_number: cnt });
} else {
first = false;
continue;
}
}
let line = match match_prefix("- ", &l) {
Some(s) => s,
None => return Err(Errors::LineParseError { line_number: cnt }),
};
let words: Vec<&str> = line.split("->").collect();
if words.len() != 3 {
return Err(Errors::LineParseError { line_number: cnt });
}
match dungeon.set_link(
words[0],
match words[1] {
"North" => Direction::North,
"South" => Direction::South,
"East" => Direction::East,
"West" => Direction::West,
s => return Err(Errors::DirectionParseError(String::from(s))),
},
words[2],
) {
Ok(()) => (),
Err(err) => return Err(err),
};
}
}
Ok(dungeon)
}
pub fn find_path(
&self,
start_room_name: &str,
end_room_name: &str,
) -> Result<Option<Vec<&Room>>, Errors> {
//check if path is 0
if start_room_name == end_room_name{
return Ok(Some(Vec::new()));
}
//check if rooms exist
match self.get_room(start_room_name){
Ok(_) => (),
Err(err) => return Err(err)
};
match self.get_room(end_room_name){
Ok(_) => (),
Err(err) => return Err(err)
};
let mut cur_room: String;
let mut path = Vec::new();
let mut queue = VecDeque::new();
queue.push_back(start_room_name);
let mut visited = HashMap::new();
visited.insert(start_room_name, true);
let mut parents: HashMap<String, String>;
parents = HashMap::new();
loop {
if queue.len() < 1 {
break Ok(None);
}
cur_room = String::from(queue.pop_front().unwrap());
if cur_room == end_room_name {
//backtrace path using parents
loop {
path.push(self.get_room(&cur_room).unwrap());
if cur_room == start_room_name {
return Ok(Some(path));
}
cur_room = String::from(parents.get(&cur_room).unwrap());
}
} else {
//continue with neighbors
match self.get_next_room(&cur_room, Direction::North) {
Ok(Some(room)) => {
parents.insert(String::from(&room.name), String::from(&cur_room));
queue.push_back(&room.name);
}
_ => (),
};
match self.get_next_room(&cur_room, Direction::South) {
Ok(Some(room)) => {
parents.insert(String::from(&room.name), String::from(&cur_room));
queue.push_back(&room.name);
}
_ => (),
};
match self.get_next_room(&cur_room, Direction::East) {
Ok(Some(room)) => {
parents.insert(String::from(&room.name), String::from(&cur_room));
queue.push_back(&room.name);
}
_ => (),
};
match self.get_next_room(&cur_room, Direction::West) {
Ok(Some(room)) => {
parents.insert(String::from(&room.name), String::from(&cur_room));
queue.push_back(&room.name);
}
_ => (),
};
}
}
}
}
fn match_prefix(prefix: &str, input: &str) -> Option<String> {
if input.contains(prefix) {
let mut input2 = input.replace(prefix, " ");
input2.retain(|c| !c.is_whitespace());
return Some(input2);
} else {
None
}
}

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

Compiling solution v0.1.0 (/tmp/d20220116-3533338-1du1l41/solution)
    Finished test [unoptimized + debuginfo] target(s) in 5.34s
     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 ... FAILED
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 ... ok
test solution_test::test_invalid_parsing ... FAILED
test solution_test::test_io_error ... FAILED
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_direct_path stdout ----
thread '<unnamed>' panicked at 'assertion failed: `(left == right)`
  left: `["Treasure Room", "Entrance"]`,
 right: `["Entrance", "Treasure Room"]`', tests/solution_test.rs:314:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `["Treasure Room", "Entrance"]`,
 right: `["Entrance", "Treasure Room"]`', tests/solution_test.rs:306:5

---- solution_test::test_finding_a_reflexive_path stdout ----
thread '<unnamed>' panicked at 'index out of bounds: the len is 0 but the index is 0', tests/solution_test.rs:361:20
thread 'main' panicked at 'index out of bounds: the len is 0 but the index is 0', tests/solution_test.rs:354:5

---- solution_test::test_finding_an_indirect_path stdout ----
thread '<unnamed>' panicked at 'assertion failed: `(left == right)`
  left: `"Treasure Room"`,
 right: `"Entrance"`', tests/solution_test.rs:347:9
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `"Treasure Room"`,
 right: `"Entrance"`', tests/solution_test.rs:320:5

---- solution_test::test_invalid_parsing stdout ----
thread 'main' panicked at 'assertion failed: matches!(Dungeon :: from_reader(\"\".as_bytes()),\n         Err(Errors :: LineParseError { line_number : 0 }))', tests/solution_test.rs:276:5

---- 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-1du1l41/solution/src/lib.rs:167:46
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Custom { kind: Other, error: "fill_buf error!" }', tests/solution_test.rs:194:5


failures:
    solution_test::test_finding_a_direct_path
    solution_test::test_finding_a_reflexive_path
    solution_test::test_finding_an_indirect_path
    solution_test::test_invalid_parsing
    solution_test::test_io_error

test result: FAILED. 10 passed; 5 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 11:30 (преди над 3 години)