Решение на Dungeons and Compilers от Димитър Георгиев

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

Към профила на Димитър Георгиев

Резултати

  • 20 точки от тестове
  • 0 бонус точки
  • 20 точки общо
  • 15 успешни тест(а)
  • 0 неуспешни тест(а)

Код

use std::collections::HashMap;
#[derive(Debug)]
pub enum Errors {
DuplicateRoom(String),
UnknownRoom(String),
IoError(std::io::Error),
LineParseError { line_number: usize },
DirectionParseError(String),
}
#[derive(Clone, Copy)]
pub enum Direction {
North,
South,
East,
West,
}
pub struct Room {
pub name: String,
neighbours : [Option<String>; 4],
}
pub struct Dungeon {
dungeon : HashMap<String, Room>,
}
impl Dungeon {
pub fn new() -> Self {
let mut dungeon = HashMap::new();
Dungeon {dungeon}
}
pub fn add_room(&mut self, name: &str) -> Result<(), Errors> {
if self.dungeon.contains_key(name) {
Result::Err(Errors::DuplicateRoom(name.to_string()))
} else {
let neighbours_temp : [Option<String>; 4] = [None, None, None, None];
let room_temp : Room = Room { name : name.to_string(), neighbours : neighbours_temp};
self.dungeon.insert(name.to_string(), room_temp);
Ok(())
}
}
pub fn get_room(&self, room_name: &str) -> Result<&Room, Errors> {
match self.dungeon.get_key_value(room_name) {
None => Result::Err(Errors::UnknownRoom(room_name.to_string())),
Some((_, room)) => Ok(room),
}
}
pub fn set_link(
&mut self,
room_name: &str,
direction: Direction,
other_room_name: &str,
) -> Result<(), Errors> {
if let None = self.dungeon.get(other_room_name) {
return Result::Err(Errors::UnknownRoom(other_room_name.to_string()));
}
if let Some(room) = self.dungeon.get_mut(room_name) {
match direction {
Direction::North => {room.neighbours[0] = Some(other_room_name.to_string());},
Direction::South => {room.neighbours[1] = Some(other_room_name.to_string());},
Direction::East => {room.neighbours[2] = Some(other_room_name.to_string());},
Direction::West => {room.neighbours[3] = Some(other_room_name.to_string());},
}
} else {
return Result::Err(Errors::UnknownRoom(room_name.to_string()))
}
if let Some(other_room) = self.dungeon.get_mut(other_room_name) {
match direction {
Direction::North => {other_room.neighbours[1] = Some(room_name.to_string());},
Direction::South => {other_room.neighbours[0] = Some(room_name.to_string());},
Direction::East => {other_room.neighbours[3] = Some(room_name.to_string());},
Direction::West => {other_room.neighbours[2] = Some(room_name.to_string());},
}
} else {
return Result::Err(Errors::UnknownRoom(room_name.to_string()))
}
Ok(())
}
pub fn get_next_room(&self, room_name: &str, direction: Direction) -> Result<Option<&Room>, Errors> {
match self.dungeon.get(room_name) {
None => Result::Err(Errors::UnknownRoom(room_name.to_string())),
Some(room) => {
match direction {
Direction::North => {if let Some(other_room) = room.neighbours[0].clone(){
return Ok(self.dungeon.get(&other_room));
} else {
return Ok(None);
}},
Direction::South => {if let Some(other_room) = room.neighbours[1].clone(){
return Ok(self.dungeon.get(&other_room));
} else {
return Ok(None);
}},
Direction::East => {if let Some(other_room) = room.neighbours[2].clone(){
return Ok(self.dungeon.get(&other_room));
} else {
return Ok(None);
}},
Direction::West => {if let Some(other_room) = room.neighbours[3].clone(){
return Ok(self.dungeon.get(&other_room));
} else {
return Ok(None);
}},
}
},
}
}
}
use std::fmt::Error;
use std::io::BufRead;
use std::ptr::read;
impl Dungeon {
pub fn from_reader<B: BufRead>(reader: B) -> Result<Self, Errors> {
let mut links_line_found : bool = false;
let mut new_line_reached : bool = false;
let mut is_empty_buffer : bool = true;
let mut hashmap = HashMap::new();
let mut dungeon = Dungeon {dungeon : hashmap};
for (i, line) in reader.lines().enumerate() {
is_empty_buffer = false;
if let Err(e) = line {
return Result::Err(Errors::IoError(e))
} else {
let line = line.unwrap();
if i == 0 {
if line == "## Rooms".to_string() {
continue;
} else {
return Result::Err(Errors::LineParseError{line_number : i+1});
}
}
if line.is_empty() && !new_line_reached {
new_line_reached = true;
continue;
}
if line.is_empty() && new_line_reached {
return Result::Err(Errors::LineParseError{line_number : i+1});
}
if !line.is_empty() && !new_line_reached {
if let Some(room) = line.strip_prefix("- ") {
dungeon.add_room(room)?;
} else {
return Result::Err(Errors::LineParseError{line_number : i+1})
}
}
if !line.is_empty() && new_line_reached && !links_line_found {
if line == "## Links".to_string() {
links_line_found = true;
continue;
} else {
return Result::Err(Errors::LineParseError{line_number : i+1})
}
}
if !line.is_empty() && new_line_reached && links_line_found {
if let Some(text_to_split) = line.strip_prefix("- ") {
let v : Vec<&str> = text_to_split.split(" -> ").collect();
if v.len() == 3 {
if v[1] != "North" && v[1] != "South" && v[1] != "East" && v[1] != "West" {
return Result::Err(Errors::DirectionParseError(v[1].to_string()));
} else {
let mut d = Direction::North;
if v[1] == "North" {d = Direction::North;}
if v[1] == "South" {d = Direction::South;}
if v[1] == "East" {d = Direction::East;}
if v[1] == "West" {d = Direction::West;}
dungeon.set_link(v[0], d, v[2])?;
}
} else {
return Result::Err(Errors::LineParseError{line_number : i+1})
}
} else {
return Result::Err(Errors::LineParseError{line_number : i+1})
}
}
}
}
if is_empty_buffer {
return Result::Err(Errors::LineParseError{line_number : 0})
}
Ok(dungeon)
}
}
use std::collections::VecDeque;
use std::collections::HashSet;
impl Dungeon {
pub fn find_path(
&self,
start_room_name: &str,
end_room_name: &str
) -> Result<Option<Vec<&Room>>, Errors> {
let mut queue: VecDeque<&Room> = VecDeque::new();
let mut visited: HashSet<String> = HashSet::new();
let mut parent : HashMap<String, String> = HashMap::new();
let start_room = self.get_room(start_room_name)?;
let end_room = self.get_room(end_room_name)?; // дали енд стаята съществува/хардкоуднато
queue.push_front(start_room);
visited.insert(start_room.name.clone());
while !queue.is_empty() {
let current_room = queue.pop_front().unwrap();
if current_room.name == end_room_name {
break;
}
if let Some(neighbour_room) = self.get_next_room(current_room.name.as_str(), Direction::North)? {
if visited.insert(neighbour_room.name.clone()) {
queue.push_back(neighbour_room);
parent.insert(neighbour_room.name.clone(), current_room.name.clone());
}
}
if let Some(neighbour_room) = self.get_next_room(current_room.name.as_str(), Direction::South)? {
if visited.insert(neighbour_room.name.clone()) {
queue.push_back(neighbour_room);
parent.insert(neighbour_room.name.clone(), current_room.name.clone());
}
}
if let Some(neighbour_room) = self.get_next_room(current_room.name.as_str(), Direction::East)? {
if visited.insert(neighbour_room.name.clone()) {
queue.push_back(neighbour_room);
parent.insert(neighbour_room.name.clone(), current_room.name.clone());
}
}
if let Some(neighbour_room) = self.get_next_room(current_room.name.as_str(), Direction::West)? {
if visited.insert(neighbour_room.name.clone()) {
queue.push_back(neighbour_room);
parent.insert(neighbour_room.name.clone(), current_room.name.clone());
}
}
}
let mut result = vec![];
if !visited.contains(end_room_name) {
return Ok(None);
} else {
let mut end_room = self.get_room(end_room_name)?;
result.push(end_room);
while let Some(end_room_parent_name) = parent.get(end_room.name.as_str()) {
let end_room_parent = self.get_room(end_room_parent_name)?;
result.push(end_room_parent);
end_room = end_room_parent;
}
}
result.reverse();
Ok(Some(result))
}
}

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

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

warning: unused import: `std::ptr::read`
   --> src/lib.rs:119:5
    |
119 | use std::ptr::read;
    |     ^^^^^^^^^^^^^^

warning: unused variable: `end_room`
   --> src/lib.rs:211:13
    |
211 |         let end_room = self.get_room(end_room_name)?; // дали енд стаята съществува/хардкоуднато
    |             ^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_end_room`
    |
    = note: `#[warn(unused_variables)]` on by default

warning: variable does not need to be mutable
  --> src/lib.rs:32:13
   |
32 |         let mut dungeon = HashMap::new();
   |             ----^^^^^^^
   |             |
   |             help: remove this `mut`
   |
   = note: `#[warn(unused_mut)]` on by default

warning: variable does not need to be mutable
   --> src/lib.rs:126:13
    |
126 |         let mut hashmap = HashMap::new();
    |             ----^^^^^^^
    |             |
    |             help: remove this `mut`

warning: `solution` (lib) generated 5 warnings
    Finished test [unoptimized + debuginfo] target(s) in 3.80s
     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 ... ok
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

test result: ok. 15 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

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

Димитър качи първо решение на 10.01.2022 21:13 (преди над 3 години)