Решение на Dungeons and Compilers от Васил Любенов

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

Към профила на Васил Любенов

Резултати

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

Код

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,
}
#[derive(Debug, Clone)]
pub struct Neighbours{
pub west: String,
pub east: String,
pub south: String,
pub north: String,
}
impl Neighbours {
pub fn clone(&self) -> Neighbours{
Neighbours{
west: self.west.clone(),
east: self.east.clone(),
north: self.north.clone(),
south: self.south.clone(),
}
}
pub fn new() -> Self {
Neighbours{
north: String::from(""),
west: String::from(""),
east: String::from(""),
south: String::from("")
}
}
//setters
pub fn set_west(&mut self, room_name: String) {
self.west = room_name;
}
pub fn set_east(&mut self, room_name: String) {
self.east = room_name;
}
pub fn set_north(&mut self, room_name: String) {
self.north = room_name;
}
pub fn set_south(&mut self, room_name: String) {
self.south = room_name;
}
//getters
pub fn get_west(self) -> Option<String> {
if self.west == ""{
return None;
}
Some(self.west.clone())
}
pub fn get_east(self) -> Option<String>{
if self.east == ""{
return None;
}
Some(self.east.clone())
}
pub fn get_north(self) -> Option<String>{
if self.north == ""{
return None;
}
Some(self.north.clone())
}
pub fn get_south(self) -> Option<String>{
if self.south == ""{
return None;
}
Some(self.south.clone())
}
}
#[derive(Debug, Clone)]
pub struct Room {
pub name: String,
pub next_to: Neighbours
}
impl Room {
pub fn new(name: String) -> Self {
Room{
name,
next_to: Neighbours::new()
}
}
}
pub struct Dungeon {
rooms: HashMap<String, Room>
}
impl Dungeon {
pub fn new() -> Self {
Dungeon{
rooms: HashMap::<String, Room>::new()
}
}
pub fn add_room(&mut self, name: &str) -> Result<(), Errors> {
if self.rooms.contains_key(&name.to_string()) {
Err(Errors::DuplicateRoom(name.to_string()))
}
else{
self.rooms.insert(name.to_string(), Room::new(name.to_string()));
Ok(())
}
}
pub fn get_room(&self, room_name: &str) -> Result<&Room, Errors> {
if self.rooms.contains_key(&room_name.to_string()) {
Ok(self.rooms.get(&room_name.to_string()).unwrap())
}
else{
Err(Errors::UnknownRoom(room_name.to_string()))
}
}
pub fn set_link(
&mut self,
room_name: &str,
direction: Direction,
other_room_name: &str,
) -> Result<(), Errors> {
if self.rooms.contains_key(&room_name.to_string()){
if self.rooms.contains_key(&other_room_name.to_string()){
let mut current = self.rooms.get_mut(&room_name.to_string()).unwrap();
//let mut second = self.rooms.get_mut(&other_room_name.to_string()).unwrap();;
match direction{
Direction::East => {
current.next_to.set_east(other_room_name.to_string());
//change the current room to the second one
current = self.rooms.get_mut(&other_room_name.to_string()).unwrap();
current.next_to.set_west(room_name.to_string());
}
Direction::West => {
current.next_to.set_west(other_room_name.to_string());
//change the current room to the second one
current = self.rooms.get_mut(&other_room_name.to_string()).unwrap();
current.next_to.set_east(room_name.to_string());
}
Direction::South => {
current.next_to.set_south(other_room_name.to_string());
//change the current room to the second one
current = self.rooms.get_mut(&other_room_name.to_string()).unwrap();
current.next_to.set_north(room_name.to_string());
}
Direction::North => {
current.next_to.set_north(other_room_name.to_string());
//change the current room to the second one
current = self.rooms.get_mut(&other_room_name.to_string()).unwrap();
current.next_to.set_south(other_room_name.to_string());
}
}
}
else{
return Err(Errors::UnknownRoom(other_room_name.to_string()));
}
}
else{
return Err(Errors::UnknownRoom(room_name.to_string()));
}
Ok(())
}
pub fn get_next_room(&self, room_name: &str, direction: Direction) -> Result<Option<&Room>, Errors> {
if self.rooms.contains_key(&room_name.to_string()) {
let get_room = self.rooms.get(&room_name.to_string());
match direction{
Direction::North => {
if get_room.unwrap().next_to.clone().get_north() == None {
Ok(None)
}
else{
let neighbour_room_name = get_room.unwrap().next_to.clone().get_north().unwrap();
Ok(Some(self.rooms.get(&neighbour_room_name.to_string())).unwrap())
}
}
Direction::East => {
if get_room.unwrap().next_to.clone().get_east() == None {
Ok(None)
}
else{
let neighbour_room_name = get_room.unwrap().next_to.clone().get_east().unwrap();
Ok(Some(self.rooms.get(&neighbour_room_name.to_string())).unwrap())
}
}
Direction::West => {
if get_room.unwrap().next_to.clone().get_west() == None {
Ok(None)
}
else{
let neighbour_room_name = get_room.unwrap().next_to.clone().get_west().unwrap();
Ok(Some(self.rooms.get(&neighbour_room_name.to_string())).unwrap())
}
}
Direction::South => {
if get_room.unwrap().next_to.clone().get_south() == None {
Ok(None)
}
else{
let neighbour_room_name = get_room.unwrap().next_to.clone().get_south().unwrap();
Ok(Some(self.rooms.get(&neighbour_room_name.to_string())).unwrap())
}
}
}
}
else{
Err(Errors::UnknownRoom(room_name.to_string()))
}
}
}
use std::io::BufRead;
//helper functions
fn str_to_direction(dir: &str) -> Direction{
match dir {
"North" => Direction::North,
"East" => Direction::East,
"South" => Direction::South,
"West" => Direction::West,
_ => unreachable!()
}
}
impl Dungeon {
pub fn from_reader<B: BufRead>(reader: B) -> Result<Self, Errors> {
//if we have an empty reader
let mut new_dungeon = Dungeon::new();
let mut row_counter = 0;
//we have a vector of all the lines and we have checked it for any io::errors
let lines_vec = reader.lines()
.map(|l| {
if let Err(val) = l{
return Err(Errors::IoError(val));
}
return Ok(l.unwrap());
})
.map(|l| l.unwrap()).collect::<Vec<String>>();
//cheking for empty buffer
if lines_vec.len() == 0 {
return Err(Errors::LineParseError{ line_number: row_counter });
}
row_counter += 1;
let mut reached_nl = false;
let mut row_reached = 0;
for mut line in lines_vec {
line = line.trim().to_string();
if !reached_nl{
if row_counter == 1{
if line != "## Rooms"{
return Err(Errors::LineParseError{ line_number: row_counter });
}
else {
row_counter += 1;
continue;
}
}
else {
if line.as_bytes().len() == 0 {
//we have reached a newline
reached_nl = true;
row_reached = row_counter;
}
else{
//geting and the first letter and checking for the correct format wihich is /- <name>/
if line.as_bytes()[0] as char != '-'{
return Err(Errors::LineParseError{ line_number: row_counter });
}
else {
new_dungeon.add_room(&line[2..])?;
}
row_counter += 1;
}
}
}
else{
if row_counter == row_reached {
if line != "## Links"{
return Err(Errors::LineParseError{ line_number: row_counter });
}
else{
row_counter += 1;
continue;
}
}
//geting and the first letter and checking for the correct format wihich is /- <name>/
else if line.as_bytes()[0] as char != '-' {
return Err(Errors::LineParseError{ line_number: row_counter });
}
else {
row_counter += 1;
let words: Vec<&str> = line[2..].split(" -> ").collect();
if words.len() != 3 {
return Err(Errors::LineParseError{ line_number: row_counter });
}
if words[1].trim() != "West" && words[1].trim() != "East" && words[1].trim() != "North" && words[1].trim() != "South" {
return Err(Errors::DirectionParseError(words[1].to_string()));
}
let dir = str_to_direction(words[1]);
new_dungeon.set_link(words[0], dir, words[2])?;
}
}
}
return Ok(new_dungeon);
}
}
fn is_not_visited(target: String, path: Vec<String>) -> bool{
for x in path{
if x == target{
return false;
}
}
return true;
}
fn findpaths<'a>(g: &'a HashMap<String, Vec<String>>, src: &'a str, dst: &'a str) -> Vec<String>{
let mut q = Vec::<Vec::<String>>::new();
// path vector to store the current path
let mut path = Vec::<String>::new();
path.push(src.to_string());
q.insert(0, path);
while !q.is_empty() {
path = q[q.len() - 1].clone();
q.pop();
let last: String = path[path.len() - 1].clone();
//we have reached the end
if last == dst{
return path;
}
for name in g[&last].clone() {
if is_not_visited(name.to_string(), path.clone()) {
let mut newpath = Vec::<String>::new();
for road in path.clone(){
newpath.push(road);
}
newpath.push(name.to_string());
q.push(newpath);
}
}
}
return vec![];
}
//finding path algorythm part
impl Dungeon {
pub fn find_path(
&self,
start_room_name: &str,
end_room_name: &str
) -> Result<Option<Vec<&Room>>, Errors> {
let mut graph = HashMap::<String, Vec<String>>::new();
for n in &self.rooms.clone(){
let mut current_neigbours = Vec::<String>::new();
//n.1 - is a Room
if n.1.next_to.clone().get_west() != None{
current_neigbours.push(n.1.next_to.clone().get_west().unwrap());
}
if n.1.next_to.clone().get_east() != None{
current_neigbours.push(n.1.next_to.clone().get_east().unwrap());
}
if n.1.next_to.clone().get_north() != None{
current_neigbours.push(n.1.next_to.clone().get_north().unwrap());
}
if n.1.next_to.clone().get_south() != None{
current_neigbours.push(n.1.next_to.clone().get_south().unwrap());
}
graph.insert(n.0.to_string(), current_neigbours);
}
let path_in_str_vec = findpaths(&graph, start_room_name, end_room_name);
let mut result = Vec::<&Room>::new();
for room in path_in_str_vec{
result.push(self.rooms.get(&room).unwrap());
}
//in case we have nothing in the path
if result.len() == 0 {
return Ok(None);
}
Ok(Some(result))
}
}

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

Compiling solution v0.1.0 (/tmp/d20220116-3533338-cll2ne/solution)
    Finished test [unoptimized + debuginfo] target(s) in 3.83s
     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 ... FAILED
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 ... 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 ... 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_cyrillic_room_names stdout ----
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `"Хол"`,
 right: `"Антре"`', tests/solution_test.rs:180:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

---- solution_test::test_finding_no_path stdout ----
thread '<unnamed>' panicked at 'assertion failed: path.is_err()', tests/solution_test.rs:382: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 'assertion failed: matches!(Dungeon :: from_reader(TEST_INPUT_5.trim().as_bytes()),\n         Err(Errors :: LineParseError { line_number : 3 }))', tests/solution_test.rs:278:5

---- solution_test::test_io_error stdout ----
thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: IoError(Custom { kind: Other, error: "fill_buf error!" })', /tmp/d20220116-3533338-cll2ne/solution/src/lib.rs:261:47
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: IoError(Custom { kind: Other, error: "fill_buf error!" })', tests/solution_test.rs:194:5


failures:
    solution_test::test_cyrillic_room_names
    solution_test::test_finding_no_path
    solution_test::test_invalid_parsing
    solution_test::test_io_error

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

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

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

Васил качи първо решение на 10.01.2022 18:07 (преди над 3 години)

Васил качи решение на 11.01.2022 13:55 (преди над 3 години)

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,
}
#[derive(Debug, Clone)]
pub struct Neighbours{
pub west: String,
pub east: String,
pub south: String,
pub north: String,
}
impl Neighbours {
pub fn clone(&self) -> Neighbours{
Neighbours{
west: self.west.clone(),
east: self.east.clone(),
north: self.north.clone(),
south: self.south.clone(),
}
}
pub fn new() -> Self {
Neighbours{
north: String::from(""),
west: String::from(""),
east: String::from(""),
south: String::from("")
}
}
//setters
pub fn set_west(&mut self, room_name: String) {
self.west = room_name;
}
pub fn set_east(&mut self, room_name: String) {
self.east = room_name;
}
pub fn set_north(&mut self, room_name: String) {
self.north = room_name;
}
pub fn set_south(&mut self, room_name: String) {
self.south = room_name;
}
//getters
pub fn get_west(self) -> Option<String> {
if self.west == ""{
return None;
}
Some(self.west.clone())
}
pub fn get_east(self) -> Option<String>{
if self.east == ""{
return None;
}
Some(self.east.clone())
}
pub fn get_north(self) -> Option<String>{
if self.north == ""{
return None;
}
Some(self.north.clone())
}
pub fn get_south(self) -> Option<String>{
if self.south == ""{
return None;
}
Some(self.south.clone())
}
}
#[derive(Debug, Clone)]
pub struct Room {
pub name: String,
pub next_to: Neighbours
}
impl Room {
pub fn new(name: String) -> Self {
Room{
name,
next_to: Neighbours::new()
}
}
}
pub struct Dungeon {
rooms: HashMap<String, Room>
}
impl Dungeon {
pub fn new() -> Self {
Dungeon{
rooms: HashMap::<String, Room>::new()
}
}
pub fn add_room(&mut self, name: &str) -> Result<(), Errors> {
if self.rooms.contains_key(&name.to_string()) {
Err(Errors::DuplicateRoom(name.to_string()))
}
else{
self.rooms.insert(name.to_string(), Room::new(name.to_string()));
Ok(())
}
}
pub fn get_room(&self, room_name: &str) -> Result<&Room, Errors> {
if self.rooms.contains_key(&room_name.to_string()) {
Ok(self.rooms.get(&room_name.to_string()).unwrap())
}
else{
Err(Errors::UnknownRoom(room_name.to_string()))
}
}
pub fn set_link(
&mut self,
room_name: &str,
direction: Direction,
other_room_name: &str,
) -> Result<(), Errors> {
if self.rooms.contains_key(&room_name.to_string()){
if self.rooms.contains_key(&other_room_name.to_string()){
let mut current = self.rooms.get_mut(&room_name.to_string()).unwrap();
//let mut second = self.rooms.get_mut(&other_room_name.to_string()).unwrap();;
match direction{
Direction::East => {
current.next_to.set_east(other_room_name.to_string());
//change the current room to the second one
current = self.rooms.get_mut(&other_room_name.to_string()).unwrap();
current.next_to.set_west(room_name.to_string());
}
Direction::West => {
current.next_to.set_west(other_room_name.to_string());
//change the current room to the second one
current = self.rooms.get_mut(&other_room_name.to_string()).unwrap();
current.next_to.set_east(room_name.to_string());
}
Direction::South => {
current.next_to.set_south(other_room_name.to_string());
//change the current room to the second one
current = self.rooms.get_mut(&other_room_name.to_string()).unwrap();
current.next_to.set_north(room_name.to_string());
}
Direction::North => {
current.next_to.set_north(other_room_name.to_string());
//change the current room to the second one
current = self.rooms.get_mut(&other_room_name.to_string()).unwrap();
current.next_to.set_south(other_room_name.to_string());
}
}
}
else{
return Err(Errors::UnknownRoom(other_room_name.to_string()));
}
}
else{
return Err(Errors::UnknownRoom(room_name.to_string()));
}
Ok(())
}
pub fn get_next_room(&self, room_name: &str, direction: Direction) -> Result<Option<&Room>, Errors> {
if self.rooms.contains_key(&room_name.to_string()) {
let get_room = self.rooms.get(&room_name.to_string());
match direction{
Direction::North => {
if get_room.unwrap().next_to.clone().get_north() == None {
Ok(None)
}
else{
let neighbour_room_name = get_room.unwrap().next_to.clone().get_north().unwrap();
Ok(Some(self.rooms.get(&neighbour_room_name.to_string())).unwrap())
}
}
Direction::East => {
if get_room.unwrap().next_to.clone().get_east() == None {
Ok(None)
}
else{
let neighbour_room_name = get_room.unwrap().next_to.clone().get_east().unwrap();
Ok(Some(self.rooms.get(&neighbour_room_name.to_string())).unwrap())
}
}
Direction::West => {
if get_room.unwrap().next_to.clone().get_west() == None {
Ok(None)
}
else{
let neighbour_room_name = get_room.unwrap().next_to.clone().get_west().unwrap();
Ok(Some(self.rooms.get(&neighbour_room_name.to_string())).unwrap())
}
}
Direction::South => {
if get_room.unwrap().next_to.clone().get_south() == None {
Ok(None)
}
else{
let neighbour_room_name = get_room.unwrap().next_to.clone().get_south().unwrap();
Ok(Some(self.rooms.get(&neighbour_room_name.to_string())).unwrap())
}
}
}
}
else{
Err(Errors::UnknownRoom(room_name.to_string()))
}
}
}
use std::io::BufRead;
//helper functions
fn str_to_direction(dir: &str) -> Direction{
match dir {
"North" => Direction::North,
"East" => Direction::East,
"South" => Direction::South,
"West" => Direction::West,
_ => unreachable!()
}
}
impl Dungeon {
pub fn from_reader<B: BufRead>(reader: B) -> Result<Self, Errors> {
//if we have an empty reader
let mut new_dungeon = Dungeon::new();
let mut row_counter = 0;
//we have a vector of all the lines and we have checked it for any io::errors
let lines_vec = reader.lines()
.map(|l| {
if let Err(val) = l{
return Err(Errors::IoError(val));
}
return Ok(l.unwrap());
})
.map(|l| l.unwrap()).collect::<Vec<String>>();
//cheking for empty buffer
if lines_vec.len() == 0 {
return Err(Errors::LineParseError{ line_number: row_counter });
}
row_counter += 1;
let mut reached_nl = false;
let mut row_reached = 0;
for mut line in lines_vec {
line = line.trim().to_string();
if !reached_nl{
if row_counter == 1{
if line != "## Rooms"{
return Err(Errors::LineParseError{ line_number: row_counter });
}
else {
row_counter += 1;
continue;
}
}
else {
if line.as_bytes().len() == 0 {
//we have reached a newline
reached_nl = true;
row_reached = row_counter;
}
else{
//geting and the first letter and checking for the correct format wihich is /- <name>/
if line.as_bytes()[0] as char != '-'{
return Err(Errors::LineParseError{ line_number: row_counter });
}
else {
new_dungeon.add_room(&line[2..])?;
}
row_counter += 1;
}
}
}
else{
if row_counter == row_reached {
if line != "## Links"{
return Err(Errors::LineParseError{ line_number: row_counter });
}
else{
row_counter += 1;
continue;
}
}
//geting and the first letter and checking for the correct format wihich is /- <name>/
else if line.as_bytes()[0] as char != '-' {
return Err(Errors::LineParseError{ line_number: row_counter });
}
else {
row_counter += 1;
let words: Vec<&str> = line[2..].split(" -> ").collect();
if words.len() != 3 {
return Err(Errors::LineParseError{ line_number: row_counter });
}
if words[1].trim() != "West" && words[1].trim() != "East" && words[1].trim() != "North" && words[1].trim() != "South" {
return Err(Errors::DirectionParseError(words[1].to_string()));
}
let dir = str_to_direction(words[1]);
new_dungeon.set_link(words[0], dir, words[2])?;
}
}
}
return Ok(new_dungeon);
}
}
fn is_not_visited(target: String, path: Vec<String>) -> bool{
for x in path{
if x == target{
return false;
}
}
return true;
}
fn findpaths<'a>(g: &'a HashMap<String, Vec<String>>, src: &'a str, dst: &'a str) -> Vec<String>{
- let mut q = Vec::<Vec::<String>>::new() ;
+ let mut q = Vec::<Vec::<String>>::new();
// path vector to store the current path
let mut path = Vec::<String>::new();
path.push(src.to_string());
q.insert(0, path);
while !q.is_empty() {
path = q[q.len() - 1].clone();
q.pop();
let last: String = path[path.len() - 1].clone();
//we have reached the end
if last == dst{
return path;
}
- // traverse to all the nodes connected to
- // current vertex and push new path to queue
for name in g[&last].clone() {
if is_not_visited(name.to_string(), path.clone()) {
let mut newpath = Vec::<String>::new();
for road in path.clone(){
newpath.push(road);
}
newpath.push(name.to_string());
q.push(newpath);
}
}
}
return vec![];
}
//finding path algorythm part
impl Dungeon {
pub fn find_path(
&self,
start_room_name: &str,
end_room_name: &str
) -> Result<Option<Vec<&Room>>, Errors> {
let mut graph = HashMap::<String, Vec<String>>::new();
for n in &self.rooms.clone(){
let mut current_neigbours = Vec::<String>::new();
//n.1 - is a Room
if n.1.next_to.clone().get_west() != None{
current_neigbours.push(n.1.next_to.clone().get_west().unwrap());
}
if n.1.next_to.clone().get_east() != None{
current_neigbours.push(n.1.next_to.clone().get_east().unwrap());
}
if n.1.next_to.clone().get_north() != None{
current_neigbours.push(n.1.next_to.clone().get_north().unwrap());
}
if n.1.next_to.clone().get_south() != None{
current_neigbours.push(n.1.next_to.clone().get_south().unwrap());
}
graph.insert(n.0.to_string(), current_neigbours);
}
let path_in_str_vec = findpaths(&graph, start_room_name, end_room_name);
let mut result = Vec::<&Room>::new();
for room in path_in_str_vec{
result.push(self.rooms.get(&room).unwrap());
}
- if result.len() < 2 {
+
+ //in case we have nothing in the path
+ if result.len() == 0 {
return Ok(None);
}
Ok(Some(result))
}
}