Решение на Dungeons and Compilers от Александър Бакалов

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

Към профила на Александър Бакалов

Резултати

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

Код

use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::io::{prelude::*, self};
use std::io::BufReader;
use std::fs::File;
use std::str::FromStr;
type AdjRooms = HashMap<Direction, String>;
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum Direction {
North,
South,
East,
West,
}
impl FromStr for Direction {
type Err = Errors;
fn from_str(input: &str) -> Result<Direction, Self::Err> {
match input {
"North" => Ok(Direction::North),
"South" => Ok(Direction::South),
"East" => Ok(Direction::East),
"West" => Ok(Direction::West),
input=> Err(Errors::DirectionParseError(String::from(input))),
}
}
}
#[derive(Debug)]
pub struct Room {
pub name: String,
pub adjacent_rooms : AdjRooms
}
impl Room {
fn new(room_name: String) -> Self {
Room {name: room_name, adjacent_rooms: HashMap::new()}
}
fn add_adjacent_room(&mut self, direction : Direction, room: String) {
self.adjacent_rooms.insert(direction, room);
}
fn get_adjacent_rooms(& self) -> Vec<&String> {
self.adjacent_rooms.values().collect::<Vec<&String>>()
}
// fn new(room_name: String, adj_rooms: HashMap<Direction, Room>) -> Self {
// Room {name: room_name, adjacent_rooms: adj_rooms}
// }
}
#[derive(Debug)]
pub enum Errors {
DuplicateRoom(String),
UnknownRoom(String),
IoError(std::io::Error),
LineParseError { line_number: usize },
DirectionParseError(String),
}
impl From<io::Error> for Errors {
fn from(e: io::Error) -> Self {
Errors::IoError(e)
}
}
#[derive(Debug)]
pub struct Dungeon {
// Каквито полета ви трябват
pub 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> {
let new_room = Room::new(String::from(name));
match self.rooms.entry(String::from(name)) {
Entry::Occupied(_o) => return Err(Errors::DuplicateRoom(String::from(name))),
Entry::Vacant(v) => v.insert(new_room)
};
Ok(())
}
/// Прочитане на дадена стая -- когато извикаме `get_room`, очакваме reference към `Room`
/// структурата с това име.
///
/// Ако няма такава стая, очакваме `Errors::UnknownRoom` с подаденото име.
///
pub fn get_room(&self, room_name: &str) -> Result<&Room, Errors> {
let room = self.rooms.get(room_name);
match room {
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> {
let room1_result = self.rooms.get_mut(room_name);
let room1 = match room1_result {
Some(x) => x,
None => return Err(Errors::UnknownRoom(String::from(room_name)))
};
room1.add_adjacent_room(direction, String::from(other_room_name));
let room2_result = self.rooms.get_mut(other_room_name);
let room2 = match room2_result {
Some(x) => x,
None => return Err(Errors::UnknownRoom(String::from(other_room_name)))
};
let other_direction = match direction {
Direction::East => Direction::West,
Direction::West => Direction::East,
Direction::North => Direction::South,
Direction::South => Direction::North
};
room2.add_adjacent_room(other_direction, 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> {
let source_room = self.get_room(room_name)?;
let next_room_option = source_room.adjacent_rooms.get(&direction);
let next_room = match next_room_option {
Some(room) => room,
None => return Ok(None)
};
let next_room_name = next_room;
Ok(Some(self.get_room(next_room_name)?))
}
/// Прочитаме структурата на dungeon от нещо, което имплементира `BufRead`. Това може да е
/// файл, или, ако тестваме, може да е просто колекция от байтове.
///
/// Успешен резултат връща новосъздадения dungeon, пакетиран в `Ok`.
///
/// Вижте по-долу за обяснение на грешките, които очакваме.
///
pub fn from_reader<B: BufRead>(reader: B) -> Result<Self, Errors> {
let mut dungeon = Dungeon::new();
let mut i: usize = 0;
let mut adding_rooms = false;
let mut adding_links = false;
let mut empty_row_found = false;
for line in reader.lines() {
let current_line = line?;
let current_line_trimmed = current_line.trim();
i += 1;
if current_line_trimmed.eq("## Rooms") && i == 1 {
adding_rooms = true;
} else if current_line_trimmed.eq("") {
adding_rooms = false;
empty_row_found = true;
} else if current_line_trimmed.eq("## Links") {
adding_links = true;
} else if i > 1 && adding_rooms && !adding_links && !empty_row_found && current_line_trimmed.starts_with("- ") {
dungeon.add_room(&current_line_trimmed.get(1..).unwrap().trim())?;
} else if i > 1 && !adding_rooms && adding_links && empty_row_found && current_line_trimmed.starts_with("- ") {
let arguments = current_line_trimmed.get(1..).unwrap().split(" -> ").collect::<Vec<&str>>();
if arguments.len() != 3 {
return Err(Errors::LineParseError { line_number: i});
} else {
let _ = dungeon.set_link(arguments[0].trim(), Direction::from_str(arguments[1].trim())?, arguments[2].trim());
}
} else {
return Err(Errors::LineParseError { line_number: i})
}
};
Ok(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> {
let parents: &mut HashMap<&str, &Room> = &mut HashMap::new();
let mut visited = Vec::new();
let queue: &mut Vec<&str> = &mut Vec::new();
let start_room: &Room = self.get_room(start_room_name)?;
let _ = self.get_room(end_room_name)?;
visited.push(start_room_name);
add_rooms_to_queue(start_room, queue, &visited, parents);
let mut found_path = false;
while queue.len() > 0 {
let current_room_name = queue.pop().unwrap();
visited.push(current_room_name);
let current_room = self.get_room(current_room_name)?;
add_rooms_to_queue(current_room, queue, &visited, parents);
if current_room_name.eq(end_room_name) {
found_path = true;
break;
}
};
// println!("PARENTS ARE: {:?}", parents);
if found_path {
let mut result = Vec::new();
let last_room_name = visited.pop().unwrap();
let mut last_room = self.get_room(last_room_name)?;
result.push(last_room);
loop {
let parent = parents.get(&last_room.name[..]);
result.push(parent.unwrap());
if parent.unwrap().name == start_room_name {
break;
}
last_room = parent.unwrap();
}
result.reverse();
Ok(Some(result))
}else {
Ok(None)
}
}
}
fn add_rooms_to_queue<'a>(start_room: &'a Room, queue: &mut Vec<&'a str>, visited: &Vec<&str>, parents: &mut HashMap<&'a str, &'a Room>){
let adj_rooms = start_room.get_adjacent_rooms();
for room_name in adj_rooms {
let str = room_name.as_str();
if !visited.contains(&str) {
queue.push(str);
parents.insert(str, start_room);
};
};
}
#[test]
fn it_works() {
let f = File::open("/Users/alex/Documents/University/Fourth year/First semester/Rust/Homework/Zork/solution/src/dungeon.txt").map_err(|e| println!("THE ERROR IS: {:?}", e)).unwrap();
let reader = BufReader::new(f);
let dungeon = Dungeon::from_reader(reader);
println!("{:?}", dungeon);
// let test = "- Hallway -> West -> Magic Lab";
// println!("{:?}", test.get(1..).unwrap().split("->").collect::<Vec<&str>>());
// let mut line = String::new();
// let len = reader.lines().for_each(f);
// println!("First line is {} bytes long", len);
assert_eq!(2, 2);
}
#[test]
fn test_basic_1() {
let mut dungeon = Dungeon::new();
dungeon.add_room("Entrance").unwrap();
dungeon.add_room("Hallway").unwrap();
dungeon.set_link("Entrance", Direction::East, "Hallway").unwrap();
assert_eq!(dungeon.get_room("Entrance").unwrap().name, "Entrance");
assert_eq!(dungeon.get_next_room("Entrance", Direction::East).unwrap().unwrap().name, "Hallway");
}
const TEST_INPUT_1: &str = "
## Rooms
- Entrance
- Hallway
## Links
- Entrance -> East -> Hallway
";
#[test]
fn test_basic_2() {
// .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");
}
#[test]
fn test_basic_3() {
let mut dungeon = Dungeon::new();
dungeon.add_room("Entrance").unwrap();
dungeon.add_room("Treasure Room").unwrap();
dungeon.set_link("Entrance", Direction::West, "Treasure Room").unwrap();
let path = dungeon.find_path("Entrance", "Treasure Room").unwrap().unwrap();
assert!(path.len() > 0);
}
#[test]
fn test_4() {
let mut dungeon = Dungeon::new();
dungeon.add_room("1").unwrap();
dungeon.add_room("2").unwrap();
dungeon.add_room("3").unwrap();
dungeon.add_room("4").unwrap();
dungeon.add_room("5").unwrap();
dungeon.add_room("6").unwrap();
dungeon.add_room("7").unwrap();
dungeon.add_room("8").unwrap();
dungeon.set_link("1", Direction::East, "2").unwrap();
dungeon.set_link("2", Direction::East, "3").unwrap();
dungeon.set_link("3", Direction::North, "4").unwrap();
dungeon.set_link("3", Direction::East, "6").unwrap();
dungeon.set_link("3", Direction::South, "8").unwrap();
dungeon.set_link("6", Direction::South, "7").unwrap();
dungeon.set_link("8", Direction::East, "7").unwrap();
let path = dungeon.find_path("1", "7").unwrap().unwrap();
println!("{:?}", path);
assert!(path.len() == 5);
}

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

Compiling solution v0.1.0 (/tmp/d20220116-3533338-jpel23/solution)
warning: unused import: `std::io::BufReader`
 --> src/lib.rs:4:5
  |
4 | use std::io::BufReader;
  |     ^^^^^^^^^^^^^^^^^^
  |
  = note: `#[warn(unused_imports)]` on by default

warning: unused import: `std::fs::File`
 --> src/lib.rs:5:5
  |
5 | use std::fs::File;
  |     ^^^^^^^^^^^^^

warning: constant is never used: `TEST_INPUT_1`
   --> src/lib.rs:310:1
    |
310 | / const TEST_INPUT_1: &str = "
311 | | ## Rooms
312 | | - Entrance
313 | | - Hallway
...   |
316 | | - Entrance -> East -> Hallway
317 | | ";
    | |__^
    |
    = note: `#[warn(dead_code)]` on by default

warning: `solution` (lib) generated 3 warnings
    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 ... FAILED
test solution_test::test_finding_an_indirect_path ... ok
test solution_test::test_finding_no_path ... ok
test solution_test::test_invalid_parsing ... FAILED
test solution_test::test_io_error ... ok
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_reflexive_path stdout ----
thread '<unnamed>' panicked at 'called `Option::unwrap()` on a `None` value', tests/solution_test.rs:360:71
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', tests/solution_test.rs:354: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


failures:
    solution_test::test_finding_a_reflexive_path
    solution_test::test_invalid_parsing

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

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

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

Александър качи първо решение на 10.01.2022 18:43 (преди над 3 години)

Александър качи решение на 11.01.2022 13:23 (преди над 3 години)

+use std::collections::HashMap;
+use std::collections::hash_map::Entry;
+use std::io::{prelude::*, self};
+use std::io::BufReader;
+use std::fs::File;
+use std::str::FromStr;
-#[cfg(test)]
-mod tests {
- use std::{collections::HashMap, hash::Hash};
- use std::collections::hash_map::Entry;
- use std::io::{prelude::*, self};
- use std::io::BufReader;
- use std::fs::File;
- use std::str::FromStr;
+type AdjRooms = HashMap<Direction, String>;
- type AdjRooms = HashMap<Direction, String>;
+#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
+pub enum Direction {
+ North,
+ South,
+ East,
+ West,
+}
- #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
- pub enum Direction {
- North,
- South,
- East,
- West,
- }
+impl FromStr for Direction {
+ type Err = Errors;
- impl FromStr for Direction {
- type Err = crate::tests::Errors;
-
- fn from_str(input: &str) -> Result<Direction, Self::Err> {
- match input {
- "North" => Ok(Direction::North),
- "South" => Ok(Direction::South),
- "East" => Ok(Direction::East),
- "West" => Ok(Direction::West),
- input=> Err(Errors::DirectionParseError(String::from(input))),
- }
+ fn from_str(input: &str) -> Result<Direction, Self::Err> {
+ match input {
+ "North" => Ok(Direction::North),
+ "South" => Ok(Direction::South),
+ "East" => Ok(Direction::East),
+ "West" => Ok(Direction::West),
+ input=> Err(Errors::DirectionParseError(String::from(input))),
}
}
+}
- #[derive(Debug)]
- pub struct Room {
- pub name: String,
- pub adjacent_rooms : AdjRooms
+#[derive(Debug)]
+pub struct Room {
+ pub name: String,
+ pub adjacent_rooms : AdjRooms
+}
+
+impl Room {
+ fn new(room_name: String) -> Self {
+ Room {name: room_name, adjacent_rooms: HashMap::new()}
}
- impl Room {
- fn new(room_name: String) -> Self {
- Room {name: room_name, adjacent_rooms: HashMap::new()}
- }
+ fn add_adjacent_room(&mut self, direction : Direction, room: String) {
+ self.adjacent_rooms.insert(direction, room);
+ }
- fn add_adjacent_room(&mut self, direction : Direction, room: String) {
- self.adjacent_rooms.insert(direction, room);
- }
+ fn get_adjacent_rooms(& self) -> Vec<&String> {
+ self.adjacent_rooms.values().collect::<Vec<&String>>()
+ }
- fn get_adjacent_rooms(& self) -> Vec<&String> {
- self.adjacent_rooms.values().collect::<Vec<&String>>()
- }
+ // fn new(room_name: String, adj_rooms: HashMap<Direction, Room>) -> Self {
+ // Room {name: room_name, adjacent_rooms: adj_rooms}
+ // }
+}
- // fn new(room_name: String, adj_rooms: HashMap<Direction, Room>) -> Self {
- // Room {name: room_name, adjacent_rooms: adj_rooms}
- // }
+#[derive(Debug)]
+pub enum Errors {
+ DuplicateRoom(String),
+ UnknownRoom(String),
+ IoError(std::io::Error),
+ LineParseError { line_number: usize },
+ DirectionParseError(String),
+}
+
+impl From<io::Error> for Errors {
+ fn from(e: io::Error) -> Self {
+ Errors::IoError(e)
}
+}
+#[derive(Debug)]
+pub struct Dungeon {
+ // Каквито полета ви трябват
+ pub rooms: HashMap<String, Room>
+}
- #[derive(Debug)]
- pub enum Errors {
- DuplicateRoom(String),
- UnknownRoom(String),
- IoError(std::io::Error),
- LineParseError { line_number: usize },
- DirectionParseError(String),
+impl Dungeon {
+ /// Конструиране на празен Dungeon, в който няма никакви стаи.
+ ///
+ pub fn new() -> Self {
+ Dungeon {rooms: HashMap::new()}
}
- impl From<io::Error> for crate::tests::Errors {
- fn from(e: io::Error) -> Self {
- Errors::IoError(e)
- }
+ /// Добавяне на стая към Dungeon с име `name`. Връща `Ok(())` при успех. Ако вече има стая с
+ /// такова име, очакваме да върнете `Errors::DuplicateRoom` с името.
+ ///
+ pub fn add_room(&mut self, name: &str) -> Result<(), Errors> {
+ let new_room = Room::new(String::from(name));
+ match self.rooms.entry(String::from(name)) {
+ Entry::Occupied(_o) => return Err(Errors::DuplicateRoom(String::from(name))),
+ Entry::Vacant(v) => v.insert(new_room)
+ };
+
+ Ok(())
}
- #[derive(Debug)]
- pub struct Dungeon {
- // Каквито полета ви трябват
- pub rooms: HashMap<String, Room>
- }
- impl Dungeon {
- /// Конструиране на празен Dungeon, в който няма никакви стаи.
- ///
- pub fn new() -> Self {
- Dungeon {rooms: HashMap::new()}
+ /// Прочитане на дадена стая -- когато извикаме `get_room`, очакваме reference към `Room`
+ /// структурата с това име.
+ ///
+ /// Ако няма такава стая, очакваме `Errors::UnknownRoom` с подаденото име.
+ ///
+ pub fn get_room(&self, room_name: &str) -> Result<&Room, Errors> {
+ let room = self.rooms.get(room_name);
+ match room {
+ Some(x) => Ok(x),
+ None => Err(Errors::UnknownRoom(String::from(room_name)))
}
-
- /// Добавяне на стая към Dungeon с име `name`. Връща `Ok(())` при успех. Ако вече има стая с
- /// такова име, очакваме да върнете `Errors::DuplicateRoom` с името.
- ///
- pub fn add_room(&mut self, name: &str) -> Result<(), Errors> {
- let new_room = Room::new(String::from(name));
- match self.rooms.entry(String::from(name)) {
- Entry::Occupied(_o) => return Err(Errors::DuplicateRoom(String::from(name))),
- Entry::Vacant(v) => v.insert(new_room)
- };
-
- Ok(())
- }
-
- /// Прочитане на дадена стая -- когато извикаме `get_room`, очакваме reference към `Room`
- /// структурата с това име.
- ///
- /// Ако няма такава стая, очакваме `Errors::UnknownRoom` с подаденото име.
- ///
- pub fn get_room(&self, room_name: &str) -> Result<&Room, Errors> {
- let room = self.rooms.get(room_name);
- match room {
- 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> {
+ /// Добавяне на съсед на дадена стая. След извикването на функцията, очакваме стаята с име
+ /// `room_name` да има връзка в посока `direction` със стаята с име `other_room_name`.
+ ///
+ /// Също така очакваме `other_room_name` да има връзка с `room_name` в *обратната* посока.
+ ///
+ /// Успешен резултат е `Ok(())`. В случай, че която от двете стаи не същестува, очакваме грешка
+ /// `Errors::UnknownRoom` със съответното име на липсваща стая. Ако и двете липсват, спокойно
+ /// върнете тази, която проверявате първо.
- let room1_result = self.rooms.get_mut(room_name);
- let room1 = match room1_result {
- Some(x) => x,
- None => return Err(Errors::UnknownRoom(String::from(room_name)))
- };
+ pub fn set_link(
+ &mut self,
+ room_name: &str,
+ direction: Direction,
+ other_room_name: &str,
+ ) -> Result<(), Errors> {
- room1.add_adjacent_room(direction, String::from(other_room_name));
+ let room1_result = self.rooms.get_mut(room_name);
+ let room1 = match room1_result {
+ Some(x) => x,
+ None => return Err(Errors::UnknownRoom(String::from(room_name)))
+ };
- let room2_result = self.rooms.get_mut(other_room_name);
- let room2 = match room2_result {
- Some(x) => x,
- None => return Err(Errors::UnknownRoom(String::from(other_room_name)))
- };
+ room1.add_adjacent_room(direction, String::from(other_room_name));
- let other_direction = match direction {
- Direction::East => Direction::West,
- Direction::West => Direction::East,
- Direction::North => Direction::South,
- Direction::South => Direction::North
- };
-
- room2.add_adjacent_room(other_direction, 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> {
- let source_room = self.get_room(room_name)?;
- let next_room_option = source_room.adjacent_rooms.get(&direction);
- let next_room = match next_room_option {
- Some(room) => room,
- None => return Ok(None)
- };
- let next_room_name = next_room;
- Ok(Some(self.get_room(next_room_name)?))
- }
+ let room2_result = self.rooms.get_mut(other_room_name);
+ let room2 = match room2_result {
+ Some(x) => x,
+ None => return Err(Errors::UnknownRoom(String::from(other_room_name)))
+ };
- /// Прочитаме структурата на dungeon от нещо, което имплементира `BufRead`. Това може да е
- /// файл, или, ако тестваме, може да е просто колекция от байтове.
- ///
- /// Успешен резултат връща новосъздадения dungeon, пакетиран в `Ok`.
- ///
- /// Вижте по-долу за обяснение на грешките, които очакваме.
- ///
- pub fn from_reader<B: BufRead>(reader: B) -> Result<Self, Errors> {
- let mut dungeon = Dungeon::new();
- let mut i: usize = 0;
- let mut adding_rooms = false;
- let mut adding_links = false;
- let mut empty_row_found = false;
- for line in reader.lines() {
- let current_line = line?;
- let current_line_trimmed = current_line.trim();
- i += 1;
- if current_line_trimmed.eq("## Rooms") && i == 1 {
- adding_rooms = true;
- } else if current_line_trimmed.eq("") {
- adding_rooms = false;
- empty_row_found = true;
- } else if current_line_trimmed.eq("## Links") {
- adding_links = true;
- } else if i > 1 && adding_rooms && !adding_links && !empty_row_found && current_line_trimmed.starts_with("- ") {
- dungeon.add_room(&current_line_trimmed.get(1..).unwrap().trim())?;
- } else if i > 1 && !adding_rooms && adding_links && empty_row_found && current_line_trimmed.starts_with("- ") {
- let arguments = current_line_trimmed.get(1..).unwrap().split(" -> ").collect::<Vec<&str>>();
- if arguments.len() != 3 {
- return Err(Errors::LineParseError { line_number: i});
- } else {
- let _ = dungeon.set_link(arguments[0].trim(), Direction::from_str(arguments[1].trim())?, arguments[2].trim());
- }
+ let other_direction = match direction {
+ Direction::East => Direction::West,
+ Direction::West => Direction::East,
+ Direction::North => Direction::South,
+ Direction::South => Direction::North
+ };
+
+ room2.add_adjacent_room(other_direction, 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> {
+ let source_room = self.get_room(room_name)?;
+ let next_room_option = source_room.adjacent_rooms.get(&direction);
+ let next_room = match next_room_option {
+ Some(room) => room,
+ None => return Ok(None)
+ };
+ let next_room_name = next_room;
+ Ok(Some(self.get_room(next_room_name)?))
+ }
+
+ /// Прочитаме структурата на dungeon от нещо, което имплементира `BufRead`. Това може да е
+ /// файл, или, ако тестваме, може да е просто колекция от байтове.
+ ///
+ /// Успешен резултат връща новосъздадения dungeon, пакетиран в `Ok`.
+ ///
+ /// Вижте по-долу за обяснение на грешките, които очакваме.
+ ///
+ pub fn from_reader<B: BufRead>(reader: B) -> Result<Self, Errors> {
+ let mut dungeon = Dungeon::new();
+ let mut i: usize = 0;
+ let mut adding_rooms = false;
+ let mut adding_links = false;
+ let mut empty_row_found = false;
+ for line in reader.lines() {
+ let current_line = line?;
+ let current_line_trimmed = current_line.trim();
+ i += 1;
+ if current_line_trimmed.eq("## Rooms") && i == 1 {
+ adding_rooms = true;
+ } else if current_line_trimmed.eq("") {
+ adding_rooms = false;
+ empty_row_found = true;
+ } else if current_line_trimmed.eq("## Links") {
+ adding_links = true;
+ } else if i > 1 && adding_rooms && !adding_links && !empty_row_found && current_line_trimmed.starts_with("- ") {
+ dungeon.add_room(&current_line_trimmed.get(1..).unwrap().trim())?;
+ } else if i > 1 && !adding_rooms && adding_links && empty_row_found && current_line_trimmed.starts_with("- ") {
+ let arguments = current_line_trimmed.get(1..).unwrap().split(" -> ").collect::<Vec<&str>>();
+ if arguments.len() != 3 {
+ return Err(Errors::LineParseError { line_number: i});
} else {
- return Err(Errors::LineParseError { line_number: i})
+ let _ = dungeon.set_link(arguments[0].trim(), Direction::from_str(arguments[1].trim())?, arguments[2].trim());
}
- };
- Ok(dungeon)
- }
-
+ } else {
+ return Err(Errors::LineParseError { line_number: i})
+ }
+ };
+ Ok(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> {
- let parents: &mut HashMap<&str, &Room> = &mut HashMap::new();
- let mut visited = Vec::new();
- let queue: &mut Vec<&str> = &mut Vec::new();
- let start_room: &Room = self.get_room(start_room_name)?;
- let _ = self.get_room(end_room_name)?;
- visited.push(start_room_name);
- add_rooms_to_queue(start_room, queue, &visited, parents);
- let mut found_path = false;
- while queue.len() > 0 {
- let current_room_name = queue.pop().unwrap();
- visited.push(current_room_name);
- let current_room = self.get_room(current_room_name)?;
- add_rooms_to_queue(current_room, queue, &visited, parents);
- if current_room_name.eq(end_room_name) {
- found_path = true;
- break;
- }
-
- };
- // println!("PARENTS ARE: {:?}", parents);
+ /// Търси път от `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 parents: &mut HashMap<&str, &Room> = &mut HashMap::new();
+ let mut visited = Vec::new();
+ let queue: &mut Vec<&str> = &mut Vec::new();
+ let start_room: &Room = self.get_room(start_room_name)?;
+ let _ = self.get_room(end_room_name)?;
+ visited.push(start_room_name);
+ add_rooms_to_queue(start_room, queue, &visited, parents);
+ let mut found_path = false;
+ while queue.len() > 0 {
+ let current_room_name = queue.pop().unwrap();
+ visited.push(current_room_name);
+ let current_room = self.get_room(current_room_name)?;
+ add_rooms_to_queue(current_room, queue, &visited, parents);
+ if current_room_name.eq(end_room_name) {
+ found_path = true;
+ break;
+ }
+
+ };
- if found_path {
- let mut result = Vec::new();
- let last_room_name = visited.pop().unwrap();
- let mut last_room = self.get_room(last_room_name)?;
- result.push(last_room);
- loop {
- let parent = parents.get(&last_room.name[..]);
- result.push(parent.unwrap());
- if parent.unwrap().name == start_room_name {
- break;
- }
- last_room = parent.unwrap();
- }
- result.reverse();
- Ok(Some(result))
+ // println!("PARENTS ARE: {:?}", parents);
- }else {
- Ok(None)
- }
+ if found_path {
+ let mut result = Vec::new();
+ let last_room_name = visited.pop().unwrap();
+ let mut last_room = self.get_room(last_room_name)?;
+ result.push(last_room);
+ loop {
+ let parent = parents.get(&last_room.name[..]);
+ result.push(parent.unwrap());
+ if parent.unwrap().name == start_room_name {
+ break;
+ }
+ last_room = parent.unwrap();
+ }
+ result.reverse();
+ Ok(Some(result))
+
+ }else {
+ Ok(None)
}
}
+}
- fn add_rooms_to_queue<'a>(start_room: &'a Room, queue: &mut Vec<&'a str>, visited: &Vec<&str>, parents: &mut HashMap<&'a str, &'a Room>){
- let adj_rooms = start_room.get_adjacent_rooms();
- for room_name in adj_rooms {
- let str = room_name.as_str();
- if !visited.contains(&str) {
- queue.push(str);
- parents.insert(str, start_room);
- };
+fn add_rooms_to_queue<'a>(start_room: &'a Room, queue: &mut Vec<&'a str>, visited: &Vec<&str>, parents: &mut HashMap<&'a str, &'a Room>){
+ let adj_rooms = start_room.get_adjacent_rooms();
+ for room_name in adj_rooms {
+ let str = room_name.as_str();
+ if !visited.contains(&str) {
+ queue.push(str);
+ parents.insert(str, start_room);
};
- }
+ };
+}
- #[test]
- fn it_works() {
- let f = File::open("/Users/alex/Documents/University/Fourth year/First semester/Rust/Homework/Zork/solution/src/dungeon.txt").map_err(|e| println!("THE ERROR IS: {:?}", e)).unwrap();
- let reader = BufReader::new(f);
- let dungeon = Dungeon::from_reader(reader);
+#[test]
+fn it_works() {
+ let f = File::open("/Users/alex/Documents/University/Fourth year/First semester/Rust/Homework/Zork/solution/src/dungeon.txt").map_err(|e| println!("THE ERROR IS: {:?}", e)).unwrap();
+ let reader = BufReader::new(f);
+ let dungeon = Dungeon::from_reader(reader);
- println!("{:?}", dungeon);
+ println!("{:?}", dungeon);
- // let test = "- Hallway -> West -> Magic Lab";
- // println!("{:?}", test.get(1..).unwrap().split("->").collect::<Vec<&str>>());
+ // let test = "- Hallway -> West -> Magic Lab";
+ // println!("{:?}", test.get(1..).unwrap().split("->").collect::<Vec<&str>>());
-
- // let mut line = String::new();
- // let len = reader.lines().for_each(f);
- // println!("First line is {} bytes long", len);
- assert_eq!(2, 2);
- }
+ // let mut line = String::new();
+ // let len = reader.lines().for_each(f);
+ // println!("First line is {} bytes long", len);
- #[test]
- fn test_basic_1() {
- let mut dungeon = Dungeon::new();
+ assert_eq!(2, 2);
+}
- dungeon.add_room("Entrance").unwrap();
- dungeon.add_room("Hallway").unwrap();
- dungeon.set_link("Entrance", Direction::East, "Hallway").unwrap();
+#[test]
+fn test_basic_1() {
+ let mut dungeon = Dungeon::new();
- assert_eq!(dungeon.get_room("Entrance").unwrap().name, "Entrance");
- assert_eq!(dungeon.get_next_room("Entrance", Direction::East).unwrap().unwrap().name, "Hallway");
- }
+ dungeon.add_room("Entrance").unwrap();
+ dungeon.add_room("Hallway").unwrap();
+ dungeon.set_link("Entrance", Direction::East, "Hallway").unwrap();
- const TEST_INPUT_1: &str = "
- ## Rooms
- - Entrance
- - Hallway
+ assert_eq!(dungeon.get_room("Entrance").unwrap().name, "Entrance");
+ assert_eq!(dungeon.get_next_room("Entrance", Direction::East).unwrap().unwrap().name, "Hallway");
+}
- ## Links
- - Entrance -> East -> Hallway
+const TEST_INPUT_1: &str = "
+## Rooms
+- Entrance
+- Hallway
+
+## Links
+- Entrance -> East -> Hallway
";
- #[test]
- fn test_basic_2() {
- // .trim() за да премахнем първия и последния ред:
- let dungeon = Dungeon::from_reader(TEST_INPUT_1.trim().as_bytes()).unwrap();
+#[test]
+fn test_basic_2() {
+ // .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_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");
- }
+ assert_eq!(dungeon.get_next_room("Entrance", Direction::East).unwrap().unwrap().name, "Hallway");
+}
- #[test]
- fn test_basic_3() {
- let mut dungeon = Dungeon::new();
+#[test]
+fn test_basic_3() {
+ let mut dungeon = Dungeon::new();
- dungeon.add_room("Entrance").unwrap();
- dungeon.add_room("Treasure Room").unwrap();
- dungeon.set_link("Entrance", Direction::West, "Treasure Room").unwrap();
+ dungeon.add_room("Entrance").unwrap();
+ dungeon.add_room("Treasure Room").unwrap();
+ dungeon.set_link("Entrance", Direction::West, "Treasure Room").unwrap();
- let path = dungeon.find_path("Entrance", "Treasure Room").unwrap().unwrap();
- assert!(path.len() > 0);
- }
+ let path = dungeon.find_path("Entrance", "Treasure Room").unwrap().unwrap();
+ assert!(path.len() > 0);
+}
- #[test]
- fn test_4() {
- let mut dungeon = Dungeon::new();
+#[test]
+fn test_4() {
+ let mut dungeon = Dungeon::new();
- dungeon.add_room("1").unwrap();
- dungeon.add_room("2").unwrap();
- dungeon.add_room("3").unwrap();
- dungeon.add_room("4").unwrap();
- dungeon.add_room("5").unwrap();
- dungeon.add_room("6").unwrap();
- dungeon.add_room("7").unwrap();
- dungeon.add_room("8").unwrap();
- dungeon.set_link("1", Direction::East, "2").unwrap();
- dungeon.set_link("2", Direction::East, "3").unwrap();
- dungeon.set_link("3", Direction::North, "4").unwrap();
- dungeon.set_link("3", Direction::East, "6").unwrap();
- dungeon.set_link("3", Direction::South, "8").unwrap();
- dungeon.set_link("6", Direction::South, "7").unwrap();
- dungeon.set_link("8", Direction::East, "7").unwrap();
+ dungeon.add_room("1").unwrap();
+ dungeon.add_room("2").unwrap();
+ dungeon.add_room("3").unwrap();
+ dungeon.add_room("4").unwrap();
+ dungeon.add_room("5").unwrap();
+ dungeon.add_room("6").unwrap();
+ dungeon.add_room("7").unwrap();
+ dungeon.add_room("8").unwrap();
+ dungeon.set_link("1", Direction::East, "2").unwrap();
+ dungeon.set_link("2", Direction::East, "3").unwrap();
+ dungeon.set_link("3", Direction::North, "4").unwrap();
+ dungeon.set_link("3", Direction::East, "6").unwrap();
+ dungeon.set_link("3", Direction::South, "8").unwrap();
+ dungeon.set_link("6", Direction::South, "7").unwrap();
+ dungeon.set_link("8", Direction::East, "7").unwrap();
- let path = dungeon.find_path("1", "7").unwrap().unwrap();
- println!("{:?}", path);
- assert!(path.len() == 5);
- }
+ let path = dungeon.find_path("1", "7").unwrap().unwrap();
+ println!("{:?}", path);
+ assert!(path.len() == 5);
}
+