Решение на Dungeons and Compilers от Станислав Димов

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

Към профила на Станислав Димов

Резултати

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

Код

use std::collections::HashMap;
use std::collections::HashSet;
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),
}
/// Четирите посоки, в които може една стая да има съседи. Може да добавите още trait
/// имплементации, за да си улесните живота.
///
#[derive(Clone, Copy)]
pub enum Direction {
North,
South,
East,
West,
}
impl Direction {
pub fn from(direction: &str) -> Result<Self, Errors> {
match direction {
"East" => Ok(Direction::East),
"West" => Ok(Direction::West),
"North" => Ok(Direction::North),
"South" => Ok(Direction::South),
_ => Err(Errors::DirectionParseError(String::from(direction))),
}
}
}
/// Една стая в подземията. Дефинира се само с име, макар че в по-интересна имплементация може да
/// държи item-и, противници...
///
#[derive(Debug)]
pub struct Room {
pub name: String,
pub neighbours: [String; 4],
}
impl Room {
pub fn new(name: String) -> Self {
Room {
name,
neighbours: [
String::from(""),
String::from(""),
String::from(""),
String::from(""),
],
}
}
}
/// Контейнер за стаите и не само. Ще работим предимно със тази структура.
///
pub struct Dungeon {
rooms: HashMap<String, Room>,
}
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> {
match self.rooms.contains_key(name) {
true => Err(Errors::DuplicateRoom(String::from(
"Room already in dungeon!",
))),
false => {
self.rooms
.insert(name.to_string(), Room::new(name.to_string()));
Ok(())
}
}
}
/// Прочитане на дадена стая -- когато извикаме `get_room`, очакваме reference към `Room`
/// структурата с това име.
///
/// Ако няма такава стая, очакваме `Errors::UnknownRoom` с подаденото име.
///
pub fn get_room(&self, room_name: &str) -> Result<&Room, Errors> {
match self.rooms.contains_key(room_name) {
true => Ok(self.rooms.get(room_name).unwrap()),
false => Err(Errors::UnknownRoom(String::from("Unknown 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> {
if !(self.rooms.contains_key(room_name) && self.rooms.contains_key(other_room_name)) {
Err(Errors::UnknownRoom(String::from(room_name)))
} else {
match direction {
Direction::East => {
self.rooms.get_mut(room_name).unwrap().neighbours[0] =
String::from(other_room_name);
self.rooms.get_mut(other_room_name).unwrap().neighbours[1] =
String::from(room_name);
Ok(())
}
Direction::West => {
self.rooms.get_mut(room_name).unwrap().neighbours[1] =
String::from(other_room_name);
self.rooms.get_mut(other_room_name).unwrap().neighbours[0] = String::from(room_name);
Ok(())
}
Direction::North => {
self.rooms.get_mut(room_name).unwrap().neighbours[2] =
String::from(other_room_name);
self.rooms.get_mut(other_room_name).unwrap().neighbours[3] = String::from(room_name);
Ok(())
}
Direction::South => {
self.rooms.get_mut(room_name).unwrap().neighbours[3] =
String::from(other_room_name);
self.rooms.get_mut(other_room_name).unwrap().neighbours[2] = String::from(room_name);
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.rooms.contains_key(room_name) {
Err(Errors::UnknownRoom(String::from(room_name)))
} else {
match direction {
Direction::East => Ok(Some(
self.rooms
.get(&self.rooms.get(room_name).unwrap().neighbours[0])
.unwrap(),
)),
Direction::West => Ok(Some(
self.rooms
.get(&self.rooms.get(room_name).unwrap().neighbours[1])
.unwrap(),
)),
Direction::North => Ok(Some(
self.rooms
.get(&self.rooms.get(room_name).unwrap().neighbours[2])
.unwrap(),
)),
Direction::South => Ok(Some(
self.rooms
.get(&self.rooms.get(room_name).unwrap().neighbours[3])
.unwrap(),
)),
}
}
}
pub fn from_reader<B: BufRead>(reader: B) -> Result<Self, Errors> {
let mut dungeon: Dungeon = Dungeon::new();
let ref mut line: String = String::from("");
let mut mut_reader = reader;
match mut_reader.read_line(line) {
Ok(_) => {
if line.to_string().trim().ne("## Rooms") {
return Err(Errors::LineParseError { line_number: 1 });
}
}
Err(error) => return Err(Errors::IoError(error)),
}
let mut counter = 1;
let result_rooms = loop {
counter += 1;
*line = String::from("");
match mut_reader.read_line(line) {
Ok(_) => {
if line.to_string() == "\n" {
break Ok(());
} else {
match Dungeon::match_prefix("-", line.trim()) {
Some(room_name) => {
dungeon.add_room(&room_name.trim().to_string()).unwrap()
}
None => {
return Err(Errors::LineParseError {
line_number: counter,
})
}
}
}
}
Err(error) => return Err(Errors::IoError(error)),
}
};
*line = String::from("");
match mut_reader.read_line(line) {
Ok(_) => match line.to_string().trim() {
"## Links" => counter += 1,
_ => {
return Err(Errors::LineParseError {
line_number: counter,
})
}
},
Err(error) => return Err(Errors::IoError(error)),
}
let result_links = loop {
counter += 1;
*line = String::from("");
let bytes = mut_reader.read_line(line);
if bytes.unwrap() == 0 {
break Ok(());
} else {
match Dungeon::match_prefix("-", line.trim()) {
Some(links) => dungeon
.set_link(
links.split("->").collect::<Vec<&str>>()[0].trim(),
Direction::from(links.split("->").collect::<Vec<&str>>()[1].trim())
.unwrap(),
links.split("->").collect::<Vec<&str>>()[2].trim(),
)
.unwrap(),
None => {
return Err(Errors::LineParseError {
line_number: counter,
})
}
}
}
};
match (result_rooms, result_links) {
(Ok(_), Ok(_)) => Ok(dungeon),
(Err(error), _) => Err(error),
(Ok(_), Err(error)) => Err(error),
}
}
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: HashSet<String> = HashSet::new();
let mut parent: HashMap<String, String> = HashMap::new();
let mut exists: bool = false;
let start_room = &self.get_room(start_room_name).unwrap().name;
queue.push_back(start_room.to_string());
while !queue.is_empty() {
let current_room_name = queue.front_mut().unwrap().clone();
if !visited.contains(&current_room_name) {
if current_room_name.eq(end_room_name) {
exists = true;
break;
}
visited.insert(current_room_name.to_string());
for neighbour in &self.get_room(&current_room_name).unwrap().neighbours {
if neighbour.ne("") {
parent.insert(neighbour.to_string(), current_room_name.to_string());
queue.push_back(neighbour.to_string());
}
}
}
queue.pop_front();
}
match exists {
true => self.get_path(start_room_name, end_room_name, parent),
false => Ok(None),
}
}
fn match_prefix<'a, 'b>(prefix: &'a str, input: &'b str) -> Option<String> {
if prefix.eq(input.split(" ").collect::<Vec<&str>>()[0]) {
Some(input.replacen("-", "", 1).to_string())
} else {
None
}
}
fn get_path(
&self,
start_room_name: &str,
end_room_name: &str,
parent: HashMap<String, String>,
) -> Result<Option<Vec<&Room>>, Errors> {
let mut path: Vec<&Room> = Vec::new();
let mut current_room_name = end_room_name;
while current_room_name.ne(start_room_name) {
path.push(self.get_room(current_room_name).unwrap());
current_room_name = parent.get(current_room_name).unwrap();
}
path.push(self.get_room(current_room_name).unwrap());
path.reverse();
Ok(Some(path))
}
}

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

Compiling solution v0.1.0 (/tmp/d20220116-3533338-1o0my8o/solution)
    Finished test [unoptimized + debuginfo] target(s) in 3.74s
     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 ... 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 ... FAILED
test solution_test::test_parsing_cyrillic_rooms ... ok
test solution_test::test_parsing_no_rooms_or_links ... FAILED
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_finding_an_indirect_path stdout ----
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Timeout', tests/solution_test.rs:320:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

---- 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(\"\".as_bytes()),\n         Err(Errors :: LineParseError { line_number : 0 }))', tests/solution_test.rs:276:5

---- solution_test::test_overwriting_a_room_link stdout ----
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', src/lib.rs:182:26

---- solution_test::test_parsing_no_rooms_or_links stdout ----
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', src/lib.rs:182:26

---- solution_test::test_room_links stdout ----
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', src/lib.rs:182:26


failures:
    solution_test::test_finding_an_indirect_path
    solution_test::test_finding_no_path
    solution_test::test_invalid_parsing
    solution_test::test_overwriting_a_room_link
    solution_test::test_parsing_no_rooms_or_links
    solution_test::test_room_links

test result: FAILED. 9 passed; 6 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.01s

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

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

Станислав качи първо решение на 09.01.2022 17:57 (преди почти 4 години)

Станислав качи решение на 09.01.2022 17:59 (преди почти 4 години)

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)]
pub enum Direction {
North,
South,
East,
West,
}
-impl Direction{
- pub fn from(direction:&str) -> Result<Self,Errors>{
+impl Direction {
+ pub fn from(direction: &str) -> Result<Self, Errors> {
match direction {
- "East" => Ok(Direction::East),
- "West" => Ok(Direction::West),
+ "East" => Ok(Direction::East),
+ "West" => Ok(Direction::West),
"North" => Ok(Direction::North),
"South" => Ok(Direction::South),
- _ => Err(Errors::DirectionParseError(String::from(direction)))
+ _ => Err(Errors::DirectionParseError(String::from(direction))),
}
}
}
/// Една стая в подземията. Дефинира се само с име, макар че в по-интересна имплементация може да
/// държи item-и, противници...
///
#[derive(Debug)]
pub struct Room {
pub name: String,
- pub neighbours: [String;4],
+ pub neighbours: [String; 4],
}
impl Room {
- pub fn new(name:String) -> Self {
- Room{name,neighbours: [
- String::from(""),
- String::from(""),
- String::from(""),
- String::from(""),
- ]}
- }
+ pub fn new(name: String) -> Self {
+ Room {
+ name,
+ neighbours: [
+ String::from(""),
+ String::from(""),
+ String::from(""),
+ String::from(""),
+ ],
+ }
+ }
}
/// Контейнер за стаите и не само. Ще работим предимно със тази структура.
///
pub struct Dungeon {
- rooms:HashMap<String,Room>,
+ rooms: HashMap<String, Room>,
}
impl Dungeon {
/// Конструиране на празен Dungeon, в който няма никакви стаи.
///
pub fn new() -> Self {
- Dungeon{rooms: HashMap::new()}
+ Dungeon {
+ rooms: HashMap::new(),
+ }
}
-
/// Добавяне на стая към Dungeon с име `name`. Връща `Ok(())` при успех. Ако вече има стая с
/// такова име, очакваме да върнете `Errors::DuplicateRoom` с името.
///
pub fn add_room(&mut self, name: &str) -> Result<(), Errors> {
- match self.rooms.contains_key(name){
- true => Err(Errors::DuplicateRoom(String::from("Room already in dungeon!"))),
+ match self.rooms.contains_key(name) {
+ true => Err(Errors::DuplicateRoom(String::from(
+ "Room already in dungeon!",
+ ))),
false => {
- self.rooms.insert(name.to_string(), Room::new(name.to_string()));
- Ok(())
+ self.rooms
+ .insert(name.to_string(), Room::new(name.to_string()));
+ Ok(())
}
}
}
/// Прочитане на дадена стая -- когато извикаме `get_room`, очакваме reference към `Room`
/// структурата с това име.
///
/// Ако няма такава стая, очакваме `Errors::UnknownRoom` с подаденото име.
///
pub fn get_room(&self, room_name: &str) -> Result<&Room, Errors> {
- match self.rooms.contains_key(room_name){
- true => Ok(self.rooms.get(room_name).unwrap()),
- false => Err(Errors::UnknownRoom(String::from("Unknown room!")))
+ match self.rooms.contains_key(room_name) {
+ true => Ok(self.rooms.get(room_name).unwrap()),
+ false => Err(Errors::UnknownRoom(String::from("Unknown 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> {
- if !(self.rooms.contains_key(room_name) && self.rooms.contains_key(other_room_name)){
+ if !(self.rooms.contains_key(room_name) && self.rooms.contains_key(other_room_name)) {
Err(Errors::UnknownRoom(String::from(room_name)))
} else {
match direction {
- Direction::East => {self.rooms.get_mut(room_name).unwrap().neighbours[0] = String::from(other_room_name); self.rooms.get_mut(other_room_name).unwrap().neighbours[1] = String::from(room_name); Ok(()) }
- Direction::West => {self.rooms.get_mut(room_name).unwrap().neighbours[1] = String::from(other_room_name);self.rooms.get_mut(room_name).unwrap().neighbours[0] = String::from(room_name); Ok(()) }
- Direction::North => {self.rooms.get_mut(room_name).unwrap().neighbours[2] = String::from(other_room_name); self.rooms.get_mut(room_name).unwrap().neighbours[3] = String::from(room_name); Ok(()) }
- Direction::South => {self.rooms.get_mut(room_name).unwrap().neighbours[3] = String::from(other_room_name); self.rooms.get_mut(room_name).unwrap().neighbours[2] = String::from(room_name); Ok(()) }
+ Direction::East => {
+ self.rooms.get_mut(room_name).unwrap().neighbours[0] =
+ String::from(other_room_name);
+ self.rooms.get_mut(other_room_name).unwrap().neighbours[1] =
+ String::from(room_name);
+ Ok(())
+ }
+ Direction::West => {
+ self.rooms.get_mut(room_name).unwrap().neighbours[1] =
+ String::from(other_room_name);
+ self.rooms.get_mut(room_name).unwrap().neighbours[0] = String::from(room_name);
+ Ok(())
+ }
+ Direction::North => {
+ self.rooms.get_mut(room_name).unwrap().neighbours[2] =
+ String::from(other_room_name);
+ self.rooms.get_mut(room_name).unwrap().neighbours[3] = String::from(room_name);
+ Ok(())
+ }
+ Direction::South => {
+ self.rooms.get_mut(room_name).unwrap().neighbours[3] =
+ String::from(other_room_name);
+ self.rooms.get_mut(room_name).unwrap().neighbours[2] = String::from(room_name);
+ 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.rooms.contains_key(room_name){
+ pub fn get_next_room(
+ &self,
+ room_name: &str,
+ direction: Direction,
+ ) -> Result<Option<&Room>, Errors> {
+ if !self.rooms.contains_key(room_name) {
Err(Errors::UnknownRoom(String::from(room_name)))
} else {
match direction {
- Direction::East => Ok(Some(self.rooms.get(&self.rooms.get(room_name).unwrap().neighbours[0]).unwrap())),
- Direction::West => Ok(Some(self.rooms.get(&self.rooms.get(room_name).unwrap().neighbours[1]).unwrap())),
- Direction::North => Ok(Some(self.rooms.get(&self.rooms.get(room_name).unwrap().neighbours[2]).unwrap())),
- Direction::South => Ok(Some(self.rooms.get(&self.rooms.get(room_name).unwrap().neighbours[3]).unwrap())),
+ Direction::East => Ok(Some(
+ self.rooms
+ .get(&self.rooms.get(room_name).unwrap().neighbours[0])
+ .unwrap(),
+ )),
+ Direction::West => Ok(Some(
+ self.rooms
+ .get(&self.rooms.get(room_name).unwrap().neighbours[1])
+ .unwrap(),
+ )),
+ Direction::North => Ok(Some(
+ self.rooms
+ .get(&self.rooms.get(room_name).unwrap().neighbours[2])
+ .unwrap(),
+ )),
+ Direction::South => Ok(Some(
+ self.rooms
+ .get(&self.rooms.get(room_name).unwrap().neighbours[3])
+ .unwrap(),
+ )),
}
}
}
pub fn from_reader<B: BufRead>(reader: B) -> Result<Self, Errors> {
let mut dungeon: Dungeon = Dungeon::new();
- let ref mut line:String=String::from("");
+ let ref mut line: String = String::from("");
let mut mut_reader = reader;
match mut_reader.read_line(line) {
- Ok(_) => if line.to_string().trim().ne("## Rooms"){
- return Err(Errors::LineParseError{line_number:1});
- } ,
- Err(error) => return Err(Errors::IoError(error))
+ Ok(_) => {
+ if line.to_string().trim().ne("## Rooms") {
+ return Err(Errors::LineParseError { line_number: 1 });
+ }
+ }
+ Err(error) => return Err(Errors::IoError(error)),
}
-
let mut counter = 1;
let result_rooms = loop {
- counter+=1;
+ counter += 1;
*line = String::from("");
match mut_reader.read_line(line) {
Ok(_) => {
- if line.to_string() == "\n"{
- break Ok(())
+ if line.to_string() == "\n" {
+ break Ok(());
} else {
- match Dungeon::match_prefix("-", line.trim()){
- Some(room_name) => dungeon.add_room(&room_name.trim().to_string()).unwrap(),
- None => return Err(Errors::LineParseError{line_number:counter}),
+ match Dungeon::match_prefix("-", line.trim()) {
+ Some(room_name) => {
+ dungeon.add_room(&room_name.trim().to_string()).unwrap()
+ }
+ None => {
+ return Err(Errors::LineParseError {
+ line_number: counter,
+ })
+ }
}
}
- },
- Err(error) => return Err(Errors::IoError(error))
+ }
+ Err(error) => return Err(Errors::IoError(error)),
}
};
-
*line = String::from("");
match mut_reader.read_line(line) {
Ok(_) => match line.to_string().trim() {
- "## Links" => counter+=1,
- _ => return Err(Errors::LineParseError{line_number:counter})
+ "## Links" => counter += 1,
+ _ => {
+ return Err(Errors::LineParseError {
+ line_number: counter,
+ })
+ }
},
- Err(error) => return Err(Errors::IoError(error))
+ Err(error) => return Err(Errors::IoError(error)),
}
let result_links = loop {
- counter+=1;
+ counter += 1;
*line = String::from("");
let bytes = mut_reader.read_line(line);
- if bytes.unwrap() == 0{
- break Ok(())
- } else {
- match Dungeon::match_prefix("-", line.trim()){
- Some(links) => dungeon.set_link(links.split("->").collect::<Vec<&str>>()[0].trim(), Direction::from(links.split("->").collect::<Vec<&str>>()[1].trim()).unwrap(), links.split("->").collect::<Vec<&str>>()[2].trim()).unwrap(),
- None => return Err(Errors::LineParseError{line_number:counter}),
+ if bytes.unwrap() == 0 {
+ break Ok(());
+ } else {
+ match Dungeon::match_prefix("-", line.trim()) {
+ Some(links) => dungeon
+ .set_link(
+ links.split("->").collect::<Vec<&str>>()[0].trim(),
+ Direction::from(links.split("->").collect::<Vec<&str>>()[1].trim())
+ .unwrap(),
+ links.split("->").collect::<Vec<&str>>()[2].trim(),
+ )
+ .unwrap(),
+ None => {
+ return Err(Errors::LineParseError {
+ line_number: counter,
+ })
+ }
}
}
};
- match (result_rooms,result_links){
+ match (result_rooms, result_links) {
(Ok(_), Ok(_)) => Ok(dungeon),
(Err(error), _) => Err(error),
(Ok(_), Err(error)) => Err(error),
}
-
}
- pub fn find_path(){
+ pub fn find_path() {}
- }
-
pub fn match_prefix<'a, 'b>(prefix: &'a str, input: &'b str) -> Option<String> {
- if prefix.eq(input.split(" ").collect::<Vec<&str>>()[0]){
- Some(input.replacen("-", "", 1).to_string())
- } else {
- None
- }
-
+ if prefix.eq(input.split(" ").collect::<Vec<&str>>()[0]) {
+ Some(input.replacen("-", "", 1).to_string())
+ } else {
+ None
+ }
}
-}
+}
+
+fn main() {
+ const TEST_INPUT_1: &str = "
+## Rooms
+- Entrance
+- Hallway
+
+## Links
+- Entrance -> East -> Hallway
+";
+
+ // .trim() за да премахнем първия и последния ред:
+ let dungeon = Dungeon::from_reader(TEST_INPUT_1.trim().as_bytes()).unwrap();
+
+ assert_eq!(dungeon.get_room("Entrance").unwrap().name, "Entrance");
+ assert_eq!(dungeon.get_room("Hallway").unwrap().name, "Hallway");
+
+ assert_eq!(
+ dungeon
+ .get_next_room("Entrance", Direction::East)
+ .unwrap()
+ .unwrap()
+ .name,
+ "Hallway"
+ );
+
+ // println!("{}",Dungeon::match_prefix("-", "- Entrance -> East -> Hallway").unwrap());
+}

Станислав качи решение на 09.01.2022 17:59 (преди почти 4 години)

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)]
pub enum Direction {
North,
South,
East,
West,
}
impl Direction {
pub fn from(direction: &str) -> Result<Self, Errors> {
match direction {
"East" => Ok(Direction::East),
"West" => Ok(Direction::West),
"North" => Ok(Direction::North),
"South" => Ok(Direction::South),
_ => Err(Errors::DirectionParseError(String::from(direction))),
}
}
}
/// Една стая в подземията. Дефинира се само с име, макар че в по-интересна имплементация може да
/// държи item-и, противници...
///
#[derive(Debug)]
pub struct Room {
pub name: String,
pub neighbours: [String; 4],
}
impl Room {
pub fn new(name: String) -> Self {
Room {
name,
neighbours: [
String::from(""),
String::from(""),
String::from(""),
String::from(""),
],
}
}
}
/// Контейнер за стаите и не само. Ще работим предимно със тази структура.
///
pub struct Dungeon {
rooms: HashMap<String, Room>,
}
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> {
match self.rooms.contains_key(name) {
true => Err(Errors::DuplicateRoom(String::from(
"Room already in dungeon!",
))),
false => {
self.rooms
.insert(name.to_string(), Room::new(name.to_string()));
Ok(())
}
}
}
/// Прочитане на дадена стая -- когато извикаме `get_room`, очакваме reference към `Room`
/// структурата с това име.
///
/// Ако няма такава стая, очакваме `Errors::UnknownRoom` с подаденото име.
///
pub fn get_room(&self, room_name: &str) -> Result<&Room, Errors> {
match self.rooms.contains_key(room_name) {
true => Ok(self.rooms.get(room_name).unwrap()),
false => Err(Errors::UnknownRoom(String::from("Unknown 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> {
if !(self.rooms.contains_key(room_name) && self.rooms.contains_key(other_room_name)) {
Err(Errors::UnknownRoom(String::from(room_name)))
} else {
match direction {
Direction::East => {
self.rooms.get_mut(room_name).unwrap().neighbours[0] =
String::from(other_room_name);
self.rooms.get_mut(other_room_name).unwrap().neighbours[1] =
String::from(room_name);
Ok(())
}
Direction::West => {
self.rooms.get_mut(room_name).unwrap().neighbours[1] =
String::from(other_room_name);
self.rooms.get_mut(room_name).unwrap().neighbours[0] = String::from(room_name);
Ok(())
}
Direction::North => {
self.rooms.get_mut(room_name).unwrap().neighbours[2] =
String::from(other_room_name);
self.rooms.get_mut(room_name).unwrap().neighbours[3] = String::from(room_name);
Ok(())
}
Direction::South => {
self.rooms.get_mut(room_name).unwrap().neighbours[3] =
String::from(other_room_name);
self.rooms.get_mut(room_name).unwrap().neighbours[2] = String::from(room_name);
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.rooms.contains_key(room_name) {
Err(Errors::UnknownRoom(String::from(room_name)))
} else {
match direction {
Direction::East => Ok(Some(
self.rooms
.get(&self.rooms.get(room_name).unwrap().neighbours[0])
.unwrap(),
)),
Direction::West => Ok(Some(
self.rooms
.get(&self.rooms.get(room_name).unwrap().neighbours[1])
.unwrap(),
)),
Direction::North => Ok(Some(
self.rooms
.get(&self.rooms.get(room_name).unwrap().neighbours[2])
.unwrap(),
)),
Direction::South => Ok(Some(
self.rooms
.get(&self.rooms.get(room_name).unwrap().neighbours[3])
.unwrap(),
)),
}
}
}
pub fn from_reader<B: BufRead>(reader: B) -> Result<Self, Errors> {
let mut dungeon: Dungeon = Dungeon::new();
let ref mut line: String = String::from("");
let mut mut_reader = reader;
match mut_reader.read_line(line) {
Ok(_) => {
if line.to_string().trim().ne("## Rooms") {
return Err(Errors::LineParseError { line_number: 1 });
}
}
Err(error) => return Err(Errors::IoError(error)),
}
let mut counter = 1;
let result_rooms = loop {
counter += 1;
*line = String::from("");
match mut_reader.read_line(line) {
Ok(_) => {
if line.to_string() == "\n" {
break Ok(());
} else {
match Dungeon::match_prefix("-", line.trim()) {
Some(room_name) => {
dungeon.add_room(&room_name.trim().to_string()).unwrap()
}
None => {
return Err(Errors::LineParseError {
line_number: counter,
})
}
}
}
}
Err(error) => return Err(Errors::IoError(error)),
}
};
*line = String::from("");
match mut_reader.read_line(line) {
Ok(_) => match line.to_string().trim() {
"## Links" => counter += 1,
_ => {
return Err(Errors::LineParseError {
line_number: counter,
})
}
},
Err(error) => return Err(Errors::IoError(error)),
}
let result_links = loop {
counter += 1;
*line = String::from("");
let bytes = mut_reader.read_line(line);
if bytes.unwrap() == 0 {
break Ok(());
} else {
match Dungeon::match_prefix("-", line.trim()) {
Some(links) => dungeon
.set_link(
links.split("->").collect::<Vec<&str>>()[0].trim(),
Direction::from(links.split("->").collect::<Vec<&str>>()[1].trim())
.unwrap(),
links.split("->").collect::<Vec<&str>>()[2].trim(),
)
.unwrap(),
None => {
return Err(Errors::LineParseError {
line_number: counter,
})
}
}
}
};
match (result_rooms, result_links) {
(Ok(_), Ok(_)) => Ok(dungeon),
(Err(error), _) => Err(error),
(Ok(_), Err(error)) => Err(error),
}
}
pub fn find_path() {}
pub fn match_prefix<'a, 'b>(prefix: &'a str, input: &'b str) -> Option<String> {
if prefix.eq(input.split(" ").collect::<Vec<&str>>()[0]) {
Some(input.replacen("-", "", 1).to_string())
} else {
None
}
}
-}
-
+}
-fn main() {
- const TEST_INPUT_1: &str = "
-## Rooms
-- Entrance
-- Hallway
-
-## Links
-- Entrance -> East -> Hallway
-";
-
- // .trim() за да премахнем първия и последния ред:
- let dungeon = Dungeon::from_reader(TEST_INPUT_1.trim().as_bytes()).unwrap();
-
- assert_eq!(dungeon.get_room("Entrance").unwrap().name, "Entrance");
- assert_eq!(dungeon.get_room("Hallway").unwrap().name, "Hallway");
-
- assert_eq!(
- dungeon
- .get_next_room("Entrance", Direction::East)
- .unwrap()
- .unwrap()
- .name,
- "Hallway"
- );
-
- // println!("{}",Dungeon::match_prefix("-", "- Entrance -> East -> Hallway").unwrap());
-}

Станислав качи решение на 09.01.2022 19:09 (преди почти 4 години)

use std::collections::HashMap;
+use std::collections::HashSet;
+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),
}
/// Четирите посоки, в които може една стая да има съседи. Може да добавите още trait
/// имплементации, за да си улесните живота.
///
#[derive(Clone, Copy)]
pub enum Direction {
North,
South,
East,
West,
}
impl Direction {
pub fn from(direction: &str) -> Result<Self, Errors> {
match direction {
"East" => Ok(Direction::East),
"West" => Ok(Direction::West),
"North" => Ok(Direction::North),
"South" => Ok(Direction::South),
_ => Err(Errors::DirectionParseError(String::from(direction))),
}
}
}
/// Една стая в подземията. Дефинира се само с име, макар че в по-интересна имплементация може да
/// държи item-и, противници...
///
#[derive(Debug)]
pub struct Room {
pub name: String,
pub neighbours: [String; 4],
}
impl Room {
pub fn new(name: String) -> Self {
Room {
name,
neighbours: [
String::from(""),
String::from(""),
String::from(""),
String::from(""),
],
}
}
}
/// Контейнер за стаите и не само. Ще работим предимно със тази структура.
///
pub struct Dungeon {
rooms: HashMap<String, Room>,
}
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> {
match self.rooms.contains_key(name) {
true => Err(Errors::DuplicateRoom(String::from(
"Room already in dungeon!",
))),
false => {
self.rooms
.insert(name.to_string(), Room::new(name.to_string()));
Ok(())
}
}
}
/// Прочитане на дадена стая -- когато извикаме `get_room`, очакваме reference към `Room`
/// структурата с това име.
///
/// Ако няма такава стая, очакваме `Errors::UnknownRoom` с подаденото име.
///
pub fn get_room(&self, room_name: &str) -> Result<&Room, Errors> {
match self.rooms.contains_key(room_name) {
true => Ok(self.rooms.get(room_name).unwrap()),
false => Err(Errors::UnknownRoom(String::from("Unknown 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> {
if !(self.rooms.contains_key(room_name) && self.rooms.contains_key(other_room_name)) {
Err(Errors::UnknownRoom(String::from(room_name)))
} else {
match direction {
Direction::East => {
self.rooms.get_mut(room_name).unwrap().neighbours[0] =
String::from(other_room_name);
self.rooms.get_mut(other_room_name).unwrap().neighbours[1] =
String::from(room_name);
Ok(())
}
Direction::West => {
self.rooms.get_mut(room_name).unwrap().neighbours[1] =
String::from(other_room_name);
self.rooms.get_mut(room_name).unwrap().neighbours[0] = String::from(room_name);
Ok(())
}
Direction::North => {
self.rooms.get_mut(room_name).unwrap().neighbours[2] =
String::from(other_room_name);
self.rooms.get_mut(room_name).unwrap().neighbours[3] = String::from(room_name);
Ok(())
}
Direction::South => {
self.rooms.get_mut(room_name).unwrap().neighbours[3] =
String::from(other_room_name);
self.rooms.get_mut(room_name).unwrap().neighbours[2] = String::from(room_name);
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.rooms.contains_key(room_name) {
Err(Errors::UnknownRoom(String::from(room_name)))
} else {
match direction {
Direction::East => Ok(Some(
self.rooms
.get(&self.rooms.get(room_name).unwrap().neighbours[0])
.unwrap(),
)),
Direction::West => Ok(Some(
self.rooms
.get(&self.rooms.get(room_name).unwrap().neighbours[1])
.unwrap(),
)),
Direction::North => Ok(Some(
self.rooms
.get(&self.rooms.get(room_name).unwrap().neighbours[2])
.unwrap(),
)),
Direction::South => Ok(Some(
self.rooms
.get(&self.rooms.get(room_name).unwrap().neighbours[3])
.unwrap(),
)),
}
}
}
pub fn from_reader<B: BufRead>(reader: B) -> Result<Self, Errors> {
let mut dungeon: Dungeon = Dungeon::new();
let ref mut line: String = String::from("");
let mut mut_reader = reader;
match mut_reader.read_line(line) {
Ok(_) => {
if line.to_string().trim().ne("## Rooms") {
return Err(Errors::LineParseError { line_number: 1 });
}
}
Err(error) => return Err(Errors::IoError(error)),
}
let mut counter = 1;
let result_rooms = loop {
counter += 1;
*line = String::from("");
match mut_reader.read_line(line) {
Ok(_) => {
if line.to_string() == "\n" {
break Ok(());
} else {
match Dungeon::match_prefix("-", line.trim()) {
Some(room_name) => {
dungeon.add_room(&room_name.trim().to_string()).unwrap()
}
None => {
return Err(Errors::LineParseError {
line_number: counter,
})
}
}
}
}
Err(error) => return Err(Errors::IoError(error)),
}
};
*line = String::from("");
match mut_reader.read_line(line) {
Ok(_) => match line.to_string().trim() {
"## Links" => counter += 1,
_ => {
return Err(Errors::LineParseError {
line_number: counter,
})
}
},
Err(error) => return Err(Errors::IoError(error)),
}
let result_links = loop {
counter += 1;
*line = String::from("");
let bytes = mut_reader.read_line(line);
if bytes.unwrap() == 0 {
break Ok(());
} else {
match Dungeon::match_prefix("-", line.trim()) {
Some(links) => dungeon
.set_link(
links.split("->").collect::<Vec<&str>>()[0].trim(),
Direction::from(links.split("->").collect::<Vec<&str>>()[1].trim())
.unwrap(),
links.split("->").collect::<Vec<&str>>()[2].trim(),
)
.unwrap(),
None => {
return Err(Errors::LineParseError {
line_number: counter,
})
}
}
}
};
match (result_rooms, result_links) {
(Ok(_), Ok(_)) => Ok(dungeon),
(Err(error), _) => Err(error),
(Ok(_), Err(error)) => Err(error),
}
}
- pub fn find_path() {}
+ 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: HashSet<String> = HashSet::new();
+ let mut parent: HashMap<String, String> = HashMap::new();
+ let mut exists: bool = false;
- pub fn match_prefix<'a, 'b>(prefix: &'a str, input: &'b str) -> Option<String> {
+ let start_room = &self.get_room(start_room_name).unwrap().name;
+ queue.push_back(start_room.to_string());
+ while !queue.is_empty() {
+ let current_room_name = queue.front_mut().unwrap().clone();
+ if !visited.contains(&current_room_name) {
+ println!("{}", current_room_name);
+ if current_room_name.eq(end_room_name) {
+ exists = true;
+ break;
+ }
+ visited.insert(current_room_name.to_string());
+ for neighbour in &self.get_room(&current_room_name).unwrap().neighbours {
+ if neighbour.ne("") {
+ parent.insert(neighbour.to_string(), current_room_name.to_string());
+ queue.push_back(neighbour.to_string());
+ }
+ }
+ }
+ queue.pop_front();
+ }
+
+ match exists {
+ true => self.get_path(start_room_name, end_room_name, parent),
+ false => Ok(None),
+ }
+ }
+
+ fn match_prefix<'a, 'b>(prefix: &'a str, input: &'b str) -> Option<String> {
if prefix.eq(input.split(" ").collect::<Vec<&str>>()[0]) {
Some(input.replacen("-", "", 1).to_string())
} else {
None
}
+ }
+
+ fn get_path(
+ &self,
+ start_room_name: &str,
+ end_room_name: &str,
+ parent: HashMap<String, String>,
+ ) -> Result<Option<Vec<&Room>>, Errors> {
+ let mut path: Vec<&Room> = Vec::new();
+ let mut current_room_name = end_room_name;
+ while current_room_name.ne(start_room_name) {
+ path.push(self.get_room(current_room_name).unwrap());
+ current_room_name = parent.get(current_room_name).unwrap();
+ }
+ path.push(self.get_room(current_room_name).unwrap());
+ Ok(Some(path))
}
}

Станислав качи решение на 09.01.2022 19:27 (преди почти 4 години)

use std::collections::HashMap;
use std::collections::HashSet;
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),
}
/// Четирите посоки, в които може една стая да има съседи. Може да добавите още trait
/// имплементации, за да си улесните живота.
///
#[derive(Clone, Copy)]
pub enum Direction {
North,
South,
East,
West,
}
impl Direction {
pub fn from(direction: &str) -> Result<Self, Errors> {
match direction {
"East" => Ok(Direction::East),
"West" => Ok(Direction::West),
"North" => Ok(Direction::North),
"South" => Ok(Direction::South),
_ => Err(Errors::DirectionParseError(String::from(direction))),
}
}
}
/// Една стая в подземията. Дефинира се само с име, макар че в по-интересна имплементация може да
/// държи item-и, противници...
///
#[derive(Debug)]
pub struct Room {
pub name: String,
pub neighbours: [String; 4],
}
impl Room {
pub fn new(name: String) -> Self {
Room {
name,
neighbours: [
String::from(""),
String::from(""),
String::from(""),
String::from(""),
],
}
}
}
/// Контейнер за стаите и не само. Ще работим предимно със тази структура.
///
pub struct Dungeon {
rooms: HashMap<String, Room>,
}
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> {
match self.rooms.contains_key(name) {
true => Err(Errors::DuplicateRoom(String::from(
"Room already in dungeon!",
))),
false => {
self.rooms
.insert(name.to_string(), Room::new(name.to_string()));
Ok(())
}
}
}
/// Прочитане на дадена стая -- когато извикаме `get_room`, очакваме reference към `Room`
/// структурата с това име.
///
/// Ако няма такава стая, очакваме `Errors::UnknownRoom` с подаденото име.
///
pub fn get_room(&self, room_name: &str) -> Result<&Room, Errors> {
match self.rooms.contains_key(room_name) {
true => Ok(self.rooms.get(room_name).unwrap()),
false => Err(Errors::UnknownRoom(String::from("Unknown 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> {
if !(self.rooms.contains_key(room_name) && self.rooms.contains_key(other_room_name)) {
Err(Errors::UnknownRoom(String::from(room_name)))
} else {
match direction {
Direction::East => {
self.rooms.get_mut(room_name).unwrap().neighbours[0] =
String::from(other_room_name);
self.rooms.get_mut(other_room_name).unwrap().neighbours[1] =
String::from(room_name);
Ok(())
}
Direction::West => {
self.rooms.get_mut(room_name).unwrap().neighbours[1] =
String::from(other_room_name);
- self.rooms.get_mut(room_name).unwrap().neighbours[0] = String::from(room_name);
+ self.rooms.get_mut(other_room_name).unwrap().neighbours[0] = String::from(room_name);
Ok(())
}
Direction::North => {
self.rooms.get_mut(room_name).unwrap().neighbours[2] =
String::from(other_room_name);
- self.rooms.get_mut(room_name).unwrap().neighbours[3] = String::from(room_name);
+ self.rooms.get_mut(other_room_name).unwrap().neighbours[3] = String::from(room_name);
Ok(())
}
Direction::South => {
self.rooms.get_mut(room_name).unwrap().neighbours[3] =
String::from(other_room_name);
- self.rooms.get_mut(room_name).unwrap().neighbours[2] = String::from(room_name);
+ self.rooms.get_mut(other_room_name).unwrap().neighbours[2] = String::from(room_name);
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.rooms.contains_key(room_name) {
Err(Errors::UnknownRoom(String::from(room_name)))
} else {
match direction {
Direction::East => Ok(Some(
self.rooms
.get(&self.rooms.get(room_name).unwrap().neighbours[0])
.unwrap(),
)),
Direction::West => Ok(Some(
self.rooms
.get(&self.rooms.get(room_name).unwrap().neighbours[1])
.unwrap(),
)),
Direction::North => Ok(Some(
self.rooms
.get(&self.rooms.get(room_name).unwrap().neighbours[2])
.unwrap(),
)),
Direction::South => Ok(Some(
self.rooms
.get(&self.rooms.get(room_name).unwrap().neighbours[3])
.unwrap(),
)),
}
}
}
pub fn from_reader<B: BufRead>(reader: B) -> Result<Self, Errors> {
let mut dungeon: Dungeon = Dungeon::new();
let ref mut line: String = String::from("");
let mut mut_reader = reader;
match mut_reader.read_line(line) {
Ok(_) => {
if line.to_string().trim().ne("## Rooms") {
return Err(Errors::LineParseError { line_number: 1 });
}
}
Err(error) => return Err(Errors::IoError(error)),
}
let mut counter = 1;
let result_rooms = loop {
counter += 1;
*line = String::from("");
match mut_reader.read_line(line) {
Ok(_) => {
if line.to_string() == "\n" {
break Ok(());
} else {
match Dungeon::match_prefix("-", line.trim()) {
Some(room_name) => {
dungeon.add_room(&room_name.trim().to_string()).unwrap()
}
None => {
return Err(Errors::LineParseError {
line_number: counter,
})
}
}
}
}
Err(error) => return Err(Errors::IoError(error)),
}
};
*line = String::from("");
match mut_reader.read_line(line) {
Ok(_) => match line.to_string().trim() {
"## Links" => counter += 1,
_ => {
return Err(Errors::LineParseError {
line_number: counter,
})
}
},
Err(error) => return Err(Errors::IoError(error)),
}
let result_links = loop {
counter += 1;
*line = String::from("");
let bytes = mut_reader.read_line(line);
if bytes.unwrap() == 0 {
break Ok(());
} else {
match Dungeon::match_prefix("-", line.trim()) {
Some(links) => dungeon
.set_link(
links.split("->").collect::<Vec<&str>>()[0].trim(),
Direction::from(links.split("->").collect::<Vec<&str>>()[1].trim())
.unwrap(),
links.split("->").collect::<Vec<&str>>()[2].trim(),
)
.unwrap(),
None => {
return Err(Errors::LineParseError {
line_number: counter,
})
}
}
}
};
match (result_rooms, result_links) {
(Ok(_), Ok(_)) => Ok(dungeon),
(Err(error), _) => Err(error),
(Ok(_), Err(error)) => Err(error),
}
}
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: HashSet<String> = HashSet::new();
let mut parent: HashMap<String, String> = HashMap::new();
let mut exists: bool = false;
let start_room = &self.get_room(start_room_name).unwrap().name;
queue.push_back(start_room.to_string());
while !queue.is_empty() {
let current_room_name = queue.front_mut().unwrap().clone();
if !visited.contains(&current_room_name) {
- println!("{}", current_room_name);
if current_room_name.eq(end_room_name) {
exists = true;
break;
}
visited.insert(current_room_name.to_string());
for neighbour in &self.get_room(&current_room_name).unwrap().neighbours {
if neighbour.ne("") {
parent.insert(neighbour.to_string(), current_room_name.to_string());
queue.push_back(neighbour.to_string());
}
}
}
queue.pop_front();
}
match exists {
true => self.get_path(start_room_name, end_room_name, parent),
false => Ok(None),
}
}
fn match_prefix<'a, 'b>(prefix: &'a str, input: &'b str) -> Option<String> {
if prefix.eq(input.split(" ").collect::<Vec<&str>>()[0]) {
Some(input.replacen("-", "", 1).to_string())
} else {
None
}
}
fn get_path(
&self,
start_room_name: &str,
end_room_name: &str,
parent: HashMap<String, String>,
) -> Result<Option<Vec<&Room>>, Errors> {
let mut path: Vec<&Room> = Vec::new();
let mut current_room_name = end_room_name;
while current_room_name.ne(start_room_name) {
path.push(self.get_room(current_room_name).unwrap());
current_room_name = parent.get(current_room_name).unwrap();
}
path.push(self.get_room(current_room_name).unwrap());
+ path.reverse();
Ok(Some(path))
}
}