Решение на Dungeons and Compilers от Даниел Величков
Към профила на Даниел Величков
Резултати
- 11 точки от тестове
- 0 бонус точки
- 11 точки общо
- 8 успешни тест(а)
- 7 неуспешни тест(а)
Код
use std::io::BufRead;
use std::fs::File;
use std::collections::HashMap;
use std::collections::HashSet;
use std::collections::VecDeque;
/// Различните грешки, които ще очакваме да върнете като резултат от някои невалидни операции.
/// Повече детайли по-долу.
///
#[derive(Debug)]
pub enum Errors {
DuplicateRoom(String),
UnknownRoom(String),
IoError(std::io::Error),
LineParseError { line_number: usize },
DirectionParseError(String),
}
/// Четирите посоки, в които може една стая да има съседи. Може да добавите още trait
/// имплементации, за да си улесните живота.
///
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub enum Direction {
North,
South,
East,
West,
}
fn opposite_direction(direction: Direction) -> Direction {
match direction {
Direction::East => return Direction::West,
Direction::West => return Direction::East,
Direction::North => return Direction::South,
Direction::South => return Direction::North
}
}
/// Една стая в подземията. Дефинира се само с име, макар че в по-интересна имплементация може да
/// държи item-и, противници...
#[derive(PartialEq, Eq, Hash, Clone)]
pub struct Room {
pub name: String,
// Каквито други полета ви трябват
}
impl Room {
pub fn new(given_name : String) -> Self {
Room {
name: given_name
}
}
}
#[derive(PartialEq, Eq, Hash)]
pub struct Link {
pub from: Room,
pub direction: Direction
}
impl Link {
pub fn new(from: Room, direction: Direction) -> Self {
Link {
from: from,
direction: direction
}
}
}
/// Контейнер за стаите и не само. Ще работим предимно със тази структура.
///
pub struct Dungeon {
pub rooms: HashMap<String, Room>,
pub hash_map: HashMap<Link , Room>,
// Каквито полета ви трябват
}
impl Dungeon {
/// Конструиране на празен Dungeon, в който няма никакви стаи.
///
pub fn new() -> Self {
Dungeon {
rooms: HashMap::new(),
hash_map: HashMap::new()
}
}
/// Добавяне на стая към Dungeon с име `name`. Връща `Ok(())` при успех. Ако вече има стая с
/// такова име, очакваме да върнете `Errors::DuplicateRoom` с името.
///
pub fn add_room(&mut self, name: &str) -> Result<(), Errors> {
if self.rooms.contains_key(&name.to_string()) {
return Err(Errors::DuplicateRoom(name.to_string()))
}
let room = Room::new(name.to_string());
&self.rooms.insert(name.to_string(), room);
Ok(())
}
/// Прочитане на дадена стая -- когато извикаме `get_room`, очакваме reference към `Room`
/// структурата с това име.
///
/// Ако няма такава стая, очакваме `Errors::UnknownRoom` с подаденото име.
///
pub fn get_room(&self, room_name: &str) -> Result<&Room, Errors> {
if self.rooms.contains_key(&room_name.to_string()) {
return Ok(self.rooms.get(&room_name.to_string()).unwrap());
}
Err(Errors::UnknownRoom(room_name.to_string()))
}
/// Добавяне на съсед на дадена стая. След извикването на функцията, очакваме стаята с име
/// `room_name` да има връзка в посока `direction` със стаята с име `other_room_name`.
///
/// Също така очакваме `other_room_name` да има връзка с `room_name` в *обратната* посока.
///
/// Успешен резултат е `Ok(())`. В случай, че която от двете стаи не същестува, очакваме грешка
/// `Errors::UnknownRoom` със съответното име на липсваща стая. Ако и двете липсват, спокойно
/// върнете тази, която проверявате първо.
///
pub fn set_link(
&mut self,
room_name: &str,
direction: Direction,
other_room_name: &str,
) -> Result<(), Errors> {
let mut flag_from = false;
let mut flag_to = false;
if self.rooms.contains_key(&room_name.to_string()) {
flag_from = true;
}
if self.rooms.contains_key(&other_room_name.to_string()) {
flag_to = true;
}
if flag_from == true && flag_to == true {
let r1 = self.rooms.get(&room_name.to_string()).unwrap();
let r2 = self.rooms.get(&other_room_name.to_string()).unwrap();
let link = Link::new(r1.clone(), direction);
let link_back = Link::new(r2.clone(), opposite_direction(direction));
&self.hash_map.insert(link, r2.clone());
&self.hash_map.insert(link_back, r1.clone());
return Ok(());
} else {
let missing_room;
if flag_from == false {
missing_room = room_name.to_string();
} else {
missing_room = other_room_name.to_string();
}
Err(Errors::UnknownRoom(missing_room))
}
}
/// Четене на съседа нJN?IH
/// U"*Y&[]=а стаята с име `r ,lmoom_name` в посока `direction`. Тук има няколко
/// варианта на изход:o
///
/// - Ако подадената стая не съществува, очакваме грешка `Errors::UnknownRoom`
/// - Ако подадената стая няма съсед в тази посока, Ok(None) е смисления резултат
/// - Иначе, чакаме reference към `Room` структурата на въпросния съсед, опакована в `Ok(Some(`.
///
pub fn get_next_room(&self, room_name: &str, direction: Direction) -> Result<Option<&Room>, Errors> {
let mut flag = false;
if self.rooms.contains_key(&room_name.to_string()) {
flag = true;
}
if flag == false {
Err(Errors::UnknownRoom(room_name.to_string()))
} else {
let r1 = self.rooms.get(&room_name.to_string()).unwrap();
let link = Link::new(r1.clone(), direction);
if self.hash_map.get(&link).is_none() {
return Ok(None);
} else {
return Ok(Some(self.hash_map.get(&link).unwrap()));
}
}
}
/// Прочитаме структурата на dungeon от нещо, което имплементира `BufRead`. Това може да е
/// файл, или, ако тестваме, може да е просто колекция от байтове.
///
/// Успешен резултат връща новосъздадения dungeon, пакетиран в `Ok`.
///
/// Вижте по-долу за обяснение на грешките, които очакваме.
///
pub fn from_reader<B: BufRead>(reader: B) -> Result<Self, Errors> {
todo!()
}
/// Търси път от `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 mut flag_from = false;
let mut flag_to = false;
if self.rooms.contains_key(&start_room_name.to_string()) {
flag_from = true;
}
if self.rooms.contains_key(&end_room_name.to_string()) {
flag_to = true;
}
if flag_from == false {
return Err(Errors::UnknownRoom(start_room_name.to_string()));
}
if flag_to == false {
return Err(Errors::UnknownRoom(end_room_name.to_string()));
}
let mut visited = HashSet::new();
let mut queue = VecDeque::new();
let mut parents = HashMap::new();
queue.push_back(start_room_name.to_string());
while !queue.is_empty() {
let vertex = queue.front().unwrap().clone();
queue.pop_front();
let vertex_room = self.rooms.get(&vertex.to_string()).unwrap();
visited.insert(vertex.to_string());
let link_east = Link::new(vertex_room.clone(), Direction::East);
let link_west = Link::new(vertex_room.clone(), Direction::West);
let link_north = Link::new(vertex_room.clone(), Direction::North);
let link_south = Link::new(vertex_room.clone(), Direction::South);
if self.hash_map.contains_key(&link_east) {
let name = self.hash_map.get(&link_east).unwrap().name.clone();
if !visited.contains(&name) {
queue.push_back(name.clone());
parents.insert(name, vertex.clone());
}
}
if self.hash_map.contains_key(&link_west) {
let name = self.hash_map.get(&link_west).unwrap().name.clone();
if !visited.contains(&name) {
queue.push_back(name.clone());
parents.insert(name, vertex.clone());
}
}
if self.hash_map.contains_key(&link_north) {
let name = self.hash_map.get(&link_north).unwrap().name.clone();
if !visited.contains(&name) {
queue.push_back(name.clone());
parents.insert(name, vertex.clone());
}
}
if self.hash_map.contains_key(&link_south) {
let name = self.hash_map.get(&link_south).unwrap().name.clone();
if !visited.contains(&name) {
queue.push_back(name.clone());
parents.insert(name, vertex.clone());
}
}
}
if visited.contains(&end_room_name.to_string()) {
let mut path = Vec::new();
let mut room = self.rooms.get(&end_room_name.to_string()).unwrap();
path.push(room);
let mut current = end_room_name.to_string();
while current != start_room_name.to_string() {
println!("{}", current);
let name = parents.get(¤t).unwrap();
room = self.rooms.get(name).unwrap();
path.push(room);
current = name.clone();
}
return Ok(Some(path));
} else {
return Ok(None);
}
}
}
Лог от изпълнението
Compiling solution v0.1.0 (/tmp/d20220116-3533338-17rqfwy/solution) warning: unused import: `std::fs::File` --> src/lib.rs:2:5 | 2 | use std::fs::File; | ^^^^^^^^^^^^^ | = note: `#[warn(unused_imports)]` on by default warning: unused variable: `reader` --> src/lib.rs:199:36 | 199 | pub fn from_reader<B: BufRead>(reader: B) -> Result<Self, Errors> { | ^^^^^^ help: if this is intentional, prefix it with an underscore: `_reader` | = note: `#[warn(unused_variables)]` on by default warning: unused borrow that must be used --> src/lib.rs:99:9 | 99 | &self.rooms.insert(name.to_string(), room); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the borrow produces a value | = note: `#[warn(unused_must_use)]` on by default help: use `let _ = ...` to ignore the resulting value | 99 | let _ = &self.rooms.insert(name.to_string(), room); | +++++++ warning: unused borrow that must be used --> src/lib.rs:147:13 | 147 | &self.hash_map.insert(link, r2.clone()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the borrow produces a value | help: use `let _ = ...` to ignore the resulting value | 147 | let _ = &self.hash_map.insert(link, r2.clone()); | +++++++ warning: unused borrow that must be used --> src/lib.rs:148:13 | 148 | &self.hash_map.insert(link_back, r1.clone()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the borrow produces a value | help: use `let _ = ...` to ignore the resulting value | 148 | let _ = &self.hash_map.insert(link_back, r1.clone()); | +++++++ warning: `solution` (lib) generated 5 warnings Finished test [unoptimized + debuginfo] target(s) in 3.63s 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 ... FAILED 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 ... ok test solution_test::test_invalid_parsing ... FAILED test solution_test::test_io_error ... FAILED test solution_test::test_overwriting_a_room_link ... ok test solution_test::test_parsing_cyrillic_rooms ... FAILED test solution_test::test_parsing_no_rooms_or_links ... FAILED test solution_test::test_parsing_rooms ... FAILED test solution_test::test_room_errors ... ok test solution_test::test_room_links ... ok failures: ---- solution_test::test_finding_a_direct_path stdout ---- Treasure Room thread '<unnamed>' panicked at 'assertion failed: `(left == right)` left: `["Treasure Room", "Entrance"]`, right: `["Entrance", "Treasure Room"]`', tests/solution_test.rs:314:9 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace thread 'main' panicked at 'assertion failed: `(left == right)` left: `["Treasure Room", "Entrance"]`, right: `["Entrance", "Treasure Room"]`', tests/solution_test.rs:306:5 ---- solution_test::test_finding_an_indirect_path stdout ---- Treasure Room Hallway 2 Side Closet Hallway 1 thread '<unnamed>' panicked at 'assertion failed: `(left == right)` left: `"Treasure Room"`, right: `"Entrance"`', tests/solution_test.rs:347:9 thread 'main' panicked at 'assertion failed: `(left == right)` left: `"Treasure Room"`, right: `"Entrance"`', tests/solution_test.rs:320:5 ---- solution_test::test_invalid_parsing stdout ---- thread 'main' panicked at 'not yet implemented', /tmp/d20220116-3533338-17rqfwy/solution/src/lib.rs:200:9 ---- solution_test::test_io_error stdout ---- thread '<unnamed>' panicked at 'not yet implemented', /tmp/d20220116-3533338-17rqfwy/solution/src/lib.rs:200:9 thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', tests/solution_test.rs:194:5 ---- solution_test::test_parsing_cyrillic_rooms stdout ---- thread 'main' panicked at 'not yet implemented', /tmp/d20220116-3533338-17rqfwy/solution/src/lib.rs:200:9 ---- solution_test::test_parsing_no_rooms_or_links stdout ---- thread 'main' panicked at 'not yet implemented', /tmp/d20220116-3533338-17rqfwy/solution/src/lib.rs:200:9 ---- solution_test::test_parsing_rooms stdout ---- thread 'main' panicked at 'not yet implemented', /tmp/d20220116-3533338-17rqfwy/solution/src/lib.rs:200:9 failures: solution_test::test_finding_a_direct_path solution_test::test_finding_an_indirect_path solution_test::test_invalid_parsing solution_test::test_io_error solution_test::test_parsing_cyrillic_rooms solution_test::test_parsing_no_rooms_or_links solution_test::test_parsing_rooms test result: FAILED. 8 passed; 7 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s error: test failed, to rerun pass '--test solution_test'