Решение на Dungeons and Compilers от Мариян Момчилов

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

Към профила на Мариян Момчилов

Резултати

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

Код

use std::{collections::HashMap, hash::Hash, io::BufRead, mem::take};
#[derive(Debug)]
pub enum Errors {
DuplicateRoom(String),
UnknownRoom(String),
IoError(std::io::Error),
LineParseError { line_number: usize },
DirectionParseError(String),
}
#[derive(Clone, Copy, Eq, PartialEq, Hash)]
pub enum Direction {
North,
South,
East,
West,
}
impl Direction {
pub fn reverse(&self) -> Self {
match *self {
Self::North => Self::South,
Self::South => Self::North,
Self::East => Self::West,
Self::West => Self::East,
}
}
}
pub struct Room {
pub name: String,
pub links: HashMap<Direction, String>
}
impl Room {
pub fn new(name: String) -> Self {
Room { name, links: HashMap::new() }
}
}
struct Link {
room_name: String,
other_room_name: String,
direction: Direction
}
pub struct Dungeon {
rooms: HashMap<String, Room>,
}
impl Dungeon {
pub fn new() -> Self {
Dungeon {
rooms: HashMap::new(),
}
}
pub fn add_room(&mut self, name: &str) -> Result<(), Errors> {
match self.rooms.get(name) {
Some(_) => Err(Errors::DuplicateRoom(String::from(name))),
None => {
self.rooms.insert(String::from(name), Room::new(String::from(name)));
Ok(())
}
}
}
pub fn get_room(&self, room_name: &str) -> Result<&Room, Errors> {
match self.rooms.get(room_name) {
Some(room_ref) => Ok(room_ref),
None => Err(Errors::UnknownRoom(String::from(room_name))),
}
}
pub fn set_link(
&mut self,
room_name: &str,
direction: Direction,
other_room_name: &str,
) -> Result<(), Errors> {
let _ = self.get_room(room_name)?;
let _ = self.get_room(other_room_name)?;
let room = self.get_room_mut(room_name)?;
room.links.insert(direction, String::from(other_room_name));
let other_room = self.get_room_mut(other_room_name)?;
other_room.links.insert(direction.reverse(), String::from(room_name));
Ok(())
}
pub fn get_next_room(&self, room_name: &str, direction: Direction) -> Result<Option<&Room>, Errors> {
let room = self.get_room(room_name)?;
let direction_room = room.links.get(&direction);
Ok(direction_room.map_or_else(|| None, |s| self.rooms.get(s)))
}
fn get_room_mut(&mut self, room_name: &str) -> Result<&mut Room, Errors> {
match self.rooms.get_mut(room_name) {
Some(room_ref) => Ok(room_ref),
None => Err(Errors::UnknownRoom(String::from(room_name))),
}
}
}
impl Dungeon {
pub fn from_reader<B: BufRead>(reader: B) -> Result<Self, Errors> {
let directions = HashMap::from([
("East", Direction::East),
("West", Direction::West),
("North", Direction::North),
("South", Direction::South)
]);
let mut dungeon = Self::new();
let mut lines = reader.lines()
.map(|l| l.map_err(Self::convert_to_errors));
let line = lines.next();
let _ = match line {
Some(s) => {
match s {
Err(e) => return Err(e),
Ok(s) => {
match Self::parse_rooms_header(&s) {
Ok(_) => Ok(()) as Result<(), Errors>,
Err(e) => return Err(e)
}
}
}
},
None => return Err(Errors::LineParseError{ line_number: 0 })
};
let mut line_number = 1;
while let Some(r) = lines.next() {
let line = r?;
if line.trim() == "" {
break;
}
let name = Self::parse_room(&line, line_number)?;
dungeon.add_room(&name)?;
line_number += 1;
};
let line = lines.next();
let _ = match line {
Some(s) => {
match s {
Err(e) => return Err(e),
Ok(s) => {
match Self::parse_links_header(&s, line_number) {
Ok(_) => Ok(()) as Result<(), Errors>,
Err(e) => return Err(e)
}
}
}
},
None => return Err(Errors::LineParseError{ line_number })
};
while let Some(r) = lines.next() {
let line = r?;
if line.trim() == "" {
break;
}
let link = Self::parse_link(&line, line_number, &directions)?;
dungeon.set_link(&link.room_name, link.direction, &link.other_room_name)?;
line_number += 1;
};
Ok(dungeon)
}
fn is_header(s: &str) -> bool {
&s[0..] == "## Rooms"
}
fn is_links(s: &str) -> bool {
&s[0..] == "## Links"
}
fn parse_rooms_header(s: &str) -> Result<(), Errors> {
if Self::is_header(s) {
return Ok(());
}
Err(Errors::LineParseError{ line_number: 0 })
}
fn parse_links_header(s: &str, line_number: usize) -> Result<(), Errors> {
if Self::is_links(s) {
return Ok(());
}
Err(Errors::LineParseError{ line_number })
}
fn convert_to_errors(err: std::io::Error) -> Errors {
Errors::IoError(std::io::Error::from(err.kind()))
}
fn parse_room(line: &str, line_number: usize) -> Result<String, Errors> {
if line.len() == 0 || &line[0..1] != "-" {
return Err(Errors::LineParseError{ line_number });
}
let trimmed = line[1..].trim();
if trimmed.len() == 0 {
return Err(Errors::LineParseError{ line_number });
}
Ok(String::from(trimmed))
}
fn parse_link(line: &str, line_number: usize, direction_map: &HashMap<&str, Direction>) -> Result<Link, Errors> {
if line.len() == 0 || &line[0..1] != "-" {
return Err(Errors::LineParseError{ line_number });
}
let trimmed = line[1..].trim();
if trimmed.len() == 0 {
return Err(Errors::LineParseError{ line_number });
}
let mut splitted = trimmed.split("->").map(|s| String::from(s.trim())).collect::<Vec<String>>();
if splitted.len() != 3 {
return Err(Errors::LineParseError{ line_number });
}
let direction = match direction_map.get(&splitted[1].as_str()) {
None => return Err(Errors::LineParseError{ line_number }),
Some(direction) => *direction
};
let room_name = take(&mut splitted[0]);
let other_room_name = take(&mut splitted[2]);
Ok(Link{ room_name, other_room_name, direction })
}
}
impl Dungeon {
pub fn find_path(
&self,
start_room_name: &str,
end_room_name: &str
) -> Result<Option<Vec<&Room>>, Errors> {
let mut parents: HashMap<String, &Room> = HashMap::new();
let mut stack = Vec::<&Room>::new();
let start_room = self.get_room(start_room_name)?;
stack.push(start_room);
parents.insert(String::from(&start_room.name), start_room);
self.all_paths_dfs(&mut stack, &mut parents);
let end_room = self.get_room(end_room_name)?;
let mut path = Vec::<&Room>::new();
let mut current = end_room;
while current as *const _ != start_room as *const _ {
current = match parents.get(&current.name) {
None => return Ok(None),
Some(room_ref_ref) => {
path.push(current);
*room_ref_ref
}
};
}
path.push(current);
Ok(Some(path))
}
fn all_paths_dfs<'a>(&'a self, stack: &mut Vec<&'a Room>, parents: &mut HashMap<String, &'a Room>) -> Result<(), Errors> {
while stack.len() > 0 {
let room = stack.pop().unwrap();
self.dfs_rec(stack, parents, room)?;
}
Ok(())
}
fn dfs_rec<'a>(&'a self, stack: &mut Vec<&'a Room>, parents: &mut HashMap<String, &'a Room>, room: &'a Room) -> Result<(), Errors> {
for (_, name_ref) in room.links.iter() {
if let None = parents.get(name_ref) {
let next_room = self.get_room(name_ref)?;
stack.push(next_room);
parents.insert(String::from(name_ref), room);
self.dfs_rec(stack, parents, next_room)?;
}
}
Ok(())
}
}

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

Compiling solution v0.1.0 (/tmp/d20220116-3533338-qpmga5/solution)
warning: unused `Result` that must be used
   --> src/lib.rs:249:9
    |
249 |         self.all_paths_dfs(&mut stack, &mut parents);
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: `#[warn(unused_must_use)]` on by default
    = note: this `Result` may be an `Err` variant, which should be handled

warning: `solution` (lib) generated 1 warning
    Finished test [unoptimized + debuginfo] target(s) in 3.92s
     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 ... 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_direct_path stdout ----
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 ----
thread '<unnamed>' panicked at 'assertion failed: first_neighbours.contains(&second.name.as_str())', tests/solution_test.rs:342:13
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', tests/solution_test.rs:320:5

---- solution_test::test_invalid_parsing stdout ----
thread 'main' panicked at 'assertion failed: matches!(Dungeon :: from_reader(TEST_INPUT_4.trim().as_bytes()),\n         Err(Errors :: LineParseError { line_number : 1 }))', tests/solution_test.rs:277:5


failures:
    solution_test::test_finding_a_direct_path
    solution_test::test_finding_an_indirect_path
    solution_test::test_invalid_parsing

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

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

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

Мариян качи решение на 27.12.2021 13:29 (преди почти 4 години)

use std::{collections::HashMap, hash::Hash, io::BufRead, mem::take};
#[derive(Debug)]
pub enum Errors {
DuplicateRoomError(String),
UnknownRoom(String),
IOError(std::io::Error),
- LineParserError { line_number: usize },
+ LineParseError { line_number: usize },
DirectionParseError(String),
}
#[derive(Clone, Copy, Eq, PartialEq, Hash)]
pub enum Direction {
North,
South,
East,
West,
}
impl Direction {
pub fn reverse(&self) -> Self {
match *self {
Self::North => Self::South,
Self::South => Self::North,
Self::East => Self::West,
Self::West => Self::East,
}
}
}
pub struct Room {
pub name: String,
pub links: HashMap<Direction, String>
}
impl Room {
pub fn new(name: String) -> Self {
Room { name, links: HashMap::new() }
}
}
struct Link {
room_name: String,
other_room_name: String,
direction: Direction
}
pub struct Dungeon {
rooms: HashMap<String, Room>,
}
impl Dungeon {
pub fn new() -> Self {
Dungeon {
rooms: HashMap::new(),
}
}
pub fn add_room(&mut self, name: &str) -> Result<(), Errors> {
match self.rooms.get(name) {
- Some(room_ref) => Err(Errors::DuplicateRoomError(String::from(name))),
+ Some(_) => Err(Errors::DuplicateRoomError(String::from(name))),
None => {
self.rooms.insert(String::from(name), Room::new(String::from(name)));
Ok(())
}
}
}
pub fn get_room(&self, room_name: &str) -> Result<&Room, Errors> {
match self.rooms.get(room_name) {
Some(room_ref) => Ok(room_ref),
None => Err(Errors::UnknownRoom(String::from(room_name))),
}
}
pub fn set_link(
&mut self,
room_name: &str,
direction: Direction,
other_room_name: &str,
) -> Result<(), Errors> {
let _ = self.get_room(room_name)?;
let _ = self.get_room(other_room_name)?;
let room = self.get_room_mut(room_name)?;
room.links.insert(direction, String::from(other_room_name));
let other_room = self.get_room_mut(other_room_name)?;
other_room.links.insert(direction.reverse(), String::from(room_name));
Ok(())
}
pub fn get_next_room(&self, room_name: &str, direction: Direction) -> Result<Option<&Room>, Errors> {
let room = self.get_room(room_name)?;
let direction_room = room.links.get(&direction);
Ok(direction_room.map_or_else(|| None, |s| self.rooms.get(s)))
}
fn get_room_mut(&mut self, room_name: &str) -> Result<&mut Room, Errors> {
match self.rooms.get_mut(room_name) {
Some(room_ref) => Ok(room_ref),
None => Err(Errors::UnknownRoom(String::from(room_name))),
}
}
}
impl Dungeon {
pub fn from_reader<B: BufRead>(reader: B) -> Result<Self, Errors> {
- let mut directions = HashMap::from([
+ let directions = HashMap::from([
("East", Direction::East),
("West", Direction::West),
("North", Direction::North),
("South", Direction::South)
]);
let mut dungeon = Self::new();
let mut lines = reader.lines()
.map(|l| l.map_err(Self::convert_to_errors));
let line = lines.next();
let _ = match line {
Some(s) => {
match s {
Err(e) => return Err(e),
- Ok(s) => Self::parse_rooms_header(&s)
+ Ok(s) => {
+ match Self::parse_rooms_header(&s) {
+ Ok(_) => Ok(()) as Result<(), Errors>,
+ Err(e) => return Err(e)
+ }
+ }
}
},
- None => return Err(Errors::LineParserError{ line_number: 0 })
+ None => return Err(Errors::LineParseError{ line_number: 0 })
};
let mut line_number = 1;
while let Some(r) = lines.next() {
let line = r?;
if line.trim() == "" {
break;
}
let name = Self::parse_room(&line, line_number)?;
dungeon.add_room(&name)?;
line_number += 1;
};
let line = lines.next();
let _ = match line {
Some(s) => {
match s {
Err(e) => return Err(e),
- Ok(s) => Self::parse_links_header(&s, line_number)
+ Ok(s) => {
+ match Self::parse_links_header(&s, line_number) {
+ Ok(_) => Ok(()) as Result<(), Errors>,
+ Err(e) => return Err(e)
+ }
+ }
}
},
- None => return Err(Errors::LineParserError{ line_number })
+ None => return Err(Errors::LineParseError{ line_number })
};
while let Some(r) = lines.next() {
let line = r?;
if line.trim() == "" {
break;
}
let link = Self::parse_link(&line, line_number, &directions)?;
dungeon.set_link(&link.room_name, link.direction, &link.other_room_name)?;
line_number += 1;
};
Ok(dungeon)
}
fn is_header(s: &str) -> bool {
&s[0..] == "## Rooms"
}
fn is_links(s: &str) -> bool {
&s[0..] == "## Links"
}
fn parse_rooms_header(s: &str) -> Result<(), Errors> {
if Self::is_header(s) {
return Ok(());
}
- Err(Errors::LineParserError{ line_number: 0 })
+ Err(Errors::LineParseError{ line_number: 0 })
}
fn parse_links_header(s: &str, line_number: usize) -> Result<(), Errors> {
if Self::is_links(s) {
return Ok(());
}
- Err(Errors::LineParserError{ line_number })
+ Err(Errors::LineParseError{ line_number })
}
fn convert_to_errors(err: std::io::Error) -> Errors {
Errors::IOError(std::io::Error::from(err.kind()))
}
fn parse_room(line: &str, line_number: usize) -> Result<String, Errors> {
if line.len() == 0 || &line[0..1] != "-" {
- return Err(Errors::LineParserError{ line_number });
+ return Err(Errors::LineParseError{ line_number });
}
let trimmed = line[1..].trim();
if trimmed.len() == 0 {
- return Err(Errors::LineParserError{ line_number });
+ return Err(Errors::LineParseError{ line_number });
}
- Ok((String::from(trimmed)))
+ Ok(String::from(trimmed))
}
fn parse_link(line: &str, line_number: usize, direction_map: &HashMap<&str, Direction>) -> Result<Link, Errors> {
if line.len() == 0 || &line[0..1] != "-" {
- return Err(Errors::LineParserError{ line_number });
+ return Err(Errors::LineParseError{ line_number });
}
let trimmed = line[1..].trim();
if trimmed.len() == 0 {
- return Err(Errors::LineParserError{ line_number });
+ return Err(Errors::LineParseError{ line_number });
}
let mut splitted = trimmed.split("->").map(|s| String::from(s.trim())).collect::<Vec<String>>();
if splitted.len() != 3 {
- return Err(Errors::LineParserError{ line_number });
+ return Err(Errors::LineParseError{ line_number });
}
let direction = match direction_map.get(&splitted[1].as_str()) {
- None => return Err(Errors::LineParserError{ line_number }),
+ None => return Err(Errors::LineParseError{ line_number }),
Some(direction) => *direction
};
let room_name = take(&mut splitted[0]);
let other_room_name = take(&mut splitted[2]);
Ok(Link{ room_name, other_room_name, direction })
}
+}
+impl Dungeon {
+ pub fn find_path(
+ &self,
+ start_room_name: &str,
+ end_room_name: &str
+ ) -> Result<Option<Vec<&Room>>, Errors> {
+ let mut parents: HashMap<String, &Room> = HashMap::new();
+ let mut stack = Vec::<&Room>::new();
+
+ let start_room = self.get_room(start_room_name)?;
+ stack.push(start_room);
+ parents.insert(String::from(&start_room.name), start_room);
+
+ self.all_paths_dfs(&mut stack, &mut parents);
+
+ let end_room = self.get_room(end_room_name)?;
+ let mut path = Vec::<&Room>::new();
+ let mut current = end_room;
+ while current as *const _ != start_room as *const _ {
+
+ current = match parents.get(&current.name) {
+ None => return Ok(None),
+ Some(room_ref_ref) => {
+ path.push(current);
+ *room_ref_ref
+ }
+ };
+ }
+ path.push(current);
+ Ok(Some(path))
+ }
+
+ fn all_paths_dfs<'a>(&'a self, stack: &mut Vec<&'a Room>, parents: &mut HashMap<String, &'a Room>) -> Result<(), Errors> {
+ while stack.len() > 0 {
+ let room = stack.pop().unwrap();
+ self.dfs_rec(stack, parents, room)?;
+ }
+ Ok(())
+ }
+
+ fn dfs_rec<'a>(&'a self, stack: &mut Vec<&'a Room>, parents: &mut HashMap<String, &'a Room>, room: &'a Room) -> Result<(), Errors> {
+ for (_, name_ref) in room.links.iter() {
+ if let None = parents.get(name_ref) {
+ let next_room = self.get_room(name_ref)?;
+ stack.push(next_room);
+ parents.insert(String::from(name_ref), room);
+ self.dfs_rec(stack, parents, next_room)?;
+ }
+ }
+ Ok(())
+ }
}

error[E0599]: no variant or associated item named IoError found for enum solution::Errors in the current scope

Не IOError, а IoError. Кода, който сме ти дали по условие не го преписвай на ръка, копирай го, за да избегнеш typo-та.

Мариян качи решение на 28.12.2021 11:28 (преди почти 4 години)

use std::{collections::HashMap, hash::Hash, io::BufRead, mem::take};
#[derive(Debug)]
pub enum Errors {
DuplicateRoomError(String),
UnknownRoom(String),
- IOError(std::io::Error),
+ IoError(std::io::Error),
LineParseError { line_number: usize },
DirectionParseError(String),
}
#[derive(Clone, Copy, Eq, PartialEq, Hash)]
pub enum Direction {
North,
South,
East,
West,
}
impl Direction {
pub fn reverse(&self) -> Self {
match *self {
Self::North => Self::South,
Self::South => Self::North,
Self::East => Self::West,
Self::West => Self::East,
}
}
}
pub struct Room {
pub name: String,
pub links: HashMap<Direction, String>
}
impl Room {
pub fn new(name: String) -> Self {
Room { name, links: HashMap::new() }
}
}
struct Link {
room_name: String,
other_room_name: String,
direction: Direction
}
pub struct Dungeon {
rooms: HashMap<String, Room>,
}
impl Dungeon {
pub fn new() -> Self {
Dungeon {
rooms: HashMap::new(),
}
}
pub fn add_room(&mut self, name: &str) -> Result<(), Errors> {
match self.rooms.get(name) {
Some(_) => Err(Errors::DuplicateRoomError(String::from(name))),
None => {
self.rooms.insert(String::from(name), Room::new(String::from(name)));
Ok(())
}
}
}
pub fn get_room(&self, room_name: &str) -> Result<&Room, Errors> {
match self.rooms.get(room_name) {
Some(room_ref) => Ok(room_ref),
None => Err(Errors::UnknownRoom(String::from(room_name))),
}
}
pub fn set_link(
&mut self,
room_name: &str,
direction: Direction,
other_room_name: &str,
) -> Result<(), Errors> {
let _ = self.get_room(room_name)?;
let _ = self.get_room(other_room_name)?;
let room = self.get_room_mut(room_name)?;
room.links.insert(direction, String::from(other_room_name));
let other_room = self.get_room_mut(other_room_name)?;
other_room.links.insert(direction.reverse(), String::from(room_name));
Ok(())
}
pub fn get_next_room(&self, room_name: &str, direction: Direction) -> Result<Option<&Room>, Errors> {
let room = self.get_room(room_name)?;
let direction_room = room.links.get(&direction);
Ok(direction_room.map_or_else(|| None, |s| self.rooms.get(s)))
}
fn get_room_mut(&mut self, room_name: &str) -> Result<&mut Room, Errors> {
match self.rooms.get_mut(room_name) {
Some(room_ref) => Ok(room_ref),
None => Err(Errors::UnknownRoom(String::from(room_name))),
}
}
}
impl Dungeon {
pub fn from_reader<B: BufRead>(reader: B) -> Result<Self, Errors> {
let directions = HashMap::from([
("East", Direction::East),
("West", Direction::West),
("North", Direction::North),
("South", Direction::South)
]);
let mut dungeon = Self::new();
let mut lines = reader.lines()
.map(|l| l.map_err(Self::convert_to_errors));
let line = lines.next();
let _ = match line {
Some(s) => {
match s {
Err(e) => return Err(e),
Ok(s) => {
match Self::parse_rooms_header(&s) {
Ok(_) => Ok(()) as Result<(), Errors>,
Err(e) => return Err(e)
}
}
}
},
None => return Err(Errors::LineParseError{ line_number: 0 })
};
let mut line_number = 1;
while let Some(r) = lines.next() {
let line = r?;
if line.trim() == "" {
break;
}
let name = Self::parse_room(&line, line_number)?;
dungeon.add_room(&name)?;
line_number += 1;
};
let line = lines.next();
let _ = match line {
Some(s) => {
match s {
Err(e) => return Err(e),
Ok(s) => {
match Self::parse_links_header(&s, line_number) {
Ok(_) => Ok(()) as Result<(), Errors>,
Err(e) => return Err(e)
}
}
}
},
None => return Err(Errors::LineParseError{ line_number })
};
while let Some(r) = lines.next() {
let line = r?;
if line.trim() == "" {
break;
}
let link = Self::parse_link(&line, line_number, &directions)?;
dungeon.set_link(&link.room_name, link.direction, &link.other_room_name)?;
line_number += 1;
};
Ok(dungeon)
}
fn is_header(s: &str) -> bool {
&s[0..] == "## Rooms"
}
fn is_links(s: &str) -> bool {
&s[0..] == "## Links"
}
fn parse_rooms_header(s: &str) -> Result<(), Errors> {
if Self::is_header(s) {
return Ok(());
}
Err(Errors::LineParseError{ line_number: 0 })
}
fn parse_links_header(s: &str, line_number: usize) -> Result<(), Errors> {
if Self::is_links(s) {
return Ok(());
}
Err(Errors::LineParseError{ line_number })
}
fn convert_to_errors(err: std::io::Error) -> Errors {
- Errors::IOError(std::io::Error::from(err.kind()))
+ Errors::IoError(std::io::Error::from(err.kind()))
}
fn parse_room(line: &str, line_number: usize) -> Result<String, Errors> {
if line.len() == 0 || &line[0..1] != "-" {
return Err(Errors::LineParseError{ line_number });
}
let trimmed = line[1..].trim();
if trimmed.len() == 0 {
return Err(Errors::LineParseError{ line_number });
}
Ok(String::from(trimmed))
}
fn parse_link(line: &str, line_number: usize, direction_map: &HashMap<&str, Direction>) -> Result<Link, Errors> {
if line.len() == 0 || &line[0..1] != "-" {
return Err(Errors::LineParseError{ line_number });
}
let trimmed = line[1..].trim();
if trimmed.len() == 0 {
return Err(Errors::LineParseError{ line_number });
}
let mut splitted = trimmed.split("->").map(|s| String::from(s.trim())).collect::<Vec<String>>();
if splitted.len() != 3 {
return Err(Errors::LineParseError{ line_number });
}
let direction = match direction_map.get(&splitted[1].as_str()) {
None => return Err(Errors::LineParseError{ line_number }),
Some(direction) => *direction
};
let room_name = take(&mut splitted[0]);
let other_room_name = take(&mut splitted[2]);
Ok(Link{ room_name, other_room_name, direction })
}
}
impl Dungeon {
pub fn find_path(
&self,
start_room_name: &str,
end_room_name: &str
) -> Result<Option<Vec<&Room>>, Errors> {
let mut parents: HashMap<String, &Room> = HashMap::new();
let mut stack = Vec::<&Room>::new();
let start_room = self.get_room(start_room_name)?;
stack.push(start_room);
parents.insert(String::from(&start_room.name), start_room);
self.all_paths_dfs(&mut stack, &mut parents);
let end_room = self.get_room(end_room_name)?;
let mut path = Vec::<&Room>::new();
let mut current = end_room;
while current as *const _ != start_room as *const _ {
current = match parents.get(&current.name) {
None => return Ok(None),
Some(room_ref_ref) => {
path.push(current);
*room_ref_ref
}
};
}
path.push(current);
Ok(Some(path))
}
fn all_paths_dfs<'a>(&'a self, stack: &mut Vec<&'a Room>, parents: &mut HashMap<String, &'a Room>) -> Result<(), Errors> {
while stack.len() > 0 {
let room = stack.pop().unwrap();
self.dfs_rec(stack, parents, room)?;
}
Ok(())
}
fn dfs_rec<'a>(&'a self, stack: &mut Vec<&'a Room>, parents: &mut HashMap<String, &'a Room>, room: &'a Room) -> Result<(), Errors> {
for (_, name_ref) in room.links.iter() {
if let None = parents.get(name_ref) {
let next_room = self.get_room(name_ref)?;
stack.push(next_room);
parents.insert(String::from(name_ref), room);
self.dfs_rec(stack, parents, next_room)?;
}
}
Ok(())
}
}

Мариян качи решение на 28.12.2021 11:38 (преди почти 4 години)

use std::{collections::HashMap, hash::Hash, io::BufRead, mem::take};
#[derive(Debug)]
pub enum Errors {
- DuplicateRoomError(String),
+ DuplicateRoom(String),
UnknownRoom(String),
IoError(std::io::Error),
LineParseError { line_number: usize },
DirectionParseError(String),
}
#[derive(Clone, Copy, Eq, PartialEq, Hash)]
pub enum Direction {
North,
South,
East,
West,
}
impl Direction {
pub fn reverse(&self) -> Self {
match *self {
Self::North => Self::South,
Self::South => Self::North,
Self::East => Self::West,
Self::West => Self::East,
}
}
}
pub struct Room {
pub name: String,
pub links: HashMap<Direction, String>
}
impl Room {
pub fn new(name: String) -> Self {
Room { name, links: HashMap::new() }
}
}
struct Link {
room_name: String,
other_room_name: String,
direction: Direction
}
pub struct Dungeon {
rooms: HashMap<String, Room>,
}
impl Dungeon {
pub fn new() -> Self {
Dungeon {
rooms: HashMap::new(),
}
}
pub fn add_room(&mut self, name: &str) -> Result<(), Errors> {
match self.rooms.get(name) {
- Some(_) => Err(Errors::DuplicateRoomError(String::from(name))),
+ Some(_) => Err(Errors::DuplicateRoom(String::from(name))),
None => {
self.rooms.insert(String::from(name), Room::new(String::from(name)));
Ok(())
}
}
}
pub fn get_room(&self, room_name: &str) -> Result<&Room, Errors> {
match self.rooms.get(room_name) {
Some(room_ref) => Ok(room_ref),
None => Err(Errors::UnknownRoom(String::from(room_name))),
}
}
pub fn set_link(
&mut self,
room_name: &str,
direction: Direction,
other_room_name: &str,
) -> Result<(), Errors> {
let _ = self.get_room(room_name)?;
let _ = self.get_room(other_room_name)?;
let room = self.get_room_mut(room_name)?;
room.links.insert(direction, String::from(other_room_name));
let other_room = self.get_room_mut(other_room_name)?;
other_room.links.insert(direction.reverse(), String::from(room_name));
Ok(())
}
pub fn get_next_room(&self, room_name: &str, direction: Direction) -> Result<Option<&Room>, Errors> {
let room = self.get_room(room_name)?;
let direction_room = room.links.get(&direction);
Ok(direction_room.map_or_else(|| None, |s| self.rooms.get(s)))
}
fn get_room_mut(&mut self, room_name: &str) -> Result<&mut Room, Errors> {
match self.rooms.get_mut(room_name) {
Some(room_ref) => Ok(room_ref),
None => Err(Errors::UnknownRoom(String::from(room_name))),
}
}
}
impl Dungeon {
pub fn from_reader<B: BufRead>(reader: B) -> Result<Self, Errors> {
let directions = HashMap::from([
("East", Direction::East),
("West", Direction::West),
("North", Direction::North),
("South", Direction::South)
]);
let mut dungeon = Self::new();
let mut lines = reader.lines()
.map(|l| l.map_err(Self::convert_to_errors));
let line = lines.next();
let _ = match line {
Some(s) => {
match s {
Err(e) => return Err(e),
Ok(s) => {
match Self::parse_rooms_header(&s) {
Ok(_) => Ok(()) as Result<(), Errors>,
Err(e) => return Err(e)
}
}
}
},
None => return Err(Errors::LineParseError{ line_number: 0 })
};
let mut line_number = 1;
while let Some(r) = lines.next() {
let line = r?;
if line.trim() == "" {
break;
}
let name = Self::parse_room(&line, line_number)?;
dungeon.add_room(&name)?;
line_number += 1;
};
let line = lines.next();
let _ = match line {
Some(s) => {
match s {
Err(e) => return Err(e),
Ok(s) => {
match Self::parse_links_header(&s, line_number) {
Ok(_) => Ok(()) as Result<(), Errors>,
Err(e) => return Err(e)
}
}
}
},
None => return Err(Errors::LineParseError{ line_number })
};
while let Some(r) = lines.next() {
let line = r?;
if line.trim() == "" {
break;
}
let link = Self::parse_link(&line, line_number, &directions)?;
dungeon.set_link(&link.room_name, link.direction, &link.other_room_name)?;
line_number += 1;
};
Ok(dungeon)
}
fn is_header(s: &str) -> bool {
&s[0..] == "## Rooms"
}
fn is_links(s: &str) -> bool {
&s[0..] == "## Links"
}
fn parse_rooms_header(s: &str) -> Result<(), Errors> {
if Self::is_header(s) {
return Ok(());
}
Err(Errors::LineParseError{ line_number: 0 })
}
fn parse_links_header(s: &str, line_number: usize) -> Result<(), Errors> {
if Self::is_links(s) {
return Ok(());
}
Err(Errors::LineParseError{ line_number })
}
fn convert_to_errors(err: std::io::Error) -> Errors {
Errors::IoError(std::io::Error::from(err.kind()))
}
fn parse_room(line: &str, line_number: usize) -> Result<String, Errors> {
if line.len() == 0 || &line[0..1] != "-" {
return Err(Errors::LineParseError{ line_number });
}
let trimmed = line[1..].trim();
if trimmed.len() == 0 {
return Err(Errors::LineParseError{ line_number });
}
Ok(String::from(trimmed))
}
fn parse_link(line: &str, line_number: usize, direction_map: &HashMap<&str, Direction>) -> Result<Link, Errors> {
if line.len() == 0 || &line[0..1] != "-" {
return Err(Errors::LineParseError{ line_number });
}
let trimmed = line[1..].trim();
if trimmed.len() == 0 {
return Err(Errors::LineParseError{ line_number });
}
let mut splitted = trimmed.split("->").map(|s| String::from(s.trim())).collect::<Vec<String>>();
if splitted.len() != 3 {
return Err(Errors::LineParseError{ line_number });
}
let direction = match direction_map.get(&splitted[1].as_str()) {
None => return Err(Errors::LineParseError{ line_number }),
Some(direction) => *direction
};
let room_name = take(&mut splitted[0]);
let other_room_name = take(&mut splitted[2]);
Ok(Link{ room_name, other_room_name, direction })
}
}
impl Dungeon {
pub fn find_path(
&self,
start_room_name: &str,
end_room_name: &str
) -> Result<Option<Vec<&Room>>, Errors> {
let mut parents: HashMap<String, &Room> = HashMap::new();
let mut stack = Vec::<&Room>::new();
let start_room = self.get_room(start_room_name)?;
stack.push(start_room);
parents.insert(String::from(&start_room.name), start_room);
self.all_paths_dfs(&mut stack, &mut parents);
let end_room = self.get_room(end_room_name)?;
let mut path = Vec::<&Room>::new();
let mut current = end_room;
while current as *const _ != start_room as *const _ {
current = match parents.get(&current.name) {
None => return Ok(None),
Some(room_ref_ref) => {
path.push(current);
*room_ref_ref
}
};
}
path.push(current);
Ok(Some(path))
}
fn all_paths_dfs<'a>(&'a self, stack: &mut Vec<&'a Room>, parents: &mut HashMap<String, &'a Room>) -> Result<(), Errors> {
while stack.len() > 0 {
let room = stack.pop().unwrap();
self.dfs_rec(stack, parents, room)?;
}
Ok(())
}
fn dfs_rec<'a>(&'a self, stack: &mut Vec<&'a Room>, parents: &mut HashMap<String, &'a Room>, room: &'a Room) -> Result<(), Errors> {
for (_, name_ref) in room.links.iter() {
if let None = parents.get(name_ref) {
let next_room = self.get_room(name_ref)?;
stack.push(next_room);
parents.insert(String::from(name_ref), room);
self.dfs_rec(stack, parents, next_room)?;
}
}
Ok(())
}
}