Решение на Dungeons and Compilers от Николай Шиваров

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

Към профила на Николай Шиваров

Резултати

  • 19 точки от тестове
  • 0 бонус точки
  • 19 точки общо
  • 14 успешни тест(а)
  • 1 неуспешни тест(а)

Код

/// Различните грешки, които ще очакваме да върнете като резултат от някои невалидни операции.
/// Повече детайли по-долу.
///
use std::io::BufRead;
use std::collections::HashMap;
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)]
pub enum Direction {
North,
South,
East,
West,
}
/// Една стая в подземията. Дефинира се само с име, макар че в по-интересна имплементация може да
/// държи item-и, противници...
///
pub struct Room {
pub name: String,
pub neighbours: HashMap<String,String>
// Каквито други полета ви трябват
}
/// Контейнер за стаите и не само. Ще работим предимно със тази структура.
///
pub struct Dungeon {
// Каквито полета ви трябват
pub hash: HashMap<String,Room>
}
impl Dungeon {
/// Конструиране на празен Dungeon, в който няма никакви стаи.
///
pub fn new() -> Self {
Dungeon{hash:HashMap::new(),}
}
/// Добавяне на стая към Dungeon с име `name`. Връща `Ok(())` при успех. Ако вече има стая с
/// такова име, очакваме да върнете `Errors::DuplicateRoom` с името.
///
pub fn add_room(&mut self, name: &str) -> Result<(), Errors> {
if self.hash.contains_key(&name.to_string()){
return Err(Errors::DuplicateRoom(name.to_string()))
}
self.hash.insert(name.to_string(), Room{name:name.to_string(),neighbours:HashMap::new(),});
return Ok(())
}
/// Прочитане на дадена стая -- когато извикаме `get_room`, очакваме reference към `Room`
/// структурата с това име.
///
/// Ако няма такава стая, очакваме `Errors::UnknownRoom` с подаденото име.
///
pub fn get_room(&self, room_name: &str) -> Result<&Room, Errors> {
if self.hash.contains_key(&room_name.to_string()){
return Ok(&self.hash[&room_name.to_string()])
}
Err(Errors::UnknownRoom(room_name.to_string()))
}
/// Добавяне на съсед на дадена стая. След извикването на функцията, очакваме стаята с име
/// `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.hash.contains_key(&room_name.to_string()){
return Err(Errors::UnknownRoom(room_name.to_string()))
}
if !self.hash.contains_key(&other_room_name.to_string()){
return Err(Errors::UnknownRoom(other_room_name.to_string()))
}
match direction{
Direction::North => self.hash.get_mut(&room_name.to_string()).unwrap().neighbours.insert("North".to_string(),other_room_name.to_string()),
Direction::South => self.hash.get_mut(&room_name.to_string()).unwrap().neighbours.insert("South".to_string(),other_room_name.to_string()),
Direction::East => self.hash.get_mut(&room_name.to_string()).unwrap().neighbours.insert("East".to_string(),other_room_name.to_string()),
Direction::West => self.hash.get_mut(&room_name.to_string()).unwrap().neighbours.insert("West".to_string(),other_room_name.to_string()),
};
match direction{
Direction::North => self.hash.get_mut(&other_room_name.to_string()).unwrap().neighbours.insert("South".to_string(),room_name.to_string()),
Direction::South => self.hash.get_mut(&other_room_name.to_string()).unwrap().neighbours.insert("North".to_string(),room_name.to_string()),
Direction::East => self.hash.get_mut(&other_room_name.to_string()).unwrap().neighbours.insert("West".to_string(),room_name.to_string()),
Direction::West => self.hash.get_mut(&other_room_name.to_string()).unwrap().neighbours.insert("East".to_string(),room_name.to_string()),
};
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.hash.contains_key(&room_name.to_string()){
return Err(Errors::UnknownRoom(room_name.to_string()))
}
let dir = match direction{
Direction::South => String::from("South"),
Direction::North => String::from("North"),
Direction::East => String::from("East"),
Direction::West => String::from("West"),
};
if !self.hash[&room_name.to_string()].neighbours.contains_key(&dir){
return Ok(None);
}
return Ok(Some(&self.hash[&self.hash[&room_name.to_string()].neighbours[&dir]]));
}
}
impl Dungeon {
/// Прочитаме структурата на dungeon от нещо, което имплементира `BufRead`. Това може да е
/// файл, или, ако тестваме, може да е просто колекция от байтове.
///
/// Успешен резултат връща новосъздадения dungeon, пакетиран в `Ok`.
///
/// Вижте по-долу за обяснение на грешките, които очакваме.
///
pub fn from_reader<B: BufRead>(reader: B) -> Result<Self, Errors> {
let mut dungeon=Dungeon::new();
let mut flag1=true;
let mut flag2=true;
let mut flag3=true;
let mut is_empty=true;
let mut count=0;
for iter in reader.lines() {
count+=1;
is_empty=false;
let line = match iter {
Err(err) => {
return Err(Errors::IoError(err));
}
Ok(result) => result,
};
if flag1{
if !line.eq("## Rooms"){
return Err(Errors::LineParseError { line_number: 1})
}
flag1=false;
continue;
}
if flag2 && line.eq(""){
flag2=false;
continue;
}
if flag2{
let v: Vec<char> = line.chars().collect();
if v[0]!='-' && v[1]!=' '{
return Err(Errors::LineParseError { line_number: count});
}
let lin= &line[2..];
dungeon.add_room(&lin)?;
continue;
}
if flag3{
if !line.eq("## Links"){
return Err(Errors::LineParseError { line_number: count});
}
flag3=false;
continue;
}
let v: Vec<char> = line.chars().collect();
if v[0]!='-' && v[1]!=' '{
return Err(Errors::LineParseError { line_number: count});
}
let mut flag11= true;
let mut flag12= false;
let mut first_end=0;
let mut second_begining=0;
let mut dir_beg=0;
let mut dir_end=0;
for i in 2..v.len(){
if flag11 && v[i]==' ' && v[i+1]=='-' && v[i+2]=='>' && v[i+3]==' '{
first_end=i;
dir_beg=i+4;
flag11=false;
continue;
}
if flag11 { continue };
if flag12 { continue };
if v[i]==' ' && v[i+1]=='-' && v[i+2]=='>' && v[i+3]==' '{
dir_end=i;
second_begining=i+4;
flag12=true;
continue;
}
};
if first_end==0 || second_begining==0{
return Err(Errors::LineParseError { line_number: count});
}
let city1=&line[2..first_end];
let dir=&line[dir_beg..dir_end];
let city2=&line[second_begining..v.len()];
let dire = match dir{
"South"=> Direction::South,
"West"=> Direction::West,
"East"=> Direction::East,
"North"=> Direction::North,
_ => return Err(Errors::DirectionParseError(dir.to_string())),
};
dungeon.set_link(city1,dire,city2)?;
}
if is_empty{
return Err(Errors::LineParseError{line_number:0})
}
return 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> {
if !self.hash.contains_key(start_room_name){
return Err(Errors::UnknownRoom(start_room_name.to_string()));
}
if !self.hash.contains_key(end_room_name){
return Err(Errors::UnknownRoom(end_room_name.to_string()));
}
if start_room_name==end_room_name{
let mut v:Vec<&Room>=Vec::new();
v.push(&self.hash[start_room_name]);
return Ok(Some(v));
}
let mut visited:HashMap::<String,bool>=HashMap::new();
let mut parent:HashMap::<String,String>=HashMap::new();
let mut q:VecDeque<String>=VecDeque::new();
q.push_back(start_room_name.to_string());
visited.insert(start_room_name.to_string(),true);
while !q.is_empty(){
let elem=q.pop_front().unwrap();
if self.hash[&elem].neighbours.contains_key("West"){
if !visited.contains_key(&self.hash[&elem].neighbours["West"].clone()){
q.push_back(self.hash[&elem].neighbours["West"].clone());
visited.insert(self.hash[&elem].neighbours["West"].clone(),true);
parent.insert(self.hash[&elem].neighbours["West"].clone(),elem.clone());
}
}
if self.hash[&elem].neighbours.contains_key("East"){
if !visited.contains_key(&self.hash[&elem].neighbours["East"].clone()){
q.push_back(self.hash[&elem].neighbours["East"].clone());
visited.insert(self.hash[&elem].neighbours["East"].clone(),true);
parent.insert(self.hash[&elem].neighbours["East"].clone(),elem.clone());
}
}
if self.hash[&elem].neighbours.contains_key("North"){
if !visited.contains_key(&self.hash[&elem].neighbours["North"].clone()){
q.push_back(self.hash[&elem].neighbours["North"].clone());
visited.insert(self.hash[&elem].neighbours["North"].clone(),true);
parent.insert(self.hash[&elem].neighbours["North"].clone(),elem.clone());
}
}
if self.hash[&elem].neighbours.contains_key("South"){
if !visited.contains_key(&self.hash[&elem].neighbours["South"].clone()){
q.push_back(self.hash[&elem].neighbours["South"].clone());
visited.insert(self.hash[&elem].neighbours["South"].clone(),true);
parent.insert(self.hash[&elem].neighbours["South"].clone(),elem.clone());
}
}
}
if !parent.contains_key(end_room_name){
return Ok(None);
}
let mut v:Vec<&Room>=Vec::new();
let mut curr=end_room_name.clone();
while curr!=start_room_name{
v.push(&self.hash[curr]);
curr=&parent[curr];
}
v.push(&self.hash[curr]);
return Ok(Some(v.into_iter().rev().collect()));
}
}

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

Compiling solution v0.1.0 (/tmp/d20220116-3533338-83sm6a/solution)
    Finished test [unoptimized + debuginfo] target(s) in 3.79s
     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 ... 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_parsing_cyrillic_rooms stdout ----
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: DirectionParseError(" -> ")', tests/solution_test.rs:294:72
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    solution_test::test_parsing_cyrillic_rooms

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

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

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

Николай качи първо решение на 09.01.2022 23:43 (преди почти 4 години)

Николай качи решение на 09.01.2022 23:56 (преди почти 4 години)

/// Различните грешки, които ще очакваме да върнете като резултат от някои невалидни операции.
/// Повече детайли по-долу.
///
use std::io::BufRead;
use std::collections::HashMap;
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)]
pub enum Direction {
North,
South,
East,
West,
}
/// Една стая в подземията. Дефинира се само с име, макар че в по-интересна имплементация може да
/// държи item-и, противници...
///
pub struct Room {
pub name: String,
pub neighbours: HashMap<String,String>
// Каквито други полета ви трябват
}
/// Контейнер за стаите и не само. Ще работим предимно със тази структура.
///
pub struct Dungeon {
// Каквито полета ви трябват
pub hash: HashMap<String,Room>
}
impl Dungeon {
/// Конструиране на празен Dungeon, в който няма никакви стаи.
///
pub fn new() -> Self {
Dungeon{hash:HashMap::new(),}
}
/// Добавяне на стая към Dungeon с име `name`. Връща `Ok(())` при успех. Ако вече има стая с
/// такова име, очакваме да върнете `Errors::DuplicateRoom` с името.
///
pub fn add_room(&mut self, name: &str) -> Result<(), Errors> {
if self.hash.contains_key(&name.to_string()){
return Err(Errors::DuplicateRoom(name.to_string()))
}
self.hash.insert(name.to_string(), Room{name:name.to_string(),neighbours:HashMap::new(),});
return Ok(())
}
/// Прочитане на дадена стая -- когато извикаме `get_room`, очакваме reference към `Room`
/// структурата с това име.
///
/// Ако няма такава стая, очакваме `Errors::UnknownRoom` с подаденото име.
///
pub fn get_room(&self, room_name: &str) -> Result<&Room, Errors> {
if self.hash.contains_key(&room_name.to_string()){
return Ok(&self.hash[&room_name.to_string()])
}
Err(Errors::UnknownRoom(room_name.to_string()))
}
/// Добавяне на съсед на дадена стая. След извикването на функцията, очакваме стаята с име
/// `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.hash.contains_key(&room_name.to_string()){
return Err(Errors::UnknownRoom(room_name.to_string()))
}
if !self.hash.contains_key(&other_room_name.to_string()){
return Err(Errors::UnknownRoom(other_room_name.to_string()))
}
match direction{
Direction::North => self.hash.get_mut(&room_name.to_string()).unwrap().neighbours.insert("North".to_string(),other_room_name.to_string()),
Direction::South => self.hash.get_mut(&room_name.to_string()).unwrap().neighbours.insert("South".to_string(),other_room_name.to_string()),
Direction::East => self.hash.get_mut(&room_name.to_string()).unwrap().neighbours.insert("East".to_string(),other_room_name.to_string()),
Direction::West => self.hash.get_mut(&room_name.to_string()).unwrap().neighbours.insert("West".to_string(),other_room_name.to_string()),
};
match direction{
Direction::North => self.hash.get_mut(&other_room_name.to_string()).unwrap().neighbours.insert("South".to_string(),room_name.to_string()),
Direction::South => self.hash.get_mut(&other_room_name.to_string()).unwrap().neighbours.insert("North".to_string(),room_name.to_string()),
Direction::East => self.hash.get_mut(&other_room_name.to_string()).unwrap().neighbours.insert("West".to_string(),room_name.to_string()),
Direction::West => self.hash.get_mut(&other_room_name.to_string()).unwrap().neighbours.insert("East".to_string(),room_name.to_string()),
};
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.hash.contains_key(&room_name.to_string()){
return Err(Errors::UnknownRoom(room_name.to_string()))
}
let dir = match direction{
Direction::South => String::from("South"),
Direction::North => String::from("North"),
Direction::East => String::from("East"),
Direction::West => String::from("West"),
};
if !self.hash[&room_name.to_string()].neighbours.contains_key(&dir){
return Ok(None);
}
return Ok(Some(&self.hash[&self.hash[&room_name.to_string()].neighbours[&dir]]));
}
}
impl Dungeon {
/// Прочитаме структурата на dungeon от нещо, което имплементира `BufRead`. Това може да е
/// файл, или, ако тестваме, може да е просто колекция от байтове.
///
/// Успешен резултат връща новосъздадения dungeon, пакетиран в `Ok`.
///
/// Вижте по-долу за обяснение на грешките, които очакваме.
///
pub fn from_reader<B: BufRead>(reader: B) -> Result<Self, Errors> {
let mut dungeon=Dungeon::new();
let mut flag1=true;
let mut flag2=true;
let mut flag3=true;
let mut is_empty=true;
let mut count=0;
for iter in reader.lines() {
count+=1;
is_empty=false;
- //let line=iter.unwrap();
+
let line = match iter {
Err(err) => {
return Err(Errors::IoError(err));
}
Ok(result) => result,
};
if flag1{
if !line.eq("## Rooms"){
return Err(Errors::LineParseError { line_number: 1})
}
flag1=false;
continue;
}
if flag2 && line.eq(""){
flag2=false;
continue;
}
if flag2{
let v: Vec<char> = line.chars().collect();
if v[0]!='-' && v[1]!=' '{
return Err(Errors::LineParseError { line_number: count});
}
let lin= &line[2..];
dungeon.add_room(&lin)?;
continue;
}
if flag3{
if !line.eq("## Links"){
return Err(Errors::LineParseError { line_number: count});
}
flag3=false;
continue;
}
let v: Vec<char> = line.chars().collect();
if v[0]!='-' && v[1]!=' '{
return Err(Errors::LineParseError { line_number: count});
}
let mut flag11= true;
let mut flag12= false;
let mut first_end=0;
let mut second_begining=0;
let mut dir_beg=0;
let mut dir_end=0;
for i in 2..v.len(){
if flag11 && v[i]==' ' && v[i+1]=='-' && v[i+2]=='>' && v[i+3]==' '{
first_end=i;
dir_beg=i+4;
flag11=false;
continue;
}
if flag11 { continue };
if flag12 { continue };
if v[i]==' ' && v[i+1]=='-' && v[i+2]=='>' && v[i+3]==' '{
dir_end=i;
second_begining=i+4;
flag12=true;
continue;
}
};
if first_end==0 || second_begining==0{
return Err(Errors::LineParseError { line_number: count});
}
let city1=&line[2..first_end];
let dir=&line[dir_beg..dir_end];
let city2=&line[second_begining..v.len()];
let dire = match dir{
"South"=> Direction::South,
"West"=> Direction::West,
"East"=> Direction::East,
"North"=> Direction::North,
_ => return Err(Errors::DirectionParseError(dir.to_string())),
};
dungeon.set_link(city1,dire,city2)?;
}
if is_empty{
return Err(Errors::LineParseError{line_number:0})
}
return 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> {
if !self.hash.contains_key(start_room_name){
return Err(Errors::UnknownRoom(start_room_name.to_string()));
}
if !self.hash.contains_key(end_room_name){
return Err(Errors::UnknownRoom(end_room_name.to_string()));
}
if start_room_name==end_room_name{
let mut v:Vec<&Room>=Vec::new();
v.push(&self.hash[start_room_name]);
return Ok(Some(v));
}
let mut visited:HashMap::<String,bool>=HashMap::new();
let mut parent:HashMap::<String,String>=HashMap::new();
let mut q:VecDeque<String>=VecDeque::new();
q.push_back(start_room_name.to_string());
visited.insert(start_room_name.to_string(),true);
while !q.is_empty(){
let elem=q.pop_front().unwrap();
if self.hash[&elem].neighbours.contains_key("West"){
if !visited.contains_key(&self.hash[&elem].neighbours["West"].clone()){
q.push_back(self.hash[&elem].neighbours["West"].clone());
visited.insert(self.hash[&elem].neighbours["West"].clone(),true);
parent.insert(self.hash[&elem].neighbours["West"].clone(),elem.clone());
}
}
if self.hash[&elem].neighbours.contains_key("East"){
if !visited.contains_key(&self.hash[&elem].neighbours["East"].clone()){
q.push_back(self.hash[&elem].neighbours["East"].clone());
visited.insert(self.hash[&elem].neighbours["East"].clone(),true);
parent.insert(self.hash[&elem].neighbours["East"].clone(),elem.clone());
}
}
if self.hash[&elem].neighbours.contains_key("North"){
if !visited.contains_key(&self.hash[&elem].neighbours["North"].clone()){
q.push_back(self.hash[&elem].neighbours["North"].clone());
visited.insert(self.hash[&elem].neighbours["North"].clone(),true);
parent.insert(self.hash[&elem].neighbours["North"].clone(),elem.clone());
}
}
if self.hash[&elem].neighbours.contains_key("South"){
if !visited.contains_key(&self.hash[&elem].neighbours["South"].clone()){
q.push_back(self.hash[&elem].neighbours["South"].clone());
visited.insert(self.hash[&elem].neighbours["South"].clone(),true);
parent.insert(self.hash[&elem].neighbours["South"].clone(),elem.clone());
}
}
}
if !parent.contains_key(end_room_name){
return Ok(None);
}
let mut v:Vec<&Room>=Vec::new();
let mut curr=end_room_name.clone();
while curr!=start_room_name{
v.push(&self.hash[curr]);
curr=&parent[curr];
}
v.push(&self.hash[curr]);
return Ok(Some(v.into_iter().rev().collect()));
}
}