Решение на Dungeons and Compilers от Георги Чобанов
Резултати
- 19 точки от тестове
- 0 бонус точки
- 19 точки общо
- 14 успешни тест(а)
- 1 неуспешни тест(а)
Код
use std::collections::HashMap;
use std::collections::VecDeque;
use std::io::BufRead;
use std::io::Error;
/// Различните грешки, които ще очакваме да върнете като резултат от някои невалидни операции.
/// Повече детайли по-долу.
///
#[derive(Debug)]
pub enum Errors {
DuplicateRoom(String),
UnknownRoom(String),
IoError(std::io::Error),
LineParseError { line_number: usize },
DirectionParseError(String),
}
/// Четирите посоки, в които може една стая да има съседи. Може да добавите още trait
/// имплементации, за да си улесните живота.
///
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub enum Direction {
North,
South,
East,
West,
}
/// Една стая в подземията. Дефинира се само с име, макар че в по-интересна имплементация може да
/// държи item-и, противници...
///
#[derive(Clone)]
pub struct Room {
pub name: String,
pub neighbours: HashMap<Direction, Option<Box<Room>>>,
// Каквито други полета ви трябват
}
/// Контейнер за стаите и не само. Ще работим предимно със тази структура.
///
pub struct Dungeon {
// Каквито полета ви трябват
rooms: HashMap<String, Room>,
}
impl Room {
pub fn new(t_name: &str) -> Self {
let mut t_neighbours = HashMap::new();
t_neighbours.insert(Direction::North, None);
t_neighbours.insert(Direction::South, None);
t_neighbours.insert(Direction::East, None);
t_neighbours.insert(Direction::West, None);
Room {name: String::from(t_name), neighbours: t_neighbours }
}
}
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> {
if self.rooms.contains_key(name) == true {
Err(Errors::DuplicateRoom(String::from("I'm sorry, it seems like this room already exists. Find yourself another Dungeon.")))
} else {
let new_room = Room::new(name);
self.rooms.insert(String::from(name), new_room);
Ok(())
}
}
/// Прочитане на дадена стая -- когато извикаме `get_room`, очакваме reference към `Room`
/// структурата с това име.
///
/// Ако няма такава стая, очакваме `Errors::UnknownRoom` с подаденото име.
///
pub fn get_room(&self, room_name: &str) -> Result<&Room, Errors> {
match self.rooms.contains_key(&String::from(room_name)) {
true => Ok(&self.rooms[&String::from(room_name)]),
false => Err(Errors::UnknownRoom(String::from("This room's name is not on the list."))),
}
}
/// Добавяне на съсед на дадена стая. След извикването на функцията, очакваме стаята с име
/// `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(&String::from(room_name)) == false {
Err(Errors::UnknownRoom(String::from(format!("No room with name '{}'",room_name))))
} else {
if self.rooms.contains_key(&String::from(other_room_name)) == false {
Err(Errors::UnknownRoom(String::from(format!("No room with name '{}'",room_name))))
} else {
let room_to_put: Room = self.rooms[&String::from(other_room_name)].clone();
let other_room_to_put: Room = self.rooms[&String::from(room_name)].clone();
if let Some(mut_rooms_d) = self.rooms.get_mut(&String::from(room_name)) {
if let Some(mut_room) = mut_rooms_d.neighbours.get_mut(&direction) {
*mut_room = Some(Box::new(room_to_put));
}
}
if let Some(mut_rooms_d_other) = self.rooms.get_mut(&String::from(other_room_name)) {
let op_direction = match direction.clone(){
Direction::North => Direction::South,
Direction::South => Direction::North,
Direction::West => Direction::East,
Direction::East => Direction::West,
};
if let Some(mut_other_room) = mut_rooms_d_other.neighbours.get_mut(&op_direction) {
*mut_other_room = Some(Box::new(other_room_to_put));
}
}
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(&String::from(room_name)) == false {
Err(Errors::UnknownRoom(String::from(format!("No room with name '{}'",room_name))))
} else {
match self.rooms[&String::from(room_name)].neighbours[&direction].as_ref() {
None => Ok(None),
Some(boxed_room) => Ok(Some(boxed_room)),
}
}
}
}
impl Dungeon {
/// match_prefix("- ", "- Foo") //=> Some("Foo")
/// match_prefix("- ", "Bar") //=> None
///
fn match_prefix<'a, 'b>(prefix: &'a str, input: &'b str) -> Option<&'b str> {
match input.split_once(prefix) {
None => None,
Some((first, _second)) if first != "" => None,
Some((first, second)) if first == "" => Some(second),
_ => None
}
}
/// Прочитаме структурата на dungeon от нещо, което имплементира `BufRead`. Това може да е
/// файл, или, ако тестваме, може да е просто колекция от байтове.
///
/// Успешен резултат връща новосъздадения dungeon, пакетиран в `Ok`.
///
/// Вижте по-долу за обяснение на грешките, които очакваме.
///
pub fn from_reader<B: BufRead>(reader: B) -> Result<Self, Errors> {
let mut line_count = 0;
let mut new_dungeon: Dungeon = Dungeon::new();
let mut rooms_read: bool = false;
let mut rooms_checked: bool = false;
let mut links_checked: bool = false;
for line in reader.lines() {
line_count += 1;
if line.is_err() {
return Err(Errors::IoError(Error::last_os_error()));
} else if line_count == 1 {
if line.as_ref().unwrap() != "## Rooms" {
return Err(Errors::LineParseError{line_number: line_count});
} else {
rooms_checked = true;
}
} else if !rooms_checked {
return Err(Errors::LineParseError{line_number: line_count});
} else if !rooms_read {
if line.as_ref().unwrap().as_str() == "" {
rooms_read = true;
} else {
let new_room = Dungeon::match_prefix("- ",line.as_ref().unwrap().as_str());
if new_room.is_none() {
return Err(Errors::LineParseError{line_number: line_count});
} else {
new_dungeon.add_room(new_room.unwrap()).unwrap();
}
}
} else if !links_checked {
if line.as_ref().unwrap() != "## Links" {
return Err(Errors::LineParseError{line_number: line_count});
} else {
links_checked = true;
}
} else {
let splited: Vec<&str> = line.as_ref().unwrap().as_str().split(" -> ").collect();
if splited.len() != 3 {
return Err(Errors::LineParseError{line_number: line_count});
}
let first_room = Dungeon::match_prefix("- ",splited[0]);
if first_room.is_none() {
return Err(Errors::LineParseError{line_number: line_count});
}
let direction = match splited[1] {
"North" => Some(Direction::North),
"South" => Some(Direction::South),
"East" => Some(Direction::East),
"West" => Some(Direction::West),
_ => None,
};
if direction.is_none() {
return Err(Errors::DirectionParseError(format!("'{}' is not a valid direction", splited[1])));
}
new_dungeon.set_link(first_room.unwrap(),direction.unwrap(),splited[2])?;
}
}
if line_count == 0 {
Err(Errors::LineParseError{line_number: line_count})
} else {
Ok(new_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 Dungeon::get_room(&self, start_room_name).is_err() {
Err(Errors::UnknownRoom(String::from(format!("No room with name '{}'",start_room_name))))
} else if Dungeon::get_room(&self, end_room_name).is_err() {
Err(Errors::UnknownRoom(String::from(format!("No room with name '{}'",end_room_name))))
} else {
let first_room = &self.rooms[start_room_name];
let last_room = &self.rooms[end_room_name];
let mut room_queue: VecDeque<&Room> = VecDeque::new();
let mut room_visited: HashMap<&str, Option<&Room>> = HashMap::new();
let mut path: Vec<&Room> = Vec::new();
for room in self.rooms.keys() {
room_visited.insert(&room.as_str(), None);
}
room_queue.push_back(first_room);
if let Some(mut_start_room) = room_visited.get_mut(&start_room_name) {
*mut_start_room = Some(first_room);
}
while !room_queue.is_empty() {
let curr_room: &Room = &room_queue.pop_front().unwrap();
for direction in curr_room.neighbours.keys() {
let next_room = Dungeon::get_next_room(&self, &curr_room.name.as_str(),*direction).unwrap();
if !next_room.is_none() {
if room_visited[&next_room.unwrap().name.as_str()].is_none() {
if let Some(mut_room) = room_visited.get_mut(&next_room.unwrap().name.as_str()) {
*mut_room = Some(curr_room);
}
room_queue.push_back(&next_room.unwrap());
}
}
}
}
if room_visited[end_room_name].is_none() {
Ok(None)
} else {
path.push(last_room);
let mut curr_room: &Room = room_visited[end_room_name].unwrap();
path.push(curr_room);
while curr_room.name != start_room_name {
curr_room = room_visited[&curr_room.name.as_str()].unwrap();
path.push(curr_room);
}
path.reverse();
Ok(Some(path))
}
}
}
}
#[test]
fn test_room_create() {
let mut test_d = Dungeon::new();
test_d.add_room("Wowie").unwrap();
test_d.add_room("Wha").unwrap();
assert_eq!(test_d.get_room("Wowie").unwrap().name, "Wowie");
assert_eq!(test_d.get_room("Wha").unwrap().name, "Wha");
assert!(test_d.get_room("You fool, you absolute buffoon").is_err());
assert!(test_d.add_room("Wowie").is_err());
}
#[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");
}
#[test]
fn test_basic_1_magic() {
let mut dungeon = Dungeon::new();
dungeon.add_room("Entrance").unwrap();
dungeon.add_room("Hallway").unwrap();
dungeon.add_room("Magic Lab").unwrap();
dungeon.set_link("Entrance", Direction::East, "Hallway").unwrap();
dungeon.set_link("Hallway", Direction::West, "Magic Lab").unwrap();
assert_eq!(dungeon.get_next_room("Entrance", Direction::East).unwrap().unwrap().name, "Hallway");
assert_eq!(dungeon.get_next_room("Hallway", Direction::West).unwrap().unwrap().name, "Magic Lab");
}
#[test]
fn test_cyrilic_just_in_case_why_not() {
let mut dungeon = Dungeon::new();
dungeon.add_room("Вход").unwrap();
dungeon.add_room("Коридор").unwrap();
dungeon.set_link("Вход", Direction::East, "Коридор").unwrap();
assert_eq!(dungeon.get_room("Вход").unwrap().name, "Вход");
assert_eq!(dungeon.get_next_room("Вход", Direction::East).unwrap().unwrap().name, "Коридор");
}
#[test]
fn test_all_directions() {
let mut dungeon = Dungeon::new();
dungeon.add_room("Entrance").unwrap();
dungeon.add_room("Room of Wonders").unwrap();
dungeon.add_room("The Triforce").unwrap();
dungeon.add_room("Bathroom").unwrap();
dungeon.set_link("Entrance", Direction::West, "Room of Wonders").unwrap();
dungeon.set_link("Entrance", Direction::South, "Bathroom").unwrap();
dungeon.set_link("Entrance", Direction::North, "The Triforce").unwrap();
assert_eq!(dungeon.get_next_room("Entrance", Direction::West).unwrap().unwrap().name, "Room of Wonders");
assert_eq!(dungeon.get_next_room("Entrance", Direction::South).unwrap().unwrap().name, "Bathroom");
assert_eq!(dungeon.get_next_room("Entrance", Direction::North).unwrap().unwrap().name, "The Triforce");
assert!(dungeon.get_next_room("Entrance", Direction::East).unwrap().is_none());
assert_eq!(dungeon.get_next_room("Room of Wonders", Direction::East).unwrap().unwrap().name, "Entrance");
assert_eq!(dungeon.get_next_room("Bathroom", Direction::North).unwrap().unwrap().name, "Entrance");
assert_eq!(dungeon.get_next_room("The Triforce", Direction::South).unwrap().unwrap().name, "Entrance");
dungeon.add_room("Yes").unwrap();
dungeon.set_link("Entrance", Direction::West, "Yes").unwrap();
assert_eq!(dungeon.get_next_room("Entrance", Direction::West).unwrap().unwrap().name, "Yes");
assert_eq!(dungeon.get_next_room("Room of Wonders", Direction::East).unwrap().unwrap().name, "Entrance");
}
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_eq!(path.len(),2);
}
#[test]
fn longer_path() {
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.set_link("1", Direction::East, "2").unwrap();
dungeon.set_link("2", Direction::South, "3").unwrap();
dungeon.set_link("3", Direction::West, "4").unwrap();
dungeon.set_link("4", Direction::South, "5").unwrap();
assert_eq!(dungeon.get_next_room("1", Direction::East).unwrap().unwrap().name, "2");
assert_eq!(dungeon.get_next_room("2", Direction::South).unwrap().unwrap().name, "3");
assert_eq!(dungeon.get_next_room("3", Direction::West).unwrap().unwrap().name, "4");
assert_eq!(dungeon.get_next_room("4", Direction::South).unwrap().unwrap().name, "5");
let path = dungeon.find_path("1","5").unwrap().unwrap();
assert_eq!(path.len(),5);
let first_room = path[0];
let last_room = path[4];
assert_eq!(first_room.name,"1");
assert_eq!(last_room.name,"5");
}
#[test]
fn path_error() {
let mut dungeon = Dungeon::new();
dungeon.add_room("exists").unwrap();
assert!(dungeon.find_path("no","ma'am").is_err());
assert!(dungeon.find_path("exists","uuuh i think not").is_err());
}
Лог от изпълнението
Compiling solution v0.1.0 (/tmp/d20220116-3533338-110rqw9/solution) warning: constant is never used: `TEST_INPUT_1` --> src/lib.rs:386:1 | 386 | / const TEST_INPUT_1: &str = " 387 | | ## Rooms 388 | | - Entrance 389 | | - Hallway ... | 392 | | - Entrance -> East -> Hallway 393 | | "; | |__^ | = note: `#[warn(dead_code)]` on by default warning: `solution` (lib) generated 1 warning Finished test [unoptimized + debuginfo] target(s) in 3.87s 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 ... ok 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 'assertion failed: `(left == right)` left: `2`, right: `1`', tests/solution_test.rs:362:9 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace thread 'main' panicked at 'assertion failed: `(left == right)` left: `2`, right: `1`', tests/solution_test.rs:354:5 failures: solution_test::test_finding_a_reflexive_path 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'
История (3 версии и 0 коментара)
Георги качи решение на 10.01.2022 17:15 (преди над 3 години)
use std::collections::HashMap;
use std::collections::VecDeque;
use std::io::BufRead;
use std::io::Error;
/// Различните грешки, които ще очакваме да върнете като резултат от някои невалидни операции.
/// Повече детайли по-долу.
///
#[derive(Debug)]
pub enum Errors {
DuplicateRoom(String),
UnknownRoom(String),
IoError(std::io::Error),
LineParseError { line_number: usize },
DirectionParseError(String),
}
/// Четирите посоки, в които може една стая да има съседи. Може да добавите още trait
/// имплементации, за да си улесните живота.
///
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub enum Direction {
North,
South,
East,
West,
}
/// Една стая в подземията. Дефинира се само с име, макар че в по-интересна имплементация може да
/// държи item-и, противници...
///
#[derive(Clone)]
pub struct Room {
pub name: String,
pub neighbours: HashMap<Direction, Option<Box<Room>>>,
// Каквито други полета ви трябват
}
/// Контейнер за стаите и не само. Ще работим предимно със тази структура.
///
pub struct Dungeon {
// Каквито полета ви трябват
rooms: HashMap<String, Room>,
}
impl Room {
- pub fn new(t_name: String) -> Self {
+ pub fn new(t_name: &str) -> Self {
let mut t_neighbours = HashMap::new();
t_neighbours.insert(Direction::North, None);
t_neighbours.insert(Direction::South, None);
t_neighbours.insert(Direction::East, None);
t_neighbours.insert(Direction::West, None);
- Room {name: t_name.clone(), neighbours: t_neighbours }
+ Room {name: String::from(t_name), neighbours: t_neighbours }
}
}
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_name = String::from(name);
- if self.rooms.contains_key(&new_name) == true {
+ if self.rooms.contains_key(name) == true {
Err(Errors::DuplicateRoom(String::from("I'm sorry, it seems like this room already exists. Find yourself another Dungeon.")))
} else {
- let new_room = Room::new(new_name.clone());
- self.rooms.insert(new_name, new_room);
+ let new_room = Room::new(name);
+ self.rooms.insert(String::from(name), new_room);
Ok(())
}
}
/// Прочитане на дадена стая -- когато извикаме `get_room`, очакваме reference към `Room`
/// структурата с това име.
///
/// Ако няма такава стая, очакваме `Errors::UnknownRoom` с подаденото име.
///
pub fn get_room(&self, room_name: &str) -> Result<&Room, Errors> {
match self.rooms.contains_key(&String::from(room_name)) {
true => Ok(&self.rooms[&String::from(room_name)]),
false => Err(Errors::UnknownRoom(String::from("This room's name is not on the list."))),
}
}
/// Добавяне на съсед на дадена стая. След извикването на функцията, очакваме стаята с име
/// `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(&String::from(room_name)) == false {
- Err(Errors::UnknownRoom(String::from(format!("No room with name {}",room_name.clone()))))
+ Err(Errors::UnknownRoom(String::from(format!("No room with name '{}'",room_name))))
} else {
if self.rooms.contains_key(&String::from(other_room_name)) == false {
- Err(Errors::UnknownRoom(String::from(format!("No room with name {}",room_name.clone()))))
+ Err(Errors::UnknownRoom(String::from(format!("No room with name '{}'",room_name))))
} else {
let room_to_put: Room = self.rooms[&String::from(other_room_name)].clone();
let other_room_to_put: Room = self.rooms[&String::from(room_name)].clone();
- if let Some(mut_rooms_d) = self.rooms.get_mut(&String::from(room_name.clone())) {
+ if let Some(mut_rooms_d) = self.rooms.get_mut(&String::from(room_name)) {
if let Some(mut_room) = mut_rooms_d.neighbours.get_mut(&direction) {
*mut_room = Some(Box::new(room_to_put));
}
}
if let Some(mut_rooms_d_other) = self.rooms.get_mut(&String::from(other_room_name)) {
let op_direction = match direction.clone(){
Direction::North => Direction::South,
Direction::South => Direction::North,
Direction::West => Direction::East,
Direction::East => Direction::West,
};
if let Some(mut_other_room) = mut_rooms_d_other.neighbours.get_mut(&op_direction) {
*mut_other_room = Some(Box::new(other_room_to_put));
}
}
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(&String::from(room_name)) == false {
- Err(Errors::UnknownRoom(String::from(format!("No room with name {}",room_name))))
+ Err(Errors::UnknownRoom(String::from(format!("No room with name '{}'",room_name))))
} else {
match self.rooms[&String::from(room_name)].neighbours[&direction].as_ref() {
None => Ok(None),
Some(boxed_room) => Ok(Some(boxed_room)),
}
}
}
}
impl Dungeon {
/// match_prefix("- ", "- Foo") //=> Some("Foo")
/// match_prefix("- ", "Bar") //=> None
///
fn match_prefix<'a, 'b>(prefix: &'a str, input: &'b str) -> Option<&'b str> {
match input.split_once(prefix) {
None => None,
Some((first, _second)) if first != "" => None,
Some((first, second)) if first == "" => Some(second),
_ => None
}
}
/// Прочитаме структурата на dungeon от нещо, което имплементира `BufRead`. Това може да е
/// файл, или, ако тестваме, може да е просто колекция от байтове.
///
/// Успешен резултат връща новосъздадения dungeon, пакетиран в `Ok`.
///
/// Вижте по-долу за обяснение на грешките, които очакваме.
///
pub fn from_reader<B: BufRead>(reader: B) -> Result<Self, Errors> {
let mut line_count = 0;
let mut new_dungeon: Dungeon = Dungeon::new();
let mut rooms_read: bool = false;
let mut rooms_checked: bool = false;
let mut links_checked: bool = false;
for line in reader.lines() {
line_count += 1;
if line.is_err() {
return Err(Errors::IoError(Error::last_os_error()));
} else if line_count == 1 {
if line.as_ref().unwrap() != "## Rooms" {
return Err(Errors::LineParseError{line_number: line_count});
} else {
rooms_checked = true;
}
} else if !rooms_checked {
return Err(Errors::LineParseError{line_number: line_count});
} else if !rooms_read {
if line.as_ref().unwrap().as_str() == "" {
rooms_read = true;
} else {
let new_room = Dungeon::match_prefix("- ",line.as_ref().unwrap().as_str());
if new_room.is_none() {
return Err(Errors::LineParseError{line_number: line_count});
} else {
new_dungeon.add_room(new_room.unwrap()).unwrap();
}
}
} else if !links_checked {
if line.as_ref().unwrap() != "## Links" {
return Err(Errors::LineParseError{line_number: line_count});
} else {
links_checked = true;
}
} else {
let splited: Vec<&str> = line.as_ref().unwrap().as_str().split(" -> ").collect();
if splited.len() != 3 {
return Err(Errors::LineParseError{line_number: line_count});
}
let first_room = Dungeon::match_prefix("- ",splited[0]);
if first_room.is_none() {
return Err(Errors::LineParseError{line_number: line_count});
}
let direction = match splited[1] {
"North" => Some(Direction::North),
"South" => Some(Direction::South),
"East" => Some(Direction::East),
"West" => Some(Direction::West),
_ => None,
};
if direction.is_none() {
- return Err(Errors::DirectionParseError(format!("{} is not a valid direction", splited[1])));
+ return Err(Errors::DirectionParseError(format!("'{}' is not a valid direction", splited[1])));
}
new_dungeon.set_link(first_room.unwrap(),direction.unwrap(),splited[2])?;
}
}
if line_count == 0 {
Err(Errors::LineParseError{line_number: line_count})
} else {
Ok(new_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 Dungeon::get_room(&self, start_room_name).is_err() {
- Err(Errors::UnknownRoom(String::from(format!("No room with name {}",start_room_name))))
+ Err(Errors::UnknownRoom(String::from(format!("No room with name '{}'",start_room_name))))
} else if Dungeon::get_room(&self, end_room_name).is_err() {
- Err(Errors::UnknownRoom(String::from(format!("No room with name {}",end_room_name))))
+ Err(Errors::UnknownRoom(String::from(format!("No room with name '{}'",end_room_name))))
} else {
let first_room = &self.rooms[start_room_name];
let last_room = &self.rooms[end_room_name];
let mut room_queue: VecDeque<&Room> = VecDeque::new();
let mut room_visited: HashMap<&str, Option<&Room>> = HashMap::new();
let mut path: Vec<&Room> = Vec::new();
for room in self.rooms.keys() {
room_visited.insert(&room.as_str(), None);
}
room_queue.push_back(first_room);
if let Some(mut_start_room) = room_visited.get_mut(&start_room_name) {
*mut_start_room = Some(first_room);
}
while !room_queue.is_empty() {
let curr_room: &Room = &room_queue.pop_front().unwrap();
for direction in curr_room.neighbours.keys() {
let next_room = Dungeon::get_next_room(&self, &curr_room.name.as_str(),*direction).unwrap();
if !next_room.is_none() {
if room_visited[&next_room.unwrap().name.as_str()].is_none() {
if let Some(mut_room) = room_visited.get_mut(&next_room.unwrap().name.as_str()) {
*mut_room = Some(curr_room);
}
room_queue.push_back(&next_room.unwrap());
}
}
}
}
if room_visited[end_room_name].is_none() {
Ok(None)
} else {
path.push(last_room);
let mut curr_room: &Room = room_visited[end_room_name].unwrap();
path.push(curr_room);
while curr_room.name != start_room_name {
curr_room = room_visited[&curr_room.name.as_str()].unwrap();
path.push(curr_room);
}
path.reverse();
Ok(Some(path))
}
}
}
}
+//////////////////////// МАХНИ ТОВА ПОСЛЕЕЕЕЕЕЕЕЕ
+///////////////ВОООООООО
+////////////РРРРРРРРР
+//////////коккккккккккккккккккккккккккккккк
+const TEST_INPUT_2: &str = "
+## Rooms
+- Entrance
+- Hallway
+
+## Links
+- Entrance -> East -> Hallway
+";
+fn main() {
+ let dungeon = Dungeon::from_reader(TEST_INPUT_2.trim().as_bytes()).unwrap();
+}
+
#[test]
fn test_room_create() {
let mut test_d = Dungeon::new();
test_d.add_room("Wowie").unwrap();
test_d.add_room("Wha").unwrap();
assert_eq!(test_d.get_room("Wowie").unwrap().name, "Wowie");
assert_eq!(test_d.get_room("Wha").unwrap().name, "Wha");
assert!(test_d.get_room("You fool, you absolute buffoon").is_err());
assert!(test_d.add_room("Wowie").is_err());
}
#[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");
}
#[test]
fn test_basic_1_magic() {
let mut dungeon = Dungeon::new();
dungeon.add_room("Entrance").unwrap();
dungeon.add_room("Hallway").unwrap();
dungeon.add_room("Magic Lab").unwrap();
dungeon.set_link("Entrance", Direction::East, "Hallway").unwrap();
dungeon.set_link("Hallway", Direction::West, "Magic Lab").unwrap();
assert_eq!(dungeon.get_next_room("Entrance", Direction::East).unwrap().unwrap().name, "Hallway");
assert_eq!(dungeon.get_next_room("Hallway", Direction::West).unwrap().unwrap().name, "Magic Lab");
}
#[test]
fn test_cyrilic_just_in_case_why_not() {
let mut dungeon = Dungeon::new();
dungeon.add_room("Вход").unwrap();
dungeon.add_room("Коридор").unwrap();
dungeon.set_link("Вход", Direction::East, "Коридор").unwrap();
assert_eq!(dungeon.get_room("Вход").unwrap().name, "Вход");
assert_eq!(dungeon.get_next_room("Вход", Direction::East).unwrap().unwrap().name, "Коридор");
}
#[test]
fn test_all_directions() {
let mut dungeon = Dungeon::new();
dungeon.add_room("Entrance").unwrap();
dungeon.add_room("Room of Wonders").unwrap();
dungeon.add_room("The Triforce").unwrap();
dungeon.add_room("Bathroom").unwrap();
dungeon.set_link("Entrance", Direction::West, "Room of Wonders").unwrap();
dungeon.set_link("Entrance", Direction::South, "Bathroom").unwrap();
dungeon.set_link("Entrance", Direction::North, "The Triforce").unwrap();
assert_eq!(dungeon.get_next_room("Entrance", Direction::West).unwrap().unwrap().name, "Room of Wonders");
assert_eq!(dungeon.get_next_room("Entrance", Direction::South).unwrap().unwrap().name, "Bathroom");
assert_eq!(dungeon.get_next_room("Entrance", Direction::North).unwrap().unwrap().name, "The Triforce");
assert!(dungeon.get_next_room("Entrance", Direction::East).unwrap().is_none());
assert_eq!(dungeon.get_next_room("Room of Wonders", Direction::East).unwrap().unwrap().name, "Entrance");
assert_eq!(dungeon.get_next_room("Bathroom", Direction::North).unwrap().unwrap().name, "Entrance");
assert_eq!(dungeon.get_next_room("The Triforce", Direction::South).unwrap().unwrap().name, "Entrance");
dungeon.add_room("Yes").unwrap();
dungeon.set_link("Entrance", Direction::West, "Yes").unwrap();
assert_eq!(dungeon.get_next_room("Entrance", Direction::West).unwrap().unwrap().name, "Yes");
assert_eq!(dungeon.get_next_room("Room of Wonders", Direction::East).unwrap().unwrap().name, "Entrance");
}
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_eq!(path.len(),2);
}
#[test]
fn longer_path() {
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.set_link("1", Direction::East, "2").unwrap();
dungeon.set_link("2", Direction::South, "3").unwrap();
dungeon.set_link("3", Direction::West, "4").unwrap();
dungeon.set_link("4", Direction::South, "5").unwrap();
assert_eq!(dungeon.get_next_room("1", Direction::East).unwrap().unwrap().name, "2");
assert_eq!(dungeon.get_next_room("2", Direction::South).unwrap().unwrap().name, "3");
assert_eq!(dungeon.get_next_room("3", Direction::West).unwrap().unwrap().name, "4");
assert_eq!(dungeon.get_next_room("4", Direction::South).unwrap().unwrap().name, "5");
let path = dungeon.find_path("1","5").unwrap().unwrap();
assert_eq!(path.len(),5);
let first_room = path[0];
let last_room = path[4];
assert_eq!(first_room.name,"1");
assert_eq!(last_room.name,"5");
}
#[test]
fn path_error() {
let mut dungeon = Dungeon::new();
dungeon.add_room("exists").unwrap();
assert!(dungeon.find_path("no","ma'am").is_err());
assert!(dungeon.find_path("exists","uuuh i think not").is_err());
}
Георги качи решение на 10.01.2022 17:16 (преди над 3 години)
use std::collections::HashMap;
use std::collections::VecDeque;
use std::io::BufRead;
use std::io::Error;
/// Различните грешки, които ще очакваме да върнете като резултат от някои невалидни операции.
/// Повече детайли по-долу.
///
#[derive(Debug)]
pub enum Errors {
DuplicateRoom(String),
UnknownRoom(String),
IoError(std::io::Error),
LineParseError { line_number: usize },
DirectionParseError(String),
}
/// Четирите посоки, в които може една стая да има съседи. Може да добавите още trait
/// имплементации, за да си улесните живота.
///
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub enum Direction {
North,
South,
East,
West,
}
/// Една стая в подземията. Дефинира се само с име, макар че в по-интересна имплементация може да
/// държи item-и, противници...
///
#[derive(Clone)]
pub struct Room {
pub name: String,
pub neighbours: HashMap<Direction, Option<Box<Room>>>,
// Каквито други полета ви трябват
}
/// Контейнер за стаите и не само. Ще работим предимно със тази структура.
///
pub struct Dungeon {
// Каквито полета ви трябват
rooms: HashMap<String, Room>,
}
impl Room {
pub fn new(t_name: &str) -> Self {
let mut t_neighbours = HashMap::new();
t_neighbours.insert(Direction::North, None);
t_neighbours.insert(Direction::South, None);
t_neighbours.insert(Direction::East, None);
t_neighbours.insert(Direction::West, None);
Room {name: String::from(t_name), neighbours: t_neighbours }
}
}
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> {
if self.rooms.contains_key(name) == true {
Err(Errors::DuplicateRoom(String::from("I'm sorry, it seems like this room already exists. Find yourself another Dungeon.")))
} else {
let new_room = Room::new(name);
self.rooms.insert(String::from(name), new_room);
Ok(())
}
}
/// Прочитане на дадена стая -- когато извикаме `get_room`, очакваме reference към `Room`
/// структурата с това име.
///
/// Ако няма такава стая, очакваме `Errors::UnknownRoom` с подаденото име.
///
pub fn get_room(&self, room_name: &str) -> Result<&Room, Errors> {
match self.rooms.contains_key(&String::from(room_name)) {
true => Ok(&self.rooms[&String::from(room_name)]),
false => Err(Errors::UnknownRoom(String::from("This room's name is not on the list."))),
}
}
/// Добавяне на съсед на дадена стая. След извикването на функцията, очакваме стаята с име
/// `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(&String::from(room_name)) == false {
Err(Errors::UnknownRoom(String::from(format!("No room with name '{}'",room_name))))
} else {
if self.rooms.contains_key(&String::from(other_room_name)) == false {
Err(Errors::UnknownRoom(String::from(format!("No room with name '{}'",room_name))))
} else {
let room_to_put: Room = self.rooms[&String::from(other_room_name)].clone();
let other_room_to_put: Room = self.rooms[&String::from(room_name)].clone();
if let Some(mut_rooms_d) = self.rooms.get_mut(&String::from(room_name)) {
if let Some(mut_room) = mut_rooms_d.neighbours.get_mut(&direction) {
*mut_room = Some(Box::new(room_to_put));
}
}
if let Some(mut_rooms_d_other) = self.rooms.get_mut(&String::from(other_room_name)) {
let op_direction = match direction.clone(){
Direction::North => Direction::South,
Direction::South => Direction::North,
Direction::West => Direction::East,
Direction::East => Direction::West,
};
if let Some(mut_other_room) = mut_rooms_d_other.neighbours.get_mut(&op_direction) {
*mut_other_room = Some(Box::new(other_room_to_put));
}
}
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(&String::from(room_name)) == false {
Err(Errors::UnknownRoom(String::from(format!("No room with name '{}'",room_name))))
} else {
match self.rooms[&String::from(room_name)].neighbours[&direction].as_ref() {
None => Ok(None),
Some(boxed_room) => Ok(Some(boxed_room)),
}
}
}
}
impl Dungeon {
/// match_prefix("- ", "- Foo") //=> Some("Foo")
/// match_prefix("- ", "Bar") //=> None
///
fn match_prefix<'a, 'b>(prefix: &'a str, input: &'b str) -> Option<&'b str> {
match input.split_once(prefix) {
None => None,
Some((first, _second)) if first != "" => None,
Some((first, second)) if first == "" => Some(second),
_ => None
}
}
/// Прочитаме структурата на dungeon от нещо, което имплементира `BufRead`. Това може да е
/// файл, или, ако тестваме, може да е просто колекция от байтове.
///
/// Успешен резултат връща новосъздадения dungeon, пакетиран в `Ok`.
///
/// Вижте по-долу за обяснение на грешките, които очакваме.
///
pub fn from_reader<B: BufRead>(reader: B) -> Result<Self, Errors> {
let mut line_count = 0;
let mut new_dungeon: Dungeon = Dungeon::new();
let mut rooms_read: bool = false;
let mut rooms_checked: bool = false;
let mut links_checked: bool = false;
for line in reader.lines() {
line_count += 1;
if line.is_err() {
return Err(Errors::IoError(Error::last_os_error()));
} else if line_count == 1 {
if line.as_ref().unwrap() != "## Rooms" {
return Err(Errors::LineParseError{line_number: line_count});
} else {
rooms_checked = true;
}
} else if !rooms_checked {
return Err(Errors::LineParseError{line_number: line_count});
} else if !rooms_read {
if line.as_ref().unwrap().as_str() == "" {
rooms_read = true;
} else {
let new_room = Dungeon::match_prefix("- ",line.as_ref().unwrap().as_str());
if new_room.is_none() {
return Err(Errors::LineParseError{line_number: line_count});
} else {
new_dungeon.add_room(new_room.unwrap()).unwrap();
}
}
} else if !links_checked {
if line.as_ref().unwrap() != "## Links" {
return Err(Errors::LineParseError{line_number: line_count});
} else {
links_checked = true;
}
} else {
let splited: Vec<&str> = line.as_ref().unwrap().as_str().split(" -> ").collect();
if splited.len() != 3 {
return Err(Errors::LineParseError{line_number: line_count});
}
let first_room = Dungeon::match_prefix("- ",splited[0]);
if first_room.is_none() {
return Err(Errors::LineParseError{line_number: line_count});
}
let direction = match splited[1] {
"North" => Some(Direction::North),
"South" => Some(Direction::South),
"East" => Some(Direction::East),
"West" => Some(Direction::West),
_ => None,
};
if direction.is_none() {
return Err(Errors::DirectionParseError(format!("'{}' is not a valid direction", splited[1])));
}
new_dungeon.set_link(first_room.unwrap(),direction.unwrap(),splited[2])?;
}
}
if line_count == 0 {
Err(Errors::LineParseError{line_number: line_count})
} else {
Ok(new_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 Dungeon::get_room(&self, start_room_name).is_err() {
Err(Errors::UnknownRoom(String::from(format!("No room with name '{}'",start_room_name))))
} else if Dungeon::get_room(&self, end_room_name).is_err() {
Err(Errors::UnknownRoom(String::from(format!("No room with name '{}'",end_room_name))))
} else {
let first_room = &self.rooms[start_room_name];
let last_room = &self.rooms[end_room_name];
let mut room_queue: VecDeque<&Room> = VecDeque::new();
let mut room_visited: HashMap<&str, Option<&Room>> = HashMap::new();
let mut path: Vec<&Room> = Vec::new();
for room in self.rooms.keys() {
room_visited.insert(&room.as_str(), None);
}
room_queue.push_back(first_room);
if let Some(mut_start_room) = room_visited.get_mut(&start_room_name) {
*mut_start_room = Some(first_room);
}
while !room_queue.is_empty() {
let curr_room: &Room = &room_queue.pop_front().unwrap();
for direction in curr_room.neighbours.keys() {
let next_room = Dungeon::get_next_room(&self, &curr_room.name.as_str(),*direction).unwrap();
if !next_room.is_none() {
if room_visited[&next_room.unwrap().name.as_str()].is_none() {
if let Some(mut_room) = room_visited.get_mut(&next_room.unwrap().name.as_str()) {
*mut_room = Some(curr_room);
}
room_queue.push_back(&next_room.unwrap());
}
}
}
}
if room_visited[end_room_name].is_none() {
Ok(None)
} else {
path.push(last_room);
let mut curr_room: &Room = room_visited[end_room_name].unwrap();
path.push(curr_room);
while curr_room.name != start_room_name {
curr_room = room_visited[&curr_room.name.as_str()].unwrap();
path.push(curr_room);
}
path.reverse();
Ok(Some(path))
}
}
}
}
-//////////////////////// МАХНИ ТОВА ПОСЛЕЕЕЕЕЕЕЕЕ
-///////////////ВОООООООО
-////////////РРРРРРРРР
-//////////коккккккккккккккккккккккккккккккк
-const TEST_INPUT_2: &str = "
-## Rooms
-- Entrance
-- Hallway
-
-## Links
-- Entrance -> East -> Hallway
-";
-fn main() {
- let dungeon = Dungeon::from_reader(TEST_INPUT_2.trim().as_bytes()).unwrap();
-}
-
#[test]
fn test_room_create() {
let mut test_d = Dungeon::new();
test_d.add_room("Wowie").unwrap();
test_d.add_room("Wha").unwrap();
assert_eq!(test_d.get_room("Wowie").unwrap().name, "Wowie");
assert_eq!(test_d.get_room("Wha").unwrap().name, "Wha");
assert!(test_d.get_room("You fool, you absolute buffoon").is_err());
assert!(test_d.add_room("Wowie").is_err());
}
#[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");
}
#[test]
fn test_basic_1_magic() {
let mut dungeon = Dungeon::new();
dungeon.add_room("Entrance").unwrap();
dungeon.add_room("Hallway").unwrap();
dungeon.add_room("Magic Lab").unwrap();
dungeon.set_link("Entrance", Direction::East, "Hallway").unwrap();
dungeon.set_link("Hallway", Direction::West, "Magic Lab").unwrap();
assert_eq!(dungeon.get_next_room("Entrance", Direction::East).unwrap().unwrap().name, "Hallway");
assert_eq!(dungeon.get_next_room("Hallway", Direction::West).unwrap().unwrap().name, "Magic Lab");
}
#[test]
fn test_cyrilic_just_in_case_why_not() {
let mut dungeon = Dungeon::new();
dungeon.add_room("Вход").unwrap();
dungeon.add_room("Коридор").unwrap();
dungeon.set_link("Вход", Direction::East, "Коридор").unwrap();
assert_eq!(dungeon.get_room("Вход").unwrap().name, "Вход");
assert_eq!(dungeon.get_next_room("Вход", Direction::East).unwrap().unwrap().name, "Коридор");
}
#[test]
fn test_all_directions() {
let mut dungeon = Dungeon::new();
dungeon.add_room("Entrance").unwrap();
dungeon.add_room("Room of Wonders").unwrap();
dungeon.add_room("The Triforce").unwrap();
dungeon.add_room("Bathroom").unwrap();
dungeon.set_link("Entrance", Direction::West, "Room of Wonders").unwrap();
dungeon.set_link("Entrance", Direction::South, "Bathroom").unwrap();
dungeon.set_link("Entrance", Direction::North, "The Triforce").unwrap();
assert_eq!(dungeon.get_next_room("Entrance", Direction::West).unwrap().unwrap().name, "Room of Wonders");
assert_eq!(dungeon.get_next_room("Entrance", Direction::South).unwrap().unwrap().name, "Bathroom");
assert_eq!(dungeon.get_next_room("Entrance", Direction::North).unwrap().unwrap().name, "The Triforce");
assert!(dungeon.get_next_room("Entrance", Direction::East).unwrap().is_none());
assert_eq!(dungeon.get_next_room("Room of Wonders", Direction::East).unwrap().unwrap().name, "Entrance");
assert_eq!(dungeon.get_next_room("Bathroom", Direction::North).unwrap().unwrap().name, "Entrance");
assert_eq!(dungeon.get_next_room("The Triforce", Direction::South).unwrap().unwrap().name, "Entrance");
dungeon.add_room("Yes").unwrap();
dungeon.set_link("Entrance", Direction::West, "Yes").unwrap();
assert_eq!(dungeon.get_next_room("Entrance", Direction::West).unwrap().unwrap().name, "Yes");
assert_eq!(dungeon.get_next_room("Room of Wonders", Direction::East).unwrap().unwrap().name, "Entrance");
}
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_eq!(path.len(),2);
}
#[test]
fn longer_path() {
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.set_link("1", Direction::East, "2").unwrap();
dungeon.set_link("2", Direction::South, "3").unwrap();
dungeon.set_link("3", Direction::West, "4").unwrap();
dungeon.set_link("4", Direction::South, "5").unwrap();
assert_eq!(dungeon.get_next_room("1", Direction::East).unwrap().unwrap().name, "2");
assert_eq!(dungeon.get_next_room("2", Direction::South).unwrap().unwrap().name, "3");
assert_eq!(dungeon.get_next_room("3", Direction::West).unwrap().unwrap().name, "4");
assert_eq!(dungeon.get_next_room("4", Direction::South).unwrap().unwrap().name, "5");
let path = dungeon.find_path("1","5").unwrap().unwrap();
assert_eq!(path.len(),5);
let first_room = path[0];
let last_room = path[4];
assert_eq!(first_room.name,"1");
assert_eq!(last_room.name,"5");
}
#[test]
fn path_error() {
let mut dungeon = Dungeon::new();
dungeon.add_room("exists").unwrap();
assert!(dungeon.find_path("no","ma'am").is_err());
assert!(dungeon.find_path("exists","uuuh i think not").is_err());
}