use screeps::{ExitDirection, Terrain, RoomName};
use crate::compressed_terrain::compressed_room_edge_terrain::RoomEdgeTerrain;
#[derive(Debug, PartialEq, Clone, Copy)]
pub struct RoomExit {
packed: u16,
}
impl RoomExit {
const EXIT_DIRECTION_OFFSET: u16 = 6;
const LENGTH_OFFSET: u16 = 9;
const START_POSITION_BITMASK: u16 = 0b111111;
const EXIT_DIRECTION_BITMASK: u16 = 0b111000000;
const EXIT_DIRECTION_INVERTED_BITMASK: u16 = 0b111111000111111;
pub fn new_from_packed(packed: u16) -> Self {
let dir_bits = (packed & Self::EXIT_DIRECTION_BITMASK) >> Self::EXIT_DIRECTION_OFFSET;
let final_packed = match dir_bits {
1 | 3 | 5 | 7 => packed,
_ => (packed & Self::EXIT_DIRECTION_INVERTED_BITMASK) | ((ExitDirection::Top as u16) << Self::EXIT_DIRECTION_OFFSET),
};
Self { packed: final_packed }
}
pub fn new(start: u8, length: u8, direction: ExitDirection) -> Self {
let packed = Self::get_packed_from_parameters(start, length, direction);
Self { packed }
}
pub fn get_packed_from_parameters(start: u8, length: u8, direction: ExitDirection) -> u16 {
let direction_val = direction as u16;
((length as u16) << Self::LENGTH_OFFSET) | (direction_val << Self::EXIT_DIRECTION_OFFSET) | start as u16
}
pub fn start(&self) -> u8 {
(self.packed & Self::START_POSITION_BITMASK) as u8
}
pub fn len(&self) -> u8 {
(self.packed >> Self::LENGTH_OFFSET) as u8
}
pub fn end(&self) -> u8 {
self.start().saturating_add(self.len()).saturating_sub(1)
}
pub fn exit_direction(&self) -> ExitDirection {
let val = (self.packed & Self::EXIT_DIRECTION_BITMASK) >> Self::EXIT_DIRECTION_OFFSET;
match val {
1 => ExitDirection::Top,
3 => ExitDirection::Right,
5 => ExitDirection::Bottom,
7 => ExitDirection::Left,
_ => unreachable!(),
}
}
pub fn packed(&self) -> u16 {
self.packed
}
pub fn memory_size(&self) -> usize {
std::mem::size_of::<u16>()
}
pub fn get_exits_from_edge_terrain(terrain: &RoomEdgeTerrain) -> (Vec<Self>, Vec<Self>, Vec<Self>, Vec<Self>) {
let top_terrain = terrain.get_top_edge_terrain();
let right_terrain = terrain.get_right_edge_terrain();
let bottom_terrain = terrain.get_bottom_edge_terrain();
let left_terrain = terrain.get_left_edge_terrain();
let top_exits = Self::get_exits_from_single_edge(&top_terrain, ExitDirection::Top);
let right_exits = Self::get_exits_from_single_edge(&right_terrain, ExitDirection::Right);
let bottom_exits = Self::get_exits_from_single_edge(&bottom_terrain, ExitDirection::Bottom);
let left_exits = Self::get_exits_from_single_edge(&left_terrain, ExitDirection::Left);
(top_exits, right_exits, bottom_exits, left_exits)
}
pub fn get_exits_from_single_edge(terrain: &[Terrain; 50], direction: ExitDirection) -> Vec<Self> {
let mut exits = Vec::new();
let mut current_exit_start = 0;
let mut current_exit_length = 0;
for i in 0..50 {
if terrain[i] == Terrain::Wall {
if current_exit_length > 0 {
let exit = Self::new(current_exit_start, current_exit_length, direction);
exits.push(exit);
current_exit_start = 0;
current_exit_length = 0;
}
continue;
}
let is_new_exit = {
if i == 0 {
true
} else {
terrain[i-1] == Terrain::Wall
}
};
if is_new_exit {
current_exit_start = i as u8;
current_exit_length = 1;
} else {
current_exit_length += 1;
}
}
if current_exit_length > 0 {
let exit = Self::new(current_exit_start, current_exit_length, direction);
exits.push(exit);
}
exits
}
}
#[derive(Debug, Clone, Copy)]
pub struct RoomExitsData {
data: RoomEdgeTerrain,
room: RoomName,
num_top_exits: usize,
num_right_exits: usize,
num_bottom_exits: usize,
num_left_exits: usize,
}
impl RoomExitsData {
pub fn new_from_compressed_edge_terrain_data(data: RoomEdgeTerrain, room: RoomName) -> Self {
let num_top_exits = RoomExit::get_exits_from_single_edge(&data.get_top_edge_terrain(), ExitDirection::Top).len();
let num_right_exits = RoomExit::get_exits_from_single_edge(&data.get_right_edge_terrain(), ExitDirection::Right).len();
let num_bottom_exits = RoomExit::get_exits_from_single_edge(&data.get_bottom_edge_terrain(), ExitDirection::Bottom).len();
let num_left_exits = RoomExit::get_exits_from_single_edge(&data.get_left_edge_terrain(), ExitDirection::Left).len();
Self {
data,
room,
num_top_exits,
num_right_exits,
num_bottom_exits,
num_left_exits,
}
}
pub fn memory_size(&self) -> usize {
self.data.memory_size()
}
pub fn top_edge_exits(&self) -> Vec<RoomExit> {
RoomExit::get_exits_from_single_edge(&self.data.get_top_edge_terrain(), ExitDirection::Top)
}
pub fn right_edge_exits(&self) -> Vec<RoomExit> {
RoomExit::get_exits_from_single_edge(&self.data.get_right_edge_terrain(), ExitDirection::Right)
}
pub fn bottom_edge_exits(&self) -> Vec<RoomExit> {
RoomExit::get_exits_from_single_edge(&self.data.get_bottom_edge_terrain(), ExitDirection::Bottom)
}
pub fn left_edge_exits(&self) -> Vec<RoomExit> {
RoomExit::get_exits_from_single_edge(&self.data.get_left_edge_terrain(), ExitDirection::Left)
}
pub fn num_top_exits(&self) -> usize {
self.num_top_exits
}
pub fn num_right_exits(&self) -> usize {
self.num_right_exits
}
pub fn num_bottom_exits(&self) -> usize {
self.num_bottom_exits
}
pub fn num_left_exits(&self) -> usize {
self.num_left_exits
}
pub fn num_exits(&self) -> usize {
self.num_top_exits + self.num_right_exits + self.num_bottom_exits + self.num_left_exits
}
pub fn edge_terrain_data(&self) -> &RoomEdgeTerrain {
&self.data
}
pub fn connected_to_top_neighbor(&self) -> bool {
(self.num_top_exits > 0) && top_room(self.room).is_some()
}
pub fn connected_to_right_neighbor(&self) -> bool {
(self.num_right_exits > 0) && right_room(self.room).is_some()
}
pub fn connected_to_bottom_neighbor(&self) -> bool {
(self.num_bottom_exits > 0) && bottom_room(self.room).is_some()
}
pub fn connected_to_left_neighbor(&self) -> bool {
(self.num_left_exits > 0) && left_room(self.room).is_some()
}
pub fn get_exit_by_index(&self, index: usize) -> Option<RoomExit> {
let total_num_exits = self.num_top_exits + self.num_right_exits + self.num_bottom_exits + self.num_left_exits;
if total_num_exits == 0 {
None
} else {
let max_index = total_num_exits - 1; if index > max_index {
None
} else {
let min_idx_top = 0;
let max_idx_top = if self.num_top_exits > 0 {
min_idx_top + self.num_top_exits - 1
} else {
min_idx_top
};
let min_idx_right = if self.num_top_exits > 0 {
max_idx_top + 1
} else {
max_idx_top
};
let max_idx_right = if self.num_right_exits > 0 {
min_idx_right + self.num_right_exits - 1
} else {
min_idx_right
};
let min_idx_bottom = if self.num_right_exits > 0 {
max_idx_right + 1
} else {
max_idx_right
};
let max_idx_bottom = if self.num_bottom_exits > 0 {
min_idx_bottom + self.num_bottom_exits - 1
} else {
min_idx_bottom
};
let min_idx_left = if self.num_bottom_exits > 0 {
max_idx_bottom + 1
} else {
max_idx_bottom
};
let max_idx_left = if self.num_left_exits > 0 {
min_idx_left + self.num_left_exits - 1
} else {
min_idx_left
};
if self.num_top_exits > 0 {
if index <= max_idx_top {
let exits = self.top_edge_exits();
let local_index = index - min_idx_top;
return exits.get(local_index).copied();
}
}
if self.num_right_exits > 0 {
if index <= max_idx_right {
let exits = self.right_edge_exits();
let local_index = index - min_idx_right;
return exits.get(local_index).copied();
}
}
if self.num_bottom_exits > 0 {
if index <= max_idx_bottom {
let exits = self.bottom_edge_exits();
let local_index = index - min_idx_bottom;
return exits.get(local_index).copied();
}
}
if self.num_left_exits > 0 {
if index <= max_idx_left {
let exits = self.left_edge_exits();
let local_index = index - min_idx_left;
return exits.get(local_index).copied();
}
}
None
}
}
}
pub fn room(&self) -> RoomName {
self.room
}
pub fn iter(&self) -> RoomExitsIter {
RoomExitsIter::new(*self)
}
}
pub struct RoomExitsIter {
data: RoomExitsData,
current_index: usize,
length: usize,
}
impl RoomExitsIter {
fn new(data: RoomExitsData) -> Self {
Self {
data,
current_index: 0,
length: data.num_exits(),
}
}
}
impl Iterator for RoomExitsIter {
type Item = RoomExit;
fn next(&mut self) -> Option<Self::Item> {
if self.current_index >= self.length {
None
} else {
let exit = self.data.get_exit_by_index(self.current_index);
self.current_index += 1;
exit
}
}
}
pub fn top_room(room: RoomName) -> Option<RoomName> {
room.checked_add((0, -1))
}
pub fn right_room(room: RoomName) -> Option<RoomName> {
room.checked_add((1, 0))
}
pub fn bottom_room(room: RoomName) -> Option<RoomName> {
room.checked_add((0, 1))
}
pub fn left_room(room: RoomName) -> Option<RoomName> {
room.checked_add((-1, 0))
}
#[cfg(test)]
mod test {
use super::*;
#[test]
pub fn room_exit_verify_parameter_packing() {
let directions = [ExitDirection::Top, ExitDirection::Right, ExitDirection::Bottom, ExitDirection::Left];
for start in 0..50 {
let max_length = 50 - start;
for length in 1..max_length {
for direction in directions {
let exit = RoomExit::new(start, length, direction);
let packed = exit.packed();
let end = start + length - 1;
assert_eq!(exit.start(), start, "Start position unpacking mismatch, ({start}, {length}, {direction:?}), Packed: {packed:b}");
assert_eq!(exit.len(), length, "Length unpacking mismatch, ({start}, {length}, {direction:?}), Packed: {packed:b}");
assert_eq!(exit.end(), end, "End position calculation mismatch, ({start}, {length}, {end}, {direction:?})");
assert_eq!(exit.exit_direction(), direction, "Exit direction calculation mismatch, ({start}, {length}, {direction:?}, Packed: {packed:b})");
}
}
}
}
#[test]
pub fn room_exit_new_from_packed_matches_original_data() {
let directions = [ExitDirection::Top, ExitDirection::Right, ExitDirection::Bottom, ExitDirection::Left];
for start in 0..50 {
let max_length = 50 - start;
for length in 1..max_length {
for direction in directions {
let exit = RoomExit::new(start, length, direction);
let packed = exit.packed();
let new_exit = RoomExit::new_from_packed(packed);
let end = start + length - 1;
assert_eq!(exit.start(), new_exit.start(), "Start position mismatch, ({start}, {length}, {direction:?}), Packed: {packed:b}");
assert_eq!(exit.len(), new_exit.len(), "Length mismatch, ({start}, {length}, {direction:?}), Packed: {packed:b}");
assert_eq!(exit.end(), new_exit.end(), "End position mismatch, ({start}, {length}, {end}, {direction:?})");
assert_eq!(exit.exit_direction(), new_exit.exit_direction(), "Exit direction mismatch, ({start}, {length}, {direction:?}, Packed: {packed:b})");
}
}
}
}
#[test]
pub fn room_exit_new_from_packed_sanitizes_invalid_direction_data() {
let directions = [ExitDirection::Top, ExitDirection::Right, ExitDirection::Bottom, ExitDirection::Left];
let invalid_direction_values: [u16; 4] = [0, 2, 4, 6]; let direction_include_bitmask = 0b111111000111111;
for start in 0..50 {
let max_length = 50 - start;
for length in 1..max_length {
for direction in directions {
for invalid_direction_val in &invalid_direction_values {
let exit = RoomExit::new(start, length, direction);
let packed = exit.packed();
let invalid_packed = (packed & direction_include_bitmask) | (invalid_direction_val << RoomExit::EXIT_DIRECTION_OFFSET);
let new_exit = RoomExit::new_from_packed(invalid_packed);
let new_packed = new_exit.packed();
let sanitized_dir_bits = (new_packed & RoomExit::EXIT_DIRECTION_BITMASK) >> RoomExit::EXIT_DIRECTION_OFFSET;
let end = start + length - 1;
assert_eq!(exit.start(), new_exit.start(), "Start position mismatch, ({start}, {length}, {direction:?}), Packed: {new_packed:b}");
assert_eq!(exit.len(), new_exit.len(), "Length mismatch, ({start}, {length}, {direction:?}), Packed: {new_packed:b}");
assert_eq!(exit.end(), new_exit.end(), "End position mismatch, ({start}, {length}, {end}, {direction:?})");
assert_eq!(sanitized_dir_bits, ExitDirection::Top as u16, "Exit direction not sanitized, ({start}, {length}, {direction:?}, Packed: {new_packed:b}, Invalid Packed: {invalid_packed:b})");
assert_eq!(new_exit.exit_direction(), ExitDirection::Top, "Exit direction not sanitized, ({start}, {length}, {direction:?}, Packed: {new_packed:b})");
}
}
}
}
}
#[test]
pub fn room_exit_get_exits_from_single_edge_returns_empty_for_all_walls() {
let terrain = [Terrain::Wall; 50];
let exits = RoomExit::get_exits_from_single_edge(&terrain, ExitDirection::Top);
assert_eq!(exits.len(), 0);
}
#[test]
pub fn room_exit_get_exits_from_single_edge_returns_expected_number_of_exits() {
let mut terrain = [Terrain::Plain; 50];
terrain[0] = Terrain::Wall;
terrain[49] = Terrain::Wall;
let exits = RoomExit::get_exits_from_single_edge(&terrain, ExitDirection::Top);
assert_eq!(exits.len(), 1);
let mut terrain = [Terrain::Plain; 50];
terrain[0] = Terrain::Wall;
terrain[49] = Terrain::Wall;
for i in 5..10 {
terrain[i] = Terrain::Wall;
}
let exits = RoomExit::get_exits_from_single_edge(&terrain, ExitDirection::Top);
assert_eq!(exits.len(), 2);
let mut terrain = [Terrain::Plain; 50];
terrain[0] = Terrain::Wall;
terrain[49] = Terrain::Wall;
for i in 5..10 {
terrain[i] = Terrain::Wall;
}
for i in 23..25 {
terrain[i] = Terrain::Wall;
}
let exits = RoomExit::get_exits_from_single_edge(&terrain, ExitDirection::Top);
assert_eq!(exits.len(), 3);
}
#[test]
pub fn room_exits_data_get_exit_by_index_returns_none_for_bad_indices() {
let room_name = RoomName::new("W0N0").unwrap();
let edge = [Terrain::Wall; 50];
let terrain = RoomEdgeTerrain::new_from_terrain_slices(&edge, &edge, &edge, &edge).unwrap();
let exits_data = RoomExitsData::new_from_compressed_edge_terrain_data(terrain, room_name);
assert_eq!(exits_data.get_exit_by_index(0), None, "Exit returned when none exists");
let edge = [Terrain::Plain; 50];
let terrain = RoomEdgeTerrain::new_from_terrain_slices(&edge, &edge, &edge, &edge).unwrap();
let exits_data = RoomExitsData::new_from_compressed_edge_terrain_data(terrain, room_name);
assert_eq!(exits_data.get_exit_by_index(8), None, "Exit returned for invalid index");
}
#[test]
pub fn room_exits_data_get_exit_by_index_returns_some_for_exits_on_4_edges() {
let room_name = RoomName::new("W0N0").unwrap();
let edge = [Terrain::Plain; 50];
let terrain = RoomEdgeTerrain::new_from_terrain_slices(&edge, &edge, &edge, &edge).unwrap();
let exits_data = RoomExitsData::new_from_compressed_edge_terrain_data(terrain, room_name);
assert_eq!(1, exits_data.num_top_exits(), "Top exit count mismatch");
assert_eq!(1, exits_data.num_right_exits(), "Right exit count mismatch");
assert_eq!(1, exits_data.num_bottom_exits(), "Bottom exit count mismatch");
assert_eq!(1, exits_data.num_left_exits(), "Left exit count mismatch");
let exit_opt = exits_data.get_exit_by_index(0);
assert!(exit_opt.is_some(), "Exit should exist");
let exit = exit_opt.unwrap();
assert_eq!(exit.start(), 1, "Exit start position invalid");
assert_eq!(exit.end(), 48, "Exit end position invalid");
assert_eq!(exit.len(), 48, "Exit length invalid");
assert_eq!(exit.exit_direction(), ExitDirection::Top, "Exit direction invalid");
let exit_opt = exits_data.get_exit_by_index(1);
assert!(exit_opt.is_some(), "Exit should exist");
let exit = exit_opt.unwrap();
assert_eq!(exit.start(), 1, "Exit start position invalid");
assert_eq!(exit.end(), 48, "Exit end position invalid");
assert_eq!(exit.len(), 48, "Exit length invalid");
assert_eq!(exit.exit_direction(), ExitDirection::Right, "Exit direction invalid");
let exit_opt = exits_data.get_exit_by_index(2);
assert!(exit_opt.is_some(), "Exit should exist");
let exit = exit_opt.unwrap();
assert_eq!(exit.start(), 1, "Exit start position invalid");
assert_eq!(exit.end(), 48, "Exit end position invalid");
assert_eq!(exit.len(), 48, "Exit length invalid");
assert_eq!(exit.exit_direction(), ExitDirection::Bottom, "Exit direction invalid");
let exit_opt = exits_data.get_exit_by_index(3);
assert!(exit_opt.is_some(), "Exit should exist");
let exit = exit_opt.unwrap();
assert_eq!(exit.start(), 1, "Exit start position invalid");
assert_eq!(exit.end(), 48, "Exit end position invalid");
assert_eq!(exit.len(), 48, "Exit length invalid");
assert_eq!(exit.exit_direction(), ExitDirection::Left, "Exit direction invalid");
}
#[test]
pub fn room_exits_data_get_exit_by_index_returns_some_for_exits_except_top_edge() {
let room_name = RoomName::new("W0N0").unwrap();
let wall_edge = [Terrain::Wall; 50];
let edge = [Terrain::Plain; 50];
let terrain = RoomEdgeTerrain::new_from_terrain_slices(&wall_edge, &edge, &edge, &edge).unwrap();
let exits_data = RoomExitsData::new_from_compressed_edge_terrain_data(terrain, room_name);
assert_eq!(0, exits_data.num_top_exits(), "Top exit count mismatch");
assert_eq!(1, exits_data.num_right_exits(), "Right exit count mismatch");
assert_eq!(1, exits_data.num_bottom_exits(), "Bottom exit count mismatch");
assert_eq!(1, exits_data.num_left_exits(), "Left exit count mismatch");
let exit_opt = exits_data.get_exit_by_index(0);
assert!(exit_opt.is_some(), "Exit should exist");
let exit = exit_opt.unwrap();
assert_eq!(exit.start(), 1, "Exit start position invalid");
assert_eq!(exit.end(), 48, "Exit end position invalid");
assert_eq!(exit.len(), 48, "Exit length invalid");
assert_eq!(exit.exit_direction(), ExitDirection::Right, "Exit direction invalid");
let exit_opt = exits_data.get_exit_by_index(1);
assert!(exit_opt.is_some(), "Exit should exist");
let exit = exit_opt.unwrap();
assert_eq!(exit.start(), 1, "Exit start position invalid");
assert_eq!(exit.end(), 48, "Exit end position invalid");
assert_eq!(exit.len(), 48, "Exit length invalid");
assert_eq!(exit.exit_direction(), ExitDirection::Bottom, "Exit direction invalid");
let exit_opt = exits_data.get_exit_by_index(2);
assert!(exit_opt.is_some(), "Exit should exist");
let exit = exit_opt.unwrap();
assert_eq!(exit.start(), 1, "Exit start position invalid");
assert_eq!(exit.end(), 48, "Exit end position invalid");
assert_eq!(exit.len(), 48, "Exit length invalid");
assert_eq!(exit.exit_direction(), ExitDirection::Left, "Exit direction invalid");
}
#[test]
pub fn room_exits_data_get_exit_by_index_returns_some_for_exits_except_right_edge() {
let room_name = RoomName::new("W0N0").unwrap();
let wall_edge = [Terrain::Wall; 50];
let edge = [Terrain::Plain; 50];
let terrain = RoomEdgeTerrain::new_from_terrain_slices(&edge, &wall_edge, &edge, &edge).unwrap();
let exits_data = RoomExitsData::new_from_compressed_edge_terrain_data(terrain, room_name);
assert_eq!(1, exits_data.num_top_exits(), "Top exit count mismatch");
assert_eq!(0, exits_data.num_right_exits(), "Right exit count mismatch");
assert_eq!(1, exits_data.num_bottom_exits(), "Bottom exit count mismatch");
assert_eq!(1, exits_data.num_left_exits(), "Left exit count mismatch");
let exit_opt = exits_data.get_exit_by_index(0);
assert!(exit_opt.is_some(), "Exit should exist");
let exit = exit_opt.unwrap();
assert_eq!(exit.start(), 1, "Exit start position invalid");
assert_eq!(exit.end(), 48, "Exit end position invalid");
assert_eq!(exit.len(), 48, "Exit length invalid");
assert_eq!(exit.exit_direction(), ExitDirection::Top, "Exit direction invalid");
let exit_opt = exits_data.get_exit_by_index(1);
assert!(exit_opt.is_some(), "Exit should exist");
let exit = exit_opt.unwrap();
assert_eq!(exit.start(), 1, "Exit start position invalid");
assert_eq!(exit.end(), 48, "Exit end position invalid");
assert_eq!(exit.len(), 48, "Exit length invalid");
assert_eq!(exit.exit_direction(), ExitDirection::Bottom, "Exit direction invalid");
let exit_opt = exits_data.get_exit_by_index(2);
assert!(exit_opt.is_some(), "Exit should exist");
let exit = exit_opt.unwrap();
assert_eq!(exit.start(), 1, "Exit start position invalid");
assert_eq!(exit.end(), 48, "Exit end position invalid");
assert_eq!(exit.len(), 48, "Exit length invalid");
assert_eq!(exit.exit_direction(), ExitDirection::Left, "Exit direction invalid");
}
#[test]
pub fn room_exits_data_get_exit_by_index_returns_some_for_exits_except_bottom_edge() {
let room_name = RoomName::new("W0N0").unwrap();
let wall_edge = [Terrain::Wall; 50];
let edge = [Terrain::Plain; 50];
let terrain = RoomEdgeTerrain::new_from_terrain_slices(&edge, &edge, &wall_edge, &edge).unwrap();
let exits_data = RoomExitsData::new_from_compressed_edge_terrain_data(terrain, room_name);
assert_eq!(1, exits_data.num_top_exits(), "Top exit count mismatch");
assert_eq!(1, exits_data.num_right_exits(), "Right exit count mismatch");
assert_eq!(0, exits_data.num_bottom_exits(), "Bottom exit count mismatch");
assert_eq!(1, exits_data.num_left_exits(), "Left exit count mismatch");
let exit_opt = exits_data.get_exit_by_index(0);
assert!(exit_opt.is_some(), "Exit should exist");
let exit = exit_opt.unwrap();
assert_eq!(exit.start(), 1, "Exit start position invalid");
assert_eq!(exit.end(), 48, "Exit end position invalid");
assert_eq!(exit.len(), 48, "Exit length invalid");
assert_eq!(exit.exit_direction(), ExitDirection::Top, "Exit direction invalid");
let exit_opt = exits_data.get_exit_by_index(1);
assert!(exit_opt.is_some(), "Exit should exist");
let exit = exit_opt.unwrap();
assert_eq!(exit.start(), 1, "Exit start position invalid");
assert_eq!(exit.end(), 48, "Exit end position invalid");
assert_eq!(exit.len(), 48, "Exit length invalid");
assert_eq!(exit.exit_direction(), ExitDirection::Right, "Exit direction invalid");
let exit_opt = exits_data.get_exit_by_index(2);
assert!(exit_opt.is_some(), "Exit should exist");
let exit = exit_opt.unwrap();
assert_eq!(exit.start(), 1, "Exit start position invalid");
assert_eq!(exit.end(), 48, "Exit end position invalid");
assert_eq!(exit.len(), 48, "Exit length invalid");
assert_eq!(exit.exit_direction(), ExitDirection::Left, "Exit direction invalid");
}
#[test]
pub fn room_exits_data_get_exit_by_index_returns_some_for_exits_except_left_edge() {
let room_name = RoomName::new("W0N0").unwrap();
let wall_edge = [Terrain::Wall; 50];
let edge = [Terrain::Plain; 50];
let terrain = RoomEdgeTerrain::new_from_terrain_slices(&edge, &edge, &edge, &wall_edge).unwrap();
let exits_data = RoomExitsData::new_from_compressed_edge_terrain_data(terrain, room_name);
assert_eq!(1, exits_data.num_top_exits(), "Top exit count mismatch");
assert_eq!(1, exits_data.num_right_exits(), "Right exit count mismatch");
assert_eq!(1, exits_data.num_bottom_exits(), "Bottom exit count mismatch");
assert_eq!(0, exits_data.num_left_exits(), "Left exit count mismatch");
let exit_opt = exits_data.get_exit_by_index(0);
assert!(exit_opt.is_some(), "Exit should exist");
let exit = exit_opt.unwrap();
assert_eq!(exit.start(), 1, "Exit start position invalid");
assert_eq!(exit.end(), 48, "Exit end position invalid");
assert_eq!(exit.len(), 48, "Exit length invalid");
assert_eq!(exit.exit_direction(), ExitDirection::Top, "Exit direction invalid");
let exit_opt = exits_data.get_exit_by_index(1);
assert!(exit_opt.is_some(), "Exit should exist");
let exit = exit_opt.unwrap();
assert_eq!(exit.start(), 1, "Exit start position invalid");
assert_eq!(exit.end(), 48, "Exit end position invalid");
assert_eq!(exit.len(), 48, "Exit length invalid");
assert_eq!(exit.exit_direction(), ExitDirection::Right, "Exit direction invalid");
let exit_opt = exits_data.get_exit_by_index(2);
assert!(exit_opt.is_some(), "Exit should exist");
let exit = exit_opt.unwrap();
assert_eq!(exit.start(), 1, "Exit start position invalid");
assert_eq!(exit.end(), 48, "Exit end position invalid");
assert_eq!(exit.len(), 48, "Exit length invalid");
assert_eq!(exit.exit_direction(), ExitDirection::Bottom, "Exit direction invalid");
}
#[test]
pub fn room_exits_data_get_exit_by_index_returns_some_for_exits_except_top_or_right() {
let room_name = RoomName::new("W0N0").unwrap();
let wall_edge = [Terrain::Wall; 50];
let edge = [Terrain::Plain; 50];
let terrain = RoomEdgeTerrain::new_from_terrain_slices(&wall_edge, &wall_edge, &edge, &edge).unwrap();
let exits_data = RoomExitsData::new_from_compressed_edge_terrain_data(terrain, room_name);
assert_eq!(0, exits_data.num_top_exits(), "Top exit count mismatch");
assert_eq!(0, exits_data.num_right_exits(), "Right exit count mismatch");
assert_eq!(1, exits_data.num_bottom_exits(), "Bottom exit count mismatch");
assert_eq!(1, exits_data.num_left_exits(), "Left exit count mismatch");
let exit_opt = exits_data.get_exit_by_index(0);
assert!(exit_opt.is_some(), "Exit should exist");
let exit = exit_opt.unwrap();
assert_eq!(exit.start(), 1, "Exit start position invalid");
assert_eq!(exit.end(), 48, "Exit end position invalid");
assert_eq!(exit.len(), 48, "Exit length invalid");
assert_eq!(exit.exit_direction(), ExitDirection::Bottom, "Exit direction invalid");
let exit_opt = exits_data.get_exit_by_index(1);
assert!(exit_opt.is_some(), "Exit should exist");
let exit = exit_opt.unwrap();
assert_eq!(exit.start(), 1, "Exit start position invalid");
assert_eq!(exit.end(), 48, "Exit end position invalid");
assert_eq!(exit.len(), 48, "Exit length invalid");
assert_eq!(exit.exit_direction(), ExitDirection::Left, "Exit direction invalid");
}
#[test]
pub fn room_exits_data_get_exit_by_index_returns_some_for_exits_except_top_or_right_or_bottom() {
let room_name = RoomName::new("W0N0").unwrap();
let wall_edge = [Terrain::Wall; 50];
let edge = [Terrain::Plain; 50];
let terrain = RoomEdgeTerrain::new_from_terrain_slices(&wall_edge, &wall_edge, &wall_edge, &edge).unwrap();
let exits_data = RoomExitsData::new_from_compressed_edge_terrain_data(terrain, room_name);
assert_eq!(0, exits_data.num_top_exits(), "Top exit count mismatch");
assert_eq!(0, exits_data.num_right_exits(), "Right exit count mismatch");
assert_eq!(0, exits_data.num_bottom_exits(), "Bottom exit count mismatch");
assert_eq!(1, exits_data.num_left_exits(), "Left exit count mismatch");
let exit_opt = exits_data.get_exit_by_index(0);
assert!(exit_opt.is_some(), "Exit should exist");
let exit = exit_opt.unwrap();
assert_eq!(exit.start(), 1, "Exit start position invalid");
assert_eq!(exit.end(), 48, "Exit end position invalid");
assert_eq!(exit.len(), 48, "Exit length invalid");
assert_eq!(exit.exit_direction(), ExitDirection::Left, "Exit direction invalid");
}
}