Решение на Dungeons and Compilers от Симеон Михайлов

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

Към профила на Симеон Михайлов

Резултати

  • 16 точки от тестове
  • 0 бонус точки
  • 16 точки общо
  • 12 успешни тест(а)
  • 3 неуспешни тест(а)

Код

use std::collections::HashMap;
use std::io::BufRead;
use std::collections::VecDeque;
/// Различните грешки, които ще очакваме да върнете като резултат от някои невалидни операции.
/// Повече детайли по-долу.
///
#[derive(Debug)]
pub enum Errors {
DuplicateRoom(String),
UnknownRoom(String),
IoError(std::io::Error),
LineParseError { line_number: usize },
DirectionParseError(String),
}
/// Четирите посоки, в които може една стая да има съседи. Може да добавите още trait
/// имплементации, за да си улесните живота.
///
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub enum Direction {
North,
South,
East,
West,
}
/// Една стая в подземията. Дефинира се само с име, макар че в по-интересна имплементация може да
/// държи item-и, противници...
///
pub struct Room {
pub name: String,
pub links: HashMap<Direction,String>,
}
/// Контейнер за стаите и не само. Ще работим предимно със тази структура.
///
pub struct Dungeon {
pub rooms: HashMap<String, Room>,
// Каквито полета ви трябват
}
impl Dungeon {
/// Конструиране на празен Dungeon, в който няма никакви стаи.
///
pub fn new() -> Self {
let dungeon = Dungeon{rooms:HashMap::new()};
dungeon
}
/// Добавяне на стая към Dungeon с име `name`. Връща `Ok(())` при успех. Ако вече има стая с
/// такова име, очакваме да върнете `Errors::DuplicateRoom` с името.
///
pub fn add_room(&mut self, name: &str) -> Result<(), Errors> {
match self.rooms.get(&*name){
Some(x) => Err(Errors::DuplicateRoom(x.clone().name.clone())), //duplicate room
None => { //make new room
let room_name = String::from(name);
let new_room = Room { name: room_name.clone(), links: HashMap::new()};
self.rooms.insert(room_name.clone(),new_room);
Ok(())
}
}
}
/// Прочитане на дадена стая -- когато извикаме `get_room`, очакваме reference към `Room`
/// структурата с това име.
///
/// Ако няма такава стая, очакваме `Errors::UnknownRoom` с подаденото име.
///
pub fn get_room(&self, room_name: &str) -> Result<&Room, Errors> {
match self.rooms.get(&*room_name){
Some(x) => Ok( x),
None => Err(Errors::UnknownRoom(String::from(room_name)))
}
}
fn get_mut_room(&mut self, room_name: &str) -> Result<&mut Room, Errors> {
match self.rooms.get_mut(&*room_name){
Some(x) => Ok( x),
None => Err(Errors::UnknownRoom(String::from(room_name)))
}
}
/// Добавяне на съсед на дадена стая. След извикването на функцията, очакваме стаята с име
/// `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> {
{
if self.get_mut_room(room_name).is_err(){
return Err(Errors::UnknownRoom(String::from(room_name)))
}
if (&self).get_room(other_room_name).is_err(){
return Err(Errors::UnknownRoom(String::from(other_room_name)))
}
}
{
let room2_name: String = self.get_mut_room(other_room_name).unwrap().name.clone();
let room1: &mut Room = self.rooms.get_mut(&String::from(room_name)).unwrap();
match direction {
Direction::North => {
room1.links.insert(Direction::North,room2_name.clone());
},
Direction::East => {
room1.links.insert(Direction::East,room2_name.clone());
},
Direction::West => {
room1.links.insert(Direction::West,room2_name.clone());
},
Direction::South => {
room1.links.insert(Direction::South,room2_name.clone());
},
}
}
let room1_name: String = self.get_mut_room(room_name).unwrap().name.clone();
let room2: &mut Room = self.rooms.get_mut(&String::from(other_room_name)).unwrap();
match direction {
Direction::North => {
room2.links.insert(Direction::North,room1_name.clone());
},
Direction::East => {
room2.links.insert(Direction::East,room1_name.clone());
},
Direction::West => {
room2.links.insert(Direction::West,room1_name.clone());
},
Direction::South => {
room2.links.insert(Direction::South,room1_name.clone());
},
}
Ok(())
}
/// Четене на съседа на стаята с име `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> {
if self.get_room(room_name).is_err(){
return Err(Errors::UnknownRoom(String::from(room_name)))
}
let room: &Room = self.get_room(room_name).unwrap();
match room.links.get(&direction){
Some(x) => Ok(Some(self.get_room(x).unwrap())),
None => Ok(None),
}
}
}
impl Dungeon {
fn get_names_and_direction(input: &str, line_number: usize) -> Result<(&str,Direction,&str),Errors> {
//input doesn't have "- "
let v: Vec<&str> = input.split(" -> ").collect();
let dir: Direction;
match v.len() {
3 => match v[1]{
"North" => dir = Direction::North,
"Soutch" => dir = Direction::South,
"East" => dir = Direction::East,
"West" => dir = Direction::West,
x => return Err(Errors::DirectionParseError(x.to_string()))
},
_ => return Err(Errors::LineParseError { line_number: line_number })
}
Ok((v[0],dir,v[2]))
}
/// Прочитаме структурата на dungeon от нещо, което имплементира `BufRead`. Това може да е
/// файл, или, ако тестваме, може да е просто колекция от байтове.
///
/// Успешен резултат връща новосъздадения dungeon, пакетиран в `Ok`.
///
/// Вижте по-долу за обяснение на грешките, които очакваме.
///
pub fn from_reader<B: BufRead>(reader: B) -> Result<Self, Errors> {
let mut lines = reader.lines();
let mut line_number: usize = 0;
//looking for "## Rooms"
let line: Result<String, std::io::Error> = match lines.next(){
Some(x) => x,
None => return Err(Errors::LineParseError { line_number: line_number })
};
line_number += 1;
match line {
Ok(x) => {
if x != "## Rooms" {
return Err(Errors::LineParseError { line_number: line_number })
}
},
Err(y) => return Err(Errors::IoError(y))
}
let mut dungeon = Dungeon::new();
// loop for rooms
while let Some(line) = lines.next() {
line_number += 1;
match line {
Ok(x) => {
if x.len() > 2 {
if &x[..2] == "- " {
match dungeon.add_room(&x[2..]) {
Ok(_) => continue,
Err(x) => return Err(x)
};
}
else{
return Err(Errors::LineParseError { line_number: line_number })
}
}
else
if x.len() == 0{
break;
}
else{
return Err(Errors::LineParseError { line_number: line_number })
}
},
Err(y) => return Err(Errors::IoError(y))
}
}
//looking for "## Links"
let line: Result<String, std::io::Error> = match lines.next(){
Some(x) => x,
None => return Err(Errors::LineParseError { line_number: line_number })
};
line_number += 1;
match line {
Ok(x) => {
if x != "## Links" {
return Err(Errors::LineParseError { line_number: line_number })
}
},
Err(y) => return Err(Errors::IoError(y))
}
//loop for links
while let Some(line) = lines.next() {
line_number += 1;
match line {
Ok(x) => {
if x.len() > 12 {
if &x[..2] == "- " {
match Dungeon::get_names_and_direction(&x[2..],line_number) {
Ok(link_info) =>
match dungeon.set_link(&link_info.0,link_info.1,&link_info.2){
Ok(_) => (),
Err(e) => return Err(e)
},
Err(er) => return Err(er)
}
}
else{
return Err(Errors::LineParseError { line_number: line_number })
}
}
else{
return Err(Errors::LineParseError { line_number: line_number })
}
},
Err(y) => return Err(Errors::IoError(y))
}
}
Ok(dungeon)
}
}
impl Dungeon {
/// Търси път от `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> {
match self.get_room(start_room_name){
Ok(_) => (),
Err(e) => return Err(e)
}
match self.get_room(end_room_name){
Ok(_) => (),
Err(e) => return Err(e)
}
let mut visited_hm: HashMap<String,bool> = HashMap::new();
let mut path: Vec<&Room> = Vec::new();
let mut queue: VecDeque<String> = VecDeque::new();
visited_hm.insert(start_room_name.to_string().clone(),true);
queue.push_back(start_room_name.to_string().clone());
while !queue.is_empty() {
let current_room_name: String = queue.front().unwrap().clone();
path.push(self.get_room(&current_room_name[..]).unwrap());
queue.pop_front();
for (_dir, room) in path[path.len()-1].links.iter() {
match visited_hm.get(&self.get_room(room).unwrap().name){
Some(_) => (),
None => {
visited_hm.insert(self.get_room(room).unwrap().name.clone(),true);
queue.push_back(self.get_room(room).unwrap().name.clone());
}
}
}
}
if &path[path.len()-1].name[..] != end_room_name {
return Ok(None)
}
Ok(Some(path))
}
}

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

Compiling solution v0.1.0 (/tmp/d20220116-3533338-1w55oeu/solution)
    Finished test [unoptimized + debuginfo] target(s) in 3.73s
     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 ... 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 ... 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 ... ok

failures:

---- solution_test::test_adding_rooms_2 stdout ----
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', tests/solution_test.rs:97:77
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_parsing_cyrillic_rooms stdout ----
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', tests/solution_test.rs:301:71


failures:
    solution_test::test_adding_rooms_2
    solution_test::test_cyrillic_room_names
    solution_test::test_parsing_cyrillic_rooms

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

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

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

Симеон качи първо решение на 09.01.2022 23:52 (преди почти 4 години)