Решение на Dungeons and Compilers от Даниел Янев

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

Към профила на Даниел Янев

Резултати

  • 5 точки от тестове
  • 0 бонус точки
  • 5 точки общо
  • 4 успешни тест(а)
  • 11 неуспешни тест(а)

Код

use std::collections::HashMap;
use std::io::BufRead;
/// Различните грешки, които ще очакваме да върнете като резултат от някои невалидни операции.
/// Повече детайли по-долу.
///
#[derive(Debug)]
pub enum Errors {
DuplicateRoom(String),
UnknownRoom(String),
IoError(std::io::Error),
LineParseError { line_number: usize },
DirectionParseError(String),
}
/// Четирите посоки, в които може една стая да има съседи. Може да добавите още trait
/// имплементации, за да си улесните живота.
///
#[derive(Clone, Copy)]
pub enum Direction {
North,
South,
East,
West,
}
impl Direction {
pub fn reverse(dir: Direction) -> Direction {
match dir {
Direction::North => Direction::South,
Direction::South => Direction::North,
Direction::West => Direction::East,
Direction::East => Direction::West,
}
}
}
/// Една стая в подземията. Дефинира се само с име, макар че в по-интересна имплементация може да
/// държи item-и, противници...
///
pub struct Room {
pub name: String,
pub north: Option<String>,
pub south: Option<String>,
pub west: Option<String>,
pub east: Option<String>,
}
impl Room {
pub fn create(name: &str) -> Room {
Room { name: name.to_string(), north: None, east: None, south: None, west: None}
}
pub fn get_neighbor(&self, direction: Direction) -> Option<String> {
match direction {
Direction::North => self.north.clone(),
Direction::South => self.south.clone(),
Direction::West => self.west.clone(),
Direction::East => self.east.clone()
}
}
pub fn connect_rooms(&mut self, dir: Direction, other_room_name: &str) {
match dir {
Direction::North => self.north = Some(other_room_name.to_string()),
Direction::South => self.south = Some(other_room_name.to_string()),
Direction::West => self.west = Some(other_room_name.to_string()),
Direction::East => self.east = Some(other_room_name.to_string())
}
}
}
/// Контейнер за стаите и не само. Ще работим предимно със тази структура.
///
pub struct Dungeon {
// Каквито полета ви трябват
rooms: HashMap<String, Room>
}
impl Dungeon {
/// Конструиране на празен Dungeon, в който няма никакви стаи.
///
pub fn new() -> Self {
Dungeon { rooms: HashMap::new() }
}
/// Добавяне на стая към Dungeon с име `name`. Връща `Ok(())` при успех. Ако вече има стая с
/// такова име, очакваме да върнете `Errors::DuplicateRoom` с името.
///
pub fn add_room(&mut self, name: &str) -> Result<(), Errors> {
match self.rooms.insert(name.to_string(), Room::create(name)) {
None => Ok(()),
Some(_) => Err(Errors::DuplicateRoom(name.to_string()))
}
}
pub fn get_room_mut(&mut self, room_name: &str) -> Result<&mut Room, Errors> {
match self.rooms.get_mut(room_name) {
Some(room) => Ok(room),
None => Err(Errors::UnknownRoom(room_name.to_string()))
}
}
/// Прочитане на дадена стая -- когато извикаме `get_room`, очакваме reference към `Room`
/// структурата с това име.
///
/// Ако няма такава стая, очакваме `Errors::UnknownRoom` с подаденото име.
///
pub fn get_room(&self, room_name: &str) -> Result<&Room, Errors> {
match self.rooms.get(room_name) {
Some(room) => Ok(room),
None => 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 room_res = self.get_room(room_name);
let o_room_res = self.get_room(other_room_name);
match (room_res, o_room_res) {
(e @ Err(_), _) => e.map(|_| ()),
(_, e @ Err(_)) => e.map(|_| ()),
(Ok(_), Ok(_)) => {
self.connect_helper(room_name, direction, other_room_name);
self.connect_helper(other_room_name, Direction::reverse(direction), other_room_name);
Ok(())
}
}
}
fn connect_helper(&mut self, room_name: &str, direction: Direction, other_room_name: &str) {
let room = self.get_room_mut(room_name).unwrap();
room.connect_rooms(direction, other_room_name)
}
/// Четене на съседа на стаята с име `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> {
match self.get_room(room_name) {
e @ Err(_) => e.map(|_| None),
Ok(room) => {
match room.get_neighbor(direction) {
None => Ok(None),
Some(neighbor_room_name) => self.get_room(&neighbor_room_name).map(|room_ref| Some(room_ref))
}
}
}
}
/// Прочитаме структурата на dungeon от нещо, което имплементира `BufRead`. Това може да е
/// файл, или, ако тестваме, може да е просто колекция от байтове.
///
/// Успешен резултат връща новосъздадения dungeon, пакетиран в `Ok`.
///
/// Вижте по-долу за обяснение на грешките, които очакваме.
///
pub fn from_reader<B: BufRead>(reader: B) -> Result<Self, Errors> {
todo!()
}
/// match_prefix("- ", "- Foo") //=> Some("Foo")
/// match_prefix("- ", "Bar") //=> None
///
fn match_prefix<'a, 'b>(prefix: &'a str, input: &'b str) -> Option<&'b str> {
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> {
todo!()
}
}

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

Compiling solution v0.1.0 (/tmp/d20220116-3533338-3kd3f7/solution)
warning: unused variable: `reader`
   --> src/lib.rs:177:36
    |
177 |     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 variable: `prefix`
   --> src/lib.rs:184:29
    |
184 |     fn match_prefix<'a, 'b>(prefix: &'a str, input: &'b str) -> Option<&'b str> {
    |                             ^^^^^^ help: if this is intentional, prefix it with an underscore: `_prefix`

warning: unused variable: `input`
   --> src/lib.rs:184:46
    |
184 |     fn match_prefix<'a, 'b>(prefix: &'a str, input: &'b str) -> Option<&'b str> {
    |                                              ^^^^^ help: if this is intentional, prefix it with an underscore: `_input`

warning: unused variable: `start_room_name`
   --> src/lib.rs:198:9
    |
198 |         start_room_name: &str,
    |         ^^^^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_start_room_name`

warning: unused variable: `end_room_name`
   --> src/lib.rs:199:9
    |
199 |         end_room_name: &str
    |         ^^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_end_room_name`

warning: associated function is never used: `match_prefix`
   --> src/lib.rs:184:8
    |
184 |     fn match_prefix<'a, 'b>(prefix: &'a str, input: &'b str) -> Option<&'b str> {
    |        ^^^^^^^^^^^^
    |
    = note: `#[warn(dead_code)]` on by default

warning: `solution` (lib) generated 6 warnings
    Finished test [unoptimized + debuginfo] target(s) in 3.31s
     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 ... FAILED
test solution_test::test_cyrillic_room_names ... FAILED
test solution_test::test_finding_a_direct_path ... FAILED
test solution_test::test_finding_a_reflexive_path ... FAILED
test solution_test::test_finding_an_indirect_path ... FAILED
test solution_test::test_finding_no_path ... FAILED
test solution_test::test_invalid_parsing ... FAILED
test solution_test::test_io_error ... 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_adding_rooms_2 stdout ----
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `"Hallway 1"`,
 right: `"Entrance"`', tests/solution_test.rs:97:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

---- solution_test::test_cyrillic_room_names stdout ----
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `"Хол"`,
 right: `"Антре"`', tests/solution_test.rs:180:5

---- solution_test::test_finding_a_direct_path stdout ----
thread '<unnamed>' panicked at 'not yet implemented', src/lib.rs:201:9
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', tests/solution_test.rs:306:5

---- solution_test::test_finding_a_reflexive_path stdout ----
thread '<unnamed>' panicked at 'not yet implemented', src/lib.rs:201:9
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', tests/solution_test.rs:354:5

---- solution_test::test_finding_an_indirect_path stdout ----
thread '<unnamed>' panicked at 'not yet implemented', src/lib.rs:201:9
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', tests/solution_test.rs:320:5

---- solution_test::test_finding_no_path stdout ----
thread '<unnamed>' panicked at 'not yet implemented', src/lib.rs:201:9
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', tests/solution_test.rs:372:5

---- solution_test::test_invalid_parsing stdout ----
thread 'main' panicked at 'not yet implemented', /tmp/d20220116-3533338-3kd3f7/solution/src/lib.rs:178:9

---- solution_test::test_io_error stdout ----
thread '<unnamed>' panicked at 'not yet implemented', /tmp/d20220116-3533338-3kd3f7/solution/src/lib.rs:178: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-3kd3f7/solution/src/lib.rs:178:9

---- solution_test::test_parsing_no_rooms_or_links stdout ----
thread 'main' panicked at 'not yet implemented', /tmp/d20220116-3533338-3kd3f7/solution/src/lib.rs:178:9

---- solution_test::test_parsing_rooms stdout ----
thread 'main' panicked at 'not yet implemented', /tmp/d20220116-3533338-3kd3f7/solution/src/lib.rs:178:9


failures:
    solution_test::test_adding_rooms_2
    solution_test::test_cyrillic_room_names
    solution_test::test_finding_a_direct_path
    solution_test::test_finding_a_reflexive_path
    solution_test::test_finding_an_indirect_path
    solution_test::test_finding_no_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. 4 passed; 11 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

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

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

Даниел качи първо решение на 11.01.2022 16:03 (преди над 3 години)

Даниел качи решение на 11.01.2022 16:10 (преди над 3 години)

use std::collections::HashMap;
use std::io::BufRead;
-
/// Различните грешки, които ще очакваме да върнете като резултат от някои невалидни операции.
/// Повече детайли по-долу.
///
#[derive(Debug)]
pub enum Errors {
DuplicateRoom(String),
UnknownRoom(String),
IoError(std::io::Error),
LineParseError { line_number: usize },
DirectionParseError(String),
}
/// Четирите посоки, в които може една стая да има съседи. Може да добавите още trait
/// имплементации, за да си улесните живота.
///
#[derive(Clone, Copy)]
pub enum Direction {
North,
South,
East,
West,
}
+impl Direction {
+ pub fn reverse(dir: Direction) -> Direction {
+ match dir {
+ Direction::North => Direction::South,
+ Direction::South => Direction::North,
+ Direction::West => Direction::East,
+ Direction::East => Direction::West,
+ }
+ }
+}
+
/// Една стая в подземията. Дефинира се само с име, макар че в по-интересна имплементация може да
/// държи item-и, противници...
///
pub struct Room {
pub name: String,
- pub w: String,
- pub e: String,
- pub n: String,
- pub s: String
+ pub north: Option<String>,
+ pub south: Option<String>,
+ pub west: Option<String>,
+ pub east: Option<String>,
}
-/// Контейнер за стаите и не само. Ще работим предимно с тази структура.
-///
-pub struct Dungeon {
- map: HashMap<String, Room>
-}
+impl Room {
+ pub fn create(name: &str) -> Room {
+ Room { name: name.to_string(), north: None, east: None, south: None, west: None}
+ }
-impl Room{
- pub fn new(name: String) -> Self {
- Room{
- name: name,
- w: String::new(),
- e: String::new(),
- n: String::new(),
- s: String::new(),
+ pub fn get_neighbor(&self, direction: Direction) -> Option<String> {
+ match direction {
+ Direction::North => self.north.clone(),
+ Direction::South => self.south.clone(),
+ Direction::West => self.west.clone(),
+ Direction::East => self.east.clone()
}
}
+
+ pub fn connect_rooms(&mut self, dir: Direction, other_room_name: &str) {
+ match dir {
+ Direction::North => self.north = Some(other_room_name.to_string()),
+ Direction::South => self.south = Some(other_room_name.to_string()),
+ Direction::West => self.west = Some(other_room_name.to_string()),
+ Direction::East => self.east = Some(other_room_name.to_string())
+ }
+ }
}
+/// Контейнер за стаите и не само. Ще работим предимно със тази структура.
+///
+pub struct Dungeon {
+ // Каквито полета ви трябват
+ rooms: HashMap<String, Room>
+}
impl Dungeon {
/// Конструиране на празен Dungeon, в който няма никакви стаи.
///
pub fn new() -> Self {
- Dungeon{ map: HashMap::new() }
+ Dungeon { rooms: HashMap::new() }
}
/// Добавяне на стая към Dungeon с име `name`. Връща `Ok(())` при успех. Ако вече има стая с
/// такова име, очакваме да върнете `Errors::DuplicateRoom` с името.
///
pub fn add_room(&mut self, name: &str) -> Result<(), Errors> {
- if self.map.contains_key(name) {
- Err(Errors::DuplicateRoom(name.to_owned()))
- } else {
- self.map.insert(name.to_owned(), Room::new(name.to_owned()));
- Ok(())
+ match self.rooms.insert(name.to_string(), Room::create(name)) {
+ None => Ok(()),
+ Some(_) => Err(Errors::DuplicateRoom(name.to_string()))
}
}
+ pub fn get_room_mut(&mut self, room_name: &str) -> Result<&mut Room, Errors> {
+ match self.rooms.get_mut(room_name) {
+ Some(room) => Ok(room),
+ None => Err(Errors::UnknownRoom(room_name.to_string()))
+ }
+ }
+
/// Прочитане на дадена стая -- когато извикаме `get_room`, очакваме reference към `Room`
/// структурата с това име.
///
/// Ако няма такава стая, очакваме `Errors::UnknownRoom` с подаденото име.
///
pub fn get_room(&self, room_name: &str) -> Result<&Room, Errors> {
- let stat = self.map.get(room_name);
-
- if stat.is_none() {
- Err(Errors::UnknownRoom(room_name.to_owned()))
- } else {
- Ok(stat.unwrap())
+ match self.rooms.get(room_name) {
+ Some(room) => Ok(room),
+ None => 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 room_res = self.get_room(room_name);
+ let o_room_res = self.get_room(other_room_name);
- if self.get_room(room_name).is_err() {
- Err(Errors::UnknownRoom(room_name.to_owned()))
- } else if self.get_room(other_room_name).is_err() {
- Err(Errors::UnknownRoom(other_room_name.to_owned()))
- } else {
- self.set_next_room(room_name, other_room_name, direction);
- self.set_next_room(other_room_name, room_name, self.get_opposite_dir(direction));
-
- Ok(())
+ match (room_res, o_room_res) {
+ (e @ Err(_), _) => e.map(|_| ()),
+ (_, e @ Err(_)) => e.map(|_| ()),
+ (Ok(_), Ok(_)) => {
+ self.connect_helper(room_name, direction, other_room_name);
+ self.connect_helper(other_room_name, Direction::reverse(direction), other_room_name);
+ Ok(())
+ }
+
}
}
- // Helper function
- pub fn get_opposite_dir(&self, direction: Direction) -> Direction{
- match direction {
- Direction::North => Direction::South,
- Direction::South => Direction::North,
- Direction::West => Direction::East,
- Direction::East => Direction::West
- }
+ fn connect_helper(&mut self, room_name: &str, direction: Direction, other_room_name: &str) {
+ let room = self.get_room_mut(room_name).unwrap();
+ room.connect_rooms(direction, other_room_name)
}
- // Helper function
- pub fn set_next_room(&mut self, room_name: &str, other_room_name: &str, direction: Direction) {
- let room = self.map.get_mut(room_name).unwrap();
-
- match direction {
- Direction::North => {
- room.n = other_room_name.to_owned();
- },
- Direction::South => {
- room.s = other_room_name.to_owned();
- },
- Direction::West => {
- room.w = other_room_name.to_owned();
- },
- Direction::East => {
- room.e = other_room_name.to_owned();
- }
- }
- }
-
/// Четене на съседа на стаята с име `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.get_room(room_name).is_err() {
- Err(Errors::UnknownRoom(room_name.to_owned()))
- } else {
- let room = self.get_room(room_name).unwrap();
- let next_room = match direction {
- Direction::North => &room.n,
- Direction::South => &room.s,
- Direction::West => &room.w,
- Direction::East => &room.e
- };
-
- if next_room.is_empty() {
- Ok(None)
- } else {
- Ok(Some(self.get_room(&next_room).unwrap()))
+ match self.get_room(room_name) {
+ e @ Err(_) => e.map(|_| None),
+ Ok(room) => {
+ match room.get_neighbor(direction) {
+ None => Ok(None),
+ Some(neighbor_room_name) => self.get_room(&neighbor_room_name).map(|room_ref| Some(room_ref))
+ }
}
}
}
+ /// Прочитаме структурата на dungeon от нещо, което имплементира `BufRead`. Това може да е
+ /// файл, или, ако тестваме, може да е просто колекция от байтове.
+ ///
+ /// Успешен резултат връща новосъздадения dungeon, пакетиран в `Ok`.
+ ///
+ /// Вижте по-долу за обяснение на грешките, които очакваме.
+ ///
pub fn from_reader<B: BufRead>(reader: B) -> Result<Self, Errors> {
todo!()
}
+ /// match_prefix("- ", "- Foo") //=> Some("Foo")
+ /// match_prefix("- ", "Bar") //=> None
+ ///
+ fn match_prefix<'a, 'b>(prefix: &'a str, input: &'b str) -> Option<&'b str> {
+ 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> {
todo!()
}
-
}