Решение на Dungeons and Compilers от Николай Натов
Резултати
- 19 точки от тестове
- 0 бонус точки
- 19 точки общо
- 14 успешни тест(а)
- 1 неуспешни тест(а)
Код
use std::{io::BufRead, collections::{HashMap, VecDeque}};
#[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 = 0,
South = 1,
East = 2,
West = 3,
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum ParsingStage
{
None = 0, Rooms = 1, Links = 2, Transition = 3
}
impl Direction
{
pub fn GetOppositeDirection(direction: Direction) -> Direction
{
match direction
{
Direction::North => return Direction::South,
Direction::South => return Direction::North,
Direction::East => return Direction::West,
Direction::West => return Direction::East
}
}
pub fn FromString(str: &str) -> Option<Direction>
{
match str
{
"North" => return Some(Direction::North),
"South" => return Some(Direction::South),
"East" => return Some(Direction::East),
"West" => return Some(Direction::West),
_ => return None
}
}
}
pub struct Room
{
pub name: String,
pub Neighbours: [Option<String>; 4]
}
pub struct Dungeon
{
m_Rooms: HashMap<String, Room>
}
impl Dungeon
{
pub fn new() -> Self
{
return Self {
m_Rooms: HashMap::new()
};
}
pub fn add_room(&mut self, name: &str) -> Result<(), Errors>
{
if !self.m_Rooms.contains_key(name)
{
let name: String = String::from(name);
let room: Room = Room
{
name: name.clone(),
Neighbours: [None, None, None, None]
};
self.m_Rooms.insert(name, room);
return Ok(());
}
else
{
return Err(Errors::DuplicateRoom(String::from(name)));
}
}
pub fn get_room(&self, name: &str) -> Result<&Room, Errors>
{
if self.m_Rooms.contains_key(name)
{
return Ok(self.m_Rooms.get(name).unwrap());
}
else
{
return Err(Errors::UnknownRoom(String::from(name)));
}
}
pub fn set_link(&mut self, name: &str, direction: Direction, neighbourName: &str) -> Result<(), Errors>
{
if self.m_Rooms.contains_key(name)
{
if self.m_Rooms.contains_key(neighbourName)
{
let oppositeDirection: Direction = Direction::GetOppositeDirection(direction);
self.m_Rooms.get_mut(name).unwrap().Neighbours[direction as usize] = Some(String::from(neighbourName));
self.m_Rooms.get_mut(neighbourName).unwrap().Neighbours[oppositeDirection as usize] = Some(String::from(name));
return Ok(());
}
else
{
return Err(Errors::UnknownRoom(String::from(neighbourName)));
}
}
else
{
return Err(Errors::UnknownRoom(String::from(name)));
}
}
pub fn get_next_room(&self, name: &str, direction: Direction) -> Result<Option<&Room>, Errors>
{
if self.m_Rooms.contains_key(name)
{
let room: &Room = self.m_Rooms.get(name).unwrap();
if room.Neighbours[direction as usize].is_some()
{
let neighbourName: &String = room.Neighbours[direction as usize].as_ref().unwrap();
let room: &Room = self.m_Rooms.get(neighbourName).unwrap();
return Ok(Some(room));
}
else
{
return Ok(None);
}
}
else
{
return Err(Errors::UnknownRoom(String::from(name)));
}
}
pub fn from_reader<B: BufRead>(reader: B) -> Result<Self, Errors>
{
let mut parsingStage: ParsingStage = ParsingStage::None;
let mut lineIndex: usize = 1;
let mut dungeon: Dungeon = Dungeon::new();
for line in reader.lines()
{
if line.is_ok()
{
let mut line: String = line.unwrap();
if line == "## Rooms"
{
if parsingStage != ParsingStage::None
{
return Err(Errors::LineParseError{ line_number: lineIndex });
}
parsingStage = ParsingStage::Rooms;
}
else if line == "## Links"
{
if parsingStage != ParsingStage::Transition
{
return Err(Errors::LineParseError{ line_number: lineIndex });
}
parsingStage = ParsingStage::Links;
}
else if line.is_empty()
{
if parsingStage != ParsingStage::Rooms
{
return Err(Errors::LineParseError{ line_number: lineIndex });
}
parsingStage = ParsingStage::Transition;
}
else if &line[0..2] == "- "
{
line.replace_range(0..2, "");
let tokens: Vec<&str> = line.split(" -> ").collect();
match parsingStage
{
ParsingStage::Rooms =>
{
if tokens.len() != 1
{
return Err(Errors::LineParseError{ line_number: lineIndex });
}
let roomName: &str = tokens[0].trim();
if let Err(error) = dungeon.add_room(roomName)
{
return Err(error);
}
},
ParsingStage::Links =>
{
if tokens.len() != 3
{
return Err(Errors::LineParseError{ line_number: lineIndex });
}
let roomName: &str = tokens[0].trim();
let direction: Option<Direction> = Direction::FromString(tokens[1].trim());
let neighbourName: &str = tokens[2].trim();
if direction.is_none()
{
return Err(Errors::DirectionParseError(String::from(tokens[1])));
}
if let Err(error) = dungeon.set_link(roomName, direction.unwrap(), neighbourName)
{
return Err(error);
}
}
_ =>
{
return Err(Errors::LineParseError{ line_number: lineIndex });
}
}
}
else
{
return Err(Errors::LineParseError{ line_number: lineIndex });
}
lineIndex += 1;
}
else
{
return Err(Errors::IoError(line.err().unwrap()));
}
}
return Ok(dungeon);
}
pub fn find_path(&self, startRoomName: &str, endRoomName: &str) -> Result<Option<Vec<&Room>>, Errors>
{
if let Err(error) = self.get_room(startRoomName)
{
return Err(error);
}
if let Err(error) = self.get_room(endRoomName)
{
return Err(error);
}
if startRoomName == endRoomName
{
return Ok(Some(vec![self.get_room(startRoomName).unwrap()]));
}
let mut parents: HashMap<String, Option<String>> = HashMap::new();
for entry in self.m_Rooms.iter()
{
parents.insert(entry.0.clone(), None);
}
let mut visited: HashMap<String, bool> = HashMap::new();
for entry in self.m_Rooms.iter()
{
visited.insert(entry.0.clone(), false);
}
let mut queue: VecDeque<&Room> = VecDeque::new();
let startRoom: &Room = self.get_room(startRoomName).unwrap();
queue.push_back(startRoom);
*visited.get_mut(&startRoom.name).unwrap() = true;
while !queue.is_empty()
{
let currentRoom: &Room = queue.remove(0).unwrap();
if currentRoom.name == endRoomName
{
break;
}
for neighbour in currentRoom.Neighbours.iter()
{
if neighbour.is_some()
{
let neighbour: &String = neighbour.as_ref().unwrap();
if visited[neighbour] == false
{
*parents.get_mut(neighbour).unwrap() = Some(currentRoom.name.clone());
*visited.get_mut(neighbour).unwrap() = true;
let neighbourRoom = self.get_room(neighbour);
if neighbourRoom.is_err()
{
return Err(neighbourRoom.err().unwrap());
}
queue.push_back(neighbourRoom.unwrap());
}
}
}
}
if parents[endRoomName] == None
{
return Ok(None);
}
let mut path: Vec<&Room> = Vec::new();
path.push(self.get_room(endRoomName).unwrap());
let mut currentRoomName = parents.get(endRoomName).unwrap();
while currentRoomName.is_some()
{
let currentRoomNameStr = currentRoomName.as_ref().unwrap();
let currentRoom = self.get_room(currentRoomNameStr.as_str());
if currentRoom.is_err()
{
return Err(currentRoom.err().unwrap());
}
path.push(currentRoom.unwrap());
currentRoomName = parents.get(currentRoomNameStr).unwrap();
}
path.reverse();
return Ok(Some(path));
}
}
Лог от изпълнението
Compiling solution v0.1.0 (/tmp/d20220116-3533338-1m2k5sk/solution) warning: method `GetOppositeDirection` should have a snake case name --> src/lib.rs:30:12 | 30 | pub fn GetOppositeDirection(direction: Direction) -> Direction | ^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `get_opposite_direction` | = note: `#[warn(non_snake_case)]` on by default warning: method `FromString` should have a snake case name --> src/lib.rs:41:12 | 41 | pub fn FromString(str: &str) -> Option<Direction> | ^^^^^^^^^^ help: convert the identifier to snake case: `from_string` warning: structure field `Neighbours` should have a snake case name --> src/lib.rs:57:9 | 57 | pub Neighbours: [Option<String>; 4] | ^^^^^^^^^^ help: convert the identifier to snake case: `neighbours` warning: structure field `m_Rooms` should have a snake case name --> src/lib.rs:62:5 | 62 | m_Rooms: HashMap<String, Room> | ^^^^^^^ help: convert the identifier to snake case: `m_rooms` warning: variable `neighbourName` should have a snake case name --> src/lib.rs:108:66 | 108 | pub fn set_link(&mut self, name: &str, direction: Direction, neighbourName: &str) -> Result<(), Errors> | ^^^^^^^^^^^^^ help: convert the identifier to snake case: `neighbour_name` warning: variable `oppositeDirection` should have a snake case name --> src/lib.rs:114:21 | 114 | let oppositeDirection: Direction = Direction::GetOppositeDirection(direction); | ^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `opposite_direction` warning: variable `neighbourName` should have a snake case name --> src/lib.rs:140:21 | 140 | let neighbourName: &String = room.Neighbours[direction as usize].as_ref().unwrap(); | ^^^^^^^^^^^^^ help: convert the identifier to snake case: `neighbour_name` warning: variable `parsingStage` should have a snake case name --> src/lib.rs:158:17 | 158 | let mut parsingStage: ParsingStage = ParsingStage::None; | ^^^^^^^^^^^^ help: convert the identifier to snake case: `parsing_stage` warning: variable `lineIndex` should have a snake case name --> src/lib.rs:159:17 | 159 | let mut lineIndex: usize = 1; | ^^^^^^^^^ help: convert the identifier to snake case: `line_index` warning: variable `roomName` should have a snake case name --> src/lib.rs:210:33 | 210 | ... let roomName: &str = tokens[0].trim(); | ^^^^^^^^ help: convert the identifier to snake case: `room_name` warning: variable `roomName` should have a snake case name --> src/lib.rs:225:33 | 225 | ... let roomName: &str = tokens[0].trim(); | ^^^^^^^^ help: convert the identifier to snake case: `room_name` warning: variable `neighbourName` should have a snake case name --> src/lib.rs:227:33 | 227 | ... let neighbourName: &str = tokens[2].trim(); | ^^^^^^^^^^^^^ help: convert the identifier to snake case: `neighbour_name` warning: variable `startRoomName` should have a snake case name --> src/lib.rs:262:29 | 262 | pub fn find_path(&self, startRoomName: &str, endRoomName: &str) -> Result<Option<Vec<&Room>>, Errors> | ^^^^^^^^^^^^^ help: convert the identifier to snake case: `start_room_name` warning: variable `endRoomName` should have a snake case name --> src/lib.rs:262:50 | 262 | pub fn find_path(&self, startRoomName: &str, endRoomName: &str) -> Result<Option<Vec<&Room>>, Errors> | ^^^^^^^^^^^ help: convert the identifier to snake case: `end_room_name` warning: variable `startRoom` should have a snake case name --> src/lib.rs:291:13 | 291 | let startRoom: &Room = self.get_room(startRoomName).unwrap(); | ^^^^^^^^^ help: convert the identifier to snake case: `start_room` warning: variable `currentRoom` should have a snake case name --> src/lib.rs:297:17 | 297 | let currentRoom: &Room = queue.remove(0).unwrap(); | ^^^^^^^^^^^ help: convert the identifier to snake case: `current_room` warning: variable `neighbourRoom` should have a snake case name --> src/lib.rs:315:29 | 315 | let neighbourRoom = self.get_room(neighbour); | ^^^^^^^^^^^^^ help: convert the identifier to snake case: `neighbour_room` warning: variable `currentRoomName` should have a snake case name --> src/lib.rs:336:17 | 336 | let mut currentRoomName = parents.get(endRoomName).unwrap(); | ^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `current_room_name` warning: variable `currentRoomNameStr` should have a snake case name --> src/lib.rs:339:17 | 339 | let currentRoomNameStr = currentRoomName.as_ref().unwrap(); | ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `current_room_name_str` warning: variable `currentRoom` should have a snake case name --> src/lib.rs:340:17 | 340 | let currentRoom = self.get_room(currentRoomNameStr.as_str()); | ^^^^^^^^^^^ help: convert the identifier to snake case: `current_room` warning: `solution` (lib) generated 20 warnings Finished test [unoptimized + debuginfo] target(s) in 4.34s 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 ... 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_invalid_parsing stdout ---- thread 'main' panicked at 'assertion failed: matches!(Dungeon :: from_reader(\"\".as_bytes()),\n Err(Errors :: LineParseError { line_number : 0 }))', tests/solution_test.rs:276:5 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace failures: solution_test::test_invalid_parsing test result: FAILED. 14 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s error: test failed, to rerun pass '--test solution_test'
История (2 версии и 0 коментара)
Николай качи решение на 03.01.2022 20:57 (преди почти 4 години)
use std::{io::BufRead, collections::{HashMap, VecDeque}};
#[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 = 0,
South = 1,
East = 2,
West = 3,
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum ParsingStage
{
None = 0, Rooms = 1, Links = 2, Transition = 3
}
impl Direction
{
pub fn GetOppositeDirection(direction: Direction) -> Direction
{
match direction
{
Direction::North => return Direction::South,
Direction::South => return Direction::North,
Direction::East => return Direction::West,
Direction::West => return Direction::East
}
}
pub fn FromString(str: &str) -> Option<Direction>
{
match str
{
"North" => return Some(Direction::North),
"South" => return Some(Direction::South),
"East" => return Some(Direction::East),
"West" => return Some(Direction::West),
_ => return None
}
}
}
pub struct Room
{
- pub Name: String,
+ pub name: String,
pub Neighbours: [Option<String>; 4]
}
pub struct Dungeon
{
m_Rooms: HashMap<String, Room>
}
impl Dungeon
{
pub fn new() -> Self
{
return Self {
m_Rooms: HashMap::new()
};
}
pub fn add_room(&mut self, name: &str) -> Result<(), Errors>
{
if !self.m_Rooms.contains_key(name)
{
let name: String = String::from(name);
let room: Room = Room
{
- Name: name.clone(),
+ name: name.clone(),
Neighbours: [None, None, None, None]
};
self.m_Rooms.insert(name, room);
return Ok(());
}
else
{
return Err(Errors::DuplicateRoom(String::from(name)));
}
}
pub fn get_room(&self, name: &str) -> Result<&Room, Errors>
{
if self.m_Rooms.contains_key(name)
{
return Ok(self.m_Rooms.get(name).unwrap());
}
else
{
return Err(Errors::UnknownRoom(String::from(name)));
}
}
pub fn set_link(&mut self, name: &str, direction: Direction, neighbourName: &str) -> Result<(), Errors>
{
if self.m_Rooms.contains_key(name)
{
if self.m_Rooms.contains_key(neighbourName)
{
let oppositeDirection: Direction = Direction::GetOppositeDirection(direction);
self.m_Rooms.get_mut(name).unwrap().Neighbours[direction as usize] = Some(String::from(neighbourName));
self.m_Rooms.get_mut(neighbourName).unwrap().Neighbours[oppositeDirection as usize] = Some(String::from(name));
return Ok(());
}
else
{
return Err(Errors::UnknownRoom(String::from(neighbourName)));
}
}
else
{
return Err(Errors::UnknownRoom(String::from(name)));
}
}
pub fn get_next_room(&self, name: &str, direction: Direction) -> Result<Option<&Room>, Errors>
{
if self.m_Rooms.contains_key(name)
{
let room: &Room = self.m_Rooms.get(name).unwrap();
if room.Neighbours[direction as usize].is_some()
{
let neighbourName: &String = room.Neighbours[direction as usize].as_ref().unwrap();
let room: &Room = self.m_Rooms.get(neighbourName).unwrap();
return Ok(Some(room));
}
else
{
return Ok(None);
}
}
else
{
return Err(Errors::UnknownRoom(String::from(name)));
}
}
pub fn from_reader<B: BufRead>(reader: B) -> Result<Self, Errors>
{
let mut parsingStage: ParsingStage = ParsingStage::None;
let mut lineIndex: usize = 1;
let mut dungeon: Dungeon = Dungeon::new();
for line in reader.lines()
{
if line.is_ok()
{
let mut line: String = line.unwrap();
if line == "## Rooms"
{
if parsingStage != ParsingStage::None
{
return Err(Errors::LineParseError{ line_number: lineIndex });
}
parsingStage = ParsingStage::Rooms;
}
else if line == "## Links"
{
if parsingStage != ParsingStage::Transition
{
return Err(Errors::LineParseError{ line_number: lineIndex });
}
parsingStage = ParsingStage::Links;
}
else if line.is_empty()
{
if parsingStage != ParsingStage::Rooms
{
return Err(Errors::LineParseError{ line_number: lineIndex });
}
parsingStage = ParsingStage::Transition;
}
else if &line[0..2] == "- "
{
line.replace_range(0..2, "");
let tokens: Vec<&str> = line.split(" -> ").collect();
match parsingStage
{
ParsingStage::Rooms =>
{
if tokens.len() != 1
{
return Err(Errors::LineParseError{ line_number: lineIndex });
}
let roomName: &str = tokens[0].trim();
if let Err(error) = dungeon.add_room(roomName)
{
return Err(error);
}
},
ParsingStage::Links =>
{
if tokens.len() != 3
{
return Err(Errors::LineParseError{ line_number: lineIndex });
}
let roomName: &str = tokens[0].trim();
let direction: Option<Direction> = Direction::FromString(tokens[1].trim());
let neighbourName: &str = tokens[2].trim();
if direction.is_none()
{
return Err(Errors::DirectionParseError(String::from(tokens[1])));
}
if let Err(error) = dungeon.set_link(roomName, direction.unwrap(), neighbourName)
{
return Err(error);
}
}
_ =>
{
return Err(Errors::LineParseError{ line_number: lineIndex });
}
}
}
else
{
return Err(Errors::LineParseError{ line_number: lineIndex });
}
lineIndex += 1;
}
else
{
return Err(Errors::IoError(line.err().unwrap()));
}
}
return Ok(dungeon);
}
pub fn find_path(&self, startRoomName: &str, endRoomName: &str) -> Result<Option<Vec<&Room>>, Errors>
{
if let Err(error) = self.get_room(startRoomName)
{
return Err(error);
}
if let Err(error) = self.get_room(endRoomName)
{
return Err(error);
}
if startRoomName == endRoomName
{
return Ok(Some(vec![self.get_room(startRoomName).unwrap()]));
}
let mut parents: HashMap<String, Option<String>> = HashMap::new();
for entry in self.m_Rooms.iter()
{
parents.insert(entry.0.clone(), None);
}
let mut visited: HashMap<String, bool> = HashMap::new();
for entry in self.m_Rooms.iter()
{
visited.insert(entry.0.clone(), false);
}
let mut queue: VecDeque<&Room> = VecDeque::new();
let startRoom: &Room = self.get_room(startRoomName).unwrap();
queue.push_back(startRoom);
- *visited.get_mut(&startRoom.Name).unwrap() = true;
+ *visited.get_mut(&startRoom.name).unwrap() = true;
while !queue.is_empty()
{
let currentRoom: &Room = queue.remove(0).unwrap();
- if currentRoom.Name == endRoomName
+ if currentRoom.name == endRoomName
{
break;
}
for neighbour in currentRoom.Neighbours.iter()
{
if neighbour.is_some()
{
let neighbour: &String = neighbour.as_ref().unwrap();
if visited[neighbour] == false
{
- *parents.get_mut(neighbour).unwrap() = Some(currentRoom.Name.clone());
+ *parents.get_mut(neighbour).unwrap() = Some(currentRoom.name.clone());
*visited.get_mut(neighbour).unwrap() = true;
let neighbourRoom = self.get_room(neighbour);
if neighbourRoom.is_err()
{
return Err(neighbourRoom.err().unwrap());
}
queue.push_back(neighbourRoom.unwrap());
}
}
}
}
if parents[endRoomName] == None
{
return Ok(None);
}
let mut path: Vec<&Room> = Vec::new();
path.push(self.get_room(endRoomName).unwrap());
let mut currentRoomName = parents.get(endRoomName).unwrap();
while currentRoomName.is_some()
{
let currentRoomNameStr = currentRoomName.as_ref().unwrap();
let currentRoom = self.get_room(currentRoomNameStr.as_str());
if currentRoom.is_err()
{
return Err(currentRoom.err().unwrap());
}
path.push(currentRoom.unwrap());
currentRoomName = parents.get(currentRoomNameStr).unwrap();
}
path.reverse();
return Ok(Some(path));
}
}