1use screeps::{ExitDirection, Terrain, RoomName};
2
3use crate::compressed_terrain::compressed_room_edge_terrain::RoomEdgeTerrain;
4
5
6#[derive(Debug, PartialEq, Clone, Copy)]
13pub struct RoomExit {
14 packed: u16,
31}
32
33impl RoomExit {
34 const EXIT_DIRECTION_OFFSET: u16 = 6;
35 const LENGTH_OFFSET: u16 = 9;
36 const START_POSITION_BITMASK: u16 = 0b111111;
37 const EXIT_DIRECTION_BITMASK: u16 = 0b111000000;
38 const EXIT_DIRECTION_INVERTED_BITMASK: u16 = 0b111111000111111;
39
40 pub fn new_from_packed(packed: u16) -> Self {
45 let dir_bits = (packed & Self::EXIT_DIRECTION_BITMASK) >> Self::EXIT_DIRECTION_OFFSET;
47 let final_packed = match dir_bits {
48 1 | 3 | 5 | 7 => packed,
49 _ => (packed & Self::EXIT_DIRECTION_INVERTED_BITMASK) | ((ExitDirection::Top as u16) << Self::EXIT_DIRECTION_OFFSET),
50 };
51
52 Self { packed: final_packed }
53 }
54
55 pub fn new(start: u8, length: u8, direction: ExitDirection) -> Self {
57 let packed = Self::get_packed_from_parameters(start, length, direction);
58
59 Self { packed }
60 }
61
62 pub fn get_packed_from_parameters(start: u8, length: u8, direction: ExitDirection) -> u16 {
64 let direction_val = direction as u16;
65 ((length as u16) << Self::LENGTH_OFFSET) | (direction_val << Self::EXIT_DIRECTION_OFFSET) | start as u16
66 }
67
68 pub fn start(&self) -> u8 {
70 (self.packed & Self::START_POSITION_BITMASK) as u8
73 }
74
75 pub fn len(&self) -> u8 {
77 (self.packed >> Self::LENGTH_OFFSET) as u8
81 }
82
83 pub fn end(&self) -> u8 {
85 self.start().saturating_add(self.len()).saturating_sub(1)
86 }
87
88 pub fn exit_direction(&self) -> ExitDirection {
90 let val = (self.packed & Self::EXIT_DIRECTION_BITMASK) >> Self::EXIT_DIRECTION_OFFSET;
91 match val {
92 1 => ExitDirection::Top,
93 3 => ExitDirection::Right,
94 5 => ExitDirection::Bottom,
95 7 => ExitDirection::Left,
96
97 _ => unreachable!(),
99 }
100 }
101
102 pub fn packed(&self) -> u16 {
104 self.packed
105 }
106
107 pub fn memory_size(&self) -> usize {
109 std::mem::size_of::<u16>()
110 }
111
112 pub fn get_exits_from_edge_terrain(terrain: &RoomEdgeTerrain) -> (Vec<Self>, Vec<Self>, Vec<Self>, Vec<Self>) {
116 let top_terrain = terrain.get_top_edge_terrain();
117 let right_terrain = terrain.get_right_edge_terrain();
118 let bottom_terrain = terrain.get_bottom_edge_terrain();
119 let left_terrain = terrain.get_left_edge_terrain();
120
121 let top_exits = Self::get_exits_from_single_edge(&top_terrain, ExitDirection::Top);
122 let right_exits = Self::get_exits_from_single_edge(&right_terrain, ExitDirection::Right);
123 let bottom_exits = Self::get_exits_from_single_edge(&bottom_terrain, ExitDirection::Bottom);
124 let left_exits = Self::get_exits_from_single_edge(&left_terrain, ExitDirection::Left);
125
126 (top_exits, right_exits, bottom_exits, left_exits)
127 }
128
129 pub fn get_exits_from_single_edge(terrain: &[Terrain; 50], direction: ExitDirection) -> Vec<Self> {
133 let mut exits = Vec::new();
134
135 let mut current_exit_start = 0;
141 let mut current_exit_length = 0;
142
143 for i in 0..50 {
144 if terrain[i] == Terrain::Wall {
145 if current_exit_length > 0 {
148 let exit = Self::new(current_exit_start, current_exit_length, direction);
149 exits.push(exit);
150 current_exit_start = 0;
151 current_exit_length = 0;
152 }
153
154 continue;
155 }
156
157 let is_new_exit = {
160 if i == 0 {
161 true
163 } else {
164 terrain[i-1] == Terrain::Wall
166 }
167 };
168
169 if is_new_exit {
171 current_exit_start = i as u8;
172 current_exit_length = 1;
173 } else {
174 current_exit_length += 1;
175 }
176 }
177
178 if current_exit_length > 0 {
181 let exit = Self::new(current_exit_start, current_exit_length, direction);
182 exits.push(exit);
183 }
184
185 exits
186 }
187}
188
189#[derive(Debug, Clone, Copy)]
191pub struct RoomExitsData {
192 data: RoomEdgeTerrain,
195
196 room: RoomName,
197 num_top_exits: usize,
198 num_right_exits: usize,
199 num_bottom_exits: usize,
200 num_left_exits: usize,
201}
202
203impl RoomExitsData {
204 pub fn new_from_compressed_edge_terrain_data(data: RoomEdgeTerrain, room: RoomName) -> Self {
205 let num_top_exits = RoomExit::get_exits_from_single_edge(&data.get_top_edge_terrain(), ExitDirection::Top).len();
206 let num_right_exits = RoomExit::get_exits_from_single_edge(&data.get_right_edge_terrain(), ExitDirection::Right).len();
207 let num_bottom_exits = RoomExit::get_exits_from_single_edge(&data.get_bottom_edge_terrain(), ExitDirection::Bottom).len();
208 let num_left_exits = RoomExit::get_exits_from_single_edge(&data.get_left_edge_terrain(), ExitDirection::Left).len();
209
210 Self {
211 data,
212 room,
213 num_top_exits,
214 num_right_exits,
215 num_bottom_exits,
216 num_left_exits,
217 }
218 }
219
220 pub fn memory_size(&self) -> usize {
222 self.data.memory_size()
223 }
224
225 pub fn top_edge_exits(&self) -> Vec<RoomExit> {
227 RoomExit::get_exits_from_single_edge(&self.data.get_top_edge_terrain(), ExitDirection::Top)
228 }
229
230 pub fn right_edge_exits(&self) -> Vec<RoomExit> {
232 RoomExit::get_exits_from_single_edge(&self.data.get_right_edge_terrain(), ExitDirection::Right)
233 }
234
235 pub fn bottom_edge_exits(&self) -> Vec<RoomExit> {
237 RoomExit::get_exits_from_single_edge(&self.data.get_bottom_edge_terrain(), ExitDirection::Bottom)
238 }
239
240 pub fn left_edge_exits(&self) -> Vec<RoomExit> {
242 RoomExit::get_exits_from_single_edge(&self.data.get_left_edge_terrain(), ExitDirection::Left)
243 }
244
245 pub fn num_top_exits(&self) -> usize {
249 self.num_top_exits
250 }
251
252 pub fn num_right_exits(&self) -> usize {
256 self.num_right_exits
257 }
258
259 pub fn num_bottom_exits(&self) -> usize {
263 self.num_bottom_exits
264 }
265
266 pub fn num_left_exits(&self) -> usize {
270 self.num_left_exits
271 }
272
273 pub fn num_exits(&self) -> usize {
277 self.num_top_exits + self.num_right_exits + self.num_bottom_exits + self.num_left_exits
278 }
279
280 pub fn edge_terrain_data(&self) -> &RoomEdgeTerrain {
282 &self.data
283 }
284
285 pub fn connected_to_top_neighbor(&self) -> bool {
291 (self.num_top_exits > 0) && top_room(self.room).is_some()
292 }
293
294 pub fn connected_to_right_neighbor(&self) -> bool {
300 (self.num_right_exits > 0) && right_room(self.room).is_some()
301 }
302
303 pub fn connected_to_bottom_neighbor(&self) -> bool {
309 (self.num_bottom_exits > 0) && bottom_room(self.room).is_some()
310 }
311
312 pub fn connected_to_left_neighbor(&self) -> bool {
318 (self.num_left_exits > 0) && left_room(self.room).is_some()
319 }
320
321 pub fn get_exit_by_index(&self, index: usize) -> Option<RoomExit> {
334 let total_num_exits = self.num_top_exits + self.num_right_exits + self.num_bottom_exits + self.num_left_exits;
335 if total_num_exits == 0 {
336 None
338 } else {
339 let max_index = total_num_exits - 1; if index > max_index {
341 None
343 } else {
344 let min_idx_top = 0;
347 let max_idx_top = if self.num_top_exits > 0 {
348 min_idx_top + self.num_top_exits - 1
349 } else {
350 min_idx_top
351 };
352
353 let min_idx_right = if self.num_top_exits > 0 {
354 max_idx_top + 1
355 } else {
356 max_idx_top
357 };
358 let max_idx_right = if self.num_right_exits > 0 {
359 min_idx_right + self.num_right_exits - 1
360 } else {
361 min_idx_right
362 };
363
364 let min_idx_bottom = if self.num_right_exits > 0 {
365 max_idx_right + 1
366 } else {
367 max_idx_right
368 };
369 let max_idx_bottom = if self.num_bottom_exits > 0 {
370 min_idx_bottom + self.num_bottom_exits - 1
371 } else {
372 min_idx_bottom
373 };
374
375 let min_idx_left = if self.num_bottom_exits > 0 {
376 max_idx_bottom + 1
377 } else {
378 max_idx_bottom
379 };
380 let max_idx_left = if self.num_left_exits > 0 {
381 min_idx_left + self.num_left_exits - 1
382 } else {
383 min_idx_left
384 };
385
386 if self.num_top_exits > 0 {
390 if index <= max_idx_top {
391 let exits = self.top_edge_exits();
393 let local_index = index - min_idx_top;
394 return exits.get(local_index).copied();
395 }
396 }
397
398 if self.num_right_exits > 0 {
402 if index <= max_idx_right {
403 let exits = self.right_edge_exits();
405 let local_index = index - min_idx_right;
406 return exits.get(local_index).copied();
407 }
408 }
409
410 if self.num_bottom_exits > 0 {
414 if index <= max_idx_bottom {
415 let exits = self.bottom_edge_exits();
417 let local_index = index - min_idx_bottom;
418 return exits.get(local_index).copied();
419 }
420 }
421
422 if self.num_left_exits > 0 {
427 if index <= max_idx_left {
428 let exits = self.left_edge_exits();
430 let local_index = index - min_idx_left;
431 return exits.get(local_index).copied();
432 }
433 }
434
435 None
438 }
439 }
440 }
441
442 pub fn room(&self) -> RoomName {
444 self.room
445 }
446
447 pub fn iter(&self) -> RoomExitsIter {
449 RoomExitsIter::new(*self)
450 }
451}
452
453pub struct RoomExitsIter {
454 data: RoomExitsData,
455 current_index: usize,
456 length: usize,
457}
458
459impl RoomExitsIter {
460 fn new(data: RoomExitsData) -> Self {
461 Self {
462 data,
463 current_index: 0,
464 length: data.num_exits(),
465 }
466 }
467}
468
469impl Iterator for RoomExitsIter {
470 type Item = RoomExit;
471
472 fn next(&mut self) -> Option<Self::Item> {
473 if self.current_index >= self.length {
474 None
475 } else {
476 let exit = self.data.get_exit_by_index(self.current_index);
477 self.current_index += 1;
478 exit
479 }
480 }
481}
482
483pub fn top_room(room: RoomName) -> Option<RoomName> {
485 room.checked_add((0, -1))
486}
487
488pub fn right_room(room: RoomName) -> Option<RoomName> {
490 room.checked_add((1, 0))
491}
492
493pub fn bottom_room(room: RoomName) -> Option<RoomName> {
495 room.checked_add((0, 1))
496}
497
498pub fn left_room(room: RoomName) -> Option<RoomName> {
500 room.checked_add((-1, 0))
501}
502
503#[cfg(test)]
504mod test {
505 use super::*;
506
507 #[test]
508 pub fn room_exit_verify_parameter_packing() {
509 let directions = [ExitDirection::Top, ExitDirection::Right, ExitDirection::Bottom, ExitDirection::Left];
512 for start in 0..50 {
513 let max_length = 50 - start;
514 for length in 1..max_length {
515 for direction in directions {
516 let exit = RoomExit::new(start, length, direction);
517
518 let packed = exit.packed();
519
520 let end = start + length - 1; assert_eq!(exit.start(), start, "Start position unpacking mismatch, ({start}, {length}, {direction:?}), Packed: {packed:b}");
523 assert_eq!(exit.len(), length, "Length unpacking mismatch, ({start}, {length}, {direction:?}), Packed: {packed:b}");
524 assert_eq!(exit.end(), end, "End position calculation mismatch, ({start}, {length}, {end}, {direction:?})");
525 assert_eq!(exit.exit_direction(), direction, "Exit direction calculation mismatch, ({start}, {length}, {direction:?}, Packed: {packed:b})");
526 }
527 }
528 }
529 }
530
531 #[test]
532 pub fn room_exit_new_from_packed_matches_original_data() {
533 let directions = [ExitDirection::Top, ExitDirection::Right, ExitDirection::Bottom, ExitDirection::Left];
534 for start in 0..50 {
535 let max_length = 50 - start;
536 for length in 1..max_length {
537 for direction in directions {
538 let exit = RoomExit::new(start, length, direction);
539
540 let packed = exit.packed();
541
542 let new_exit = RoomExit::new_from_packed(packed);
543
544 let end = start + length - 1; assert_eq!(exit.start(), new_exit.start(), "Start position mismatch, ({start}, {length}, {direction:?}), Packed: {packed:b}");
547 assert_eq!(exit.len(), new_exit.len(), "Length mismatch, ({start}, {length}, {direction:?}), Packed: {packed:b}");
548 assert_eq!(exit.end(), new_exit.end(), "End position mismatch, ({start}, {length}, {end}, {direction:?})");
549 assert_eq!(exit.exit_direction(), new_exit.exit_direction(), "Exit direction mismatch, ({start}, {length}, {direction:?}, Packed: {packed:b})");
550 }
551 }
552 }
553 }
554
555 #[test]
556 pub fn room_exit_new_from_packed_sanitizes_invalid_direction_data() {
557 let directions = [ExitDirection::Top, ExitDirection::Right, ExitDirection::Bottom, ExitDirection::Left];
558 let invalid_direction_values: [u16; 4] = [0, 2, 4, 6]; let direction_include_bitmask = 0b111111000111111;
560 for start in 0..50 {
561 let max_length = 50 - start;
562 for length in 1..max_length {
563 for direction in directions {
564 for invalid_direction_val in &invalid_direction_values {
565 let exit = RoomExit::new(start, length, direction);
566
567 let packed = exit.packed();
568
569 let invalid_packed = (packed & direction_include_bitmask) | (invalid_direction_val << RoomExit::EXIT_DIRECTION_OFFSET);
570
571 let new_exit = RoomExit::new_from_packed(invalid_packed);
572
573 let new_packed = new_exit.packed();
574
575 let sanitized_dir_bits = (new_packed & RoomExit::EXIT_DIRECTION_BITMASK) >> RoomExit::EXIT_DIRECTION_OFFSET;
576
577 let end = start + length - 1; assert_eq!(exit.start(), new_exit.start(), "Start position mismatch, ({start}, {length}, {direction:?}), Packed: {new_packed:b}");
580 assert_eq!(exit.len(), new_exit.len(), "Length mismatch, ({start}, {length}, {direction:?}), Packed: {new_packed:b}");
581 assert_eq!(exit.end(), new_exit.end(), "End position mismatch, ({start}, {length}, {end}, {direction:?})");
582 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})");
583 assert_eq!(new_exit.exit_direction(), ExitDirection::Top, "Exit direction not sanitized, ({start}, {length}, {direction:?}, Packed: {new_packed:b})");
584 }
585 }
586 }
587 }
588 }
589
590 #[test]
591 pub fn room_exit_get_exits_from_single_edge_returns_empty_for_all_walls() {
592 let terrain = [Terrain::Wall; 50];
593
594 let exits = RoomExit::get_exits_from_single_edge(&terrain, ExitDirection::Top);
595
596 assert_eq!(exits.len(), 0);
597 }
598
599 #[test]
600 pub fn room_exit_get_exits_from_single_edge_returns_expected_number_of_exits() {
601 let mut terrain = [Terrain::Plain; 50];
603 terrain[0] = Terrain::Wall;
604 terrain[49] = Terrain::Wall;
605
606 let exits = RoomExit::get_exits_from_single_edge(&terrain, ExitDirection::Top);
607
608 assert_eq!(exits.len(), 1);
609
610 let mut terrain = [Terrain::Plain; 50];
612 terrain[0] = Terrain::Wall;
613 terrain[49] = Terrain::Wall;
614
615 for i in 5..10 {
616 terrain[i] = Terrain::Wall;
617 }
618
619 let exits = RoomExit::get_exits_from_single_edge(&terrain, ExitDirection::Top);
620
621 assert_eq!(exits.len(), 2);
622
623 let mut terrain = [Terrain::Plain; 50];
625 terrain[0] = Terrain::Wall;
626 terrain[49] = Terrain::Wall;
627
628 for i in 5..10 {
629 terrain[i] = Terrain::Wall;
630 }
631
632 for i in 23..25 {
633 terrain[i] = Terrain::Wall;
634 }
635
636 let exits = RoomExit::get_exits_from_single_edge(&terrain, ExitDirection::Top);
637
638 assert_eq!(exits.len(), 3);
639 }
640
641 #[test]
642 pub fn room_exits_data_get_exit_by_index_returns_none_for_bad_indices() {
643 let room_name = RoomName::new("W0N0").unwrap();
644
645 let edge = [Terrain::Wall; 50];
648 let terrain = RoomEdgeTerrain::new_from_terrain_slices(&edge, &edge, &edge, &edge).unwrap();
649 let exits_data = RoomExitsData::new_from_compressed_edge_terrain_data(terrain, room_name);
650 assert_eq!(exits_data.get_exit_by_index(0), None, "Exit returned when none exists");
651
652 let edge = [Terrain::Plain; 50];
654 let terrain = RoomEdgeTerrain::new_from_terrain_slices(&edge, &edge, &edge, &edge).unwrap();
655 let exits_data = RoomExitsData::new_from_compressed_edge_terrain_data(terrain, room_name);
656 assert_eq!(exits_data.get_exit_by_index(8), None, "Exit returned for invalid index");
657 }
658
659 #[test]
660 pub fn room_exits_data_get_exit_by_index_returns_some_for_exits_on_4_edges() {
661 let room_name = RoomName::new("W0N0").unwrap();
662
663 let edge = [Terrain::Plain; 50];
664 let terrain = RoomEdgeTerrain::new_from_terrain_slices(&edge, &edge, &edge, &edge).unwrap();
665 let exits_data = RoomExitsData::new_from_compressed_edge_terrain_data(terrain, room_name);
666
667 assert_eq!(1, exits_data.num_top_exits(), "Top exit count mismatch");
668 assert_eq!(1, exits_data.num_right_exits(), "Right exit count mismatch");
669 assert_eq!(1, exits_data.num_bottom_exits(), "Bottom exit count mismatch");
670 assert_eq!(1, exits_data.num_left_exits(), "Left exit count mismatch");
671
672 let exit_opt = exits_data.get_exit_by_index(0);
674 assert!(exit_opt.is_some(), "Exit should exist");
675
676 let exit = exit_opt.unwrap();
677
678 assert_eq!(exit.start(), 1, "Exit start position invalid");
679 assert_eq!(exit.end(), 48, "Exit end position invalid");
680 assert_eq!(exit.len(), 48, "Exit length invalid");
681 assert_eq!(exit.exit_direction(), ExitDirection::Top, "Exit direction invalid");
682
683 let exit_opt = exits_data.get_exit_by_index(1);
685 assert!(exit_opt.is_some(), "Exit should exist");
686
687 let exit = exit_opt.unwrap();
688
689 assert_eq!(exit.start(), 1, "Exit start position invalid");
690 assert_eq!(exit.end(), 48, "Exit end position invalid");
691 assert_eq!(exit.len(), 48, "Exit length invalid");
692 assert_eq!(exit.exit_direction(), ExitDirection::Right, "Exit direction invalid");
693
694 let exit_opt = exits_data.get_exit_by_index(2);
696 assert!(exit_opt.is_some(), "Exit should exist");
697
698 let exit = exit_opt.unwrap();
699
700 assert_eq!(exit.start(), 1, "Exit start position invalid");
701 assert_eq!(exit.end(), 48, "Exit end position invalid");
702 assert_eq!(exit.len(), 48, "Exit length invalid");
703 assert_eq!(exit.exit_direction(), ExitDirection::Bottom, "Exit direction invalid");
704
705 let exit_opt = exits_data.get_exit_by_index(3);
707 assert!(exit_opt.is_some(), "Exit should exist");
708
709 let exit = exit_opt.unwrap();
710
711 assert_eq!(exit.start(), 1, "Exit start position invalid");
712 assert_eq!(exit.end(), 48, "Exit end position invalid");
713 assert_eq!(exit.len(), 48, "Exit length invalid");
714 assert_eq!(exit.exit_direction(), ExitDirection::Left, "Exit direction invalid");
715 }
716
717 #[test]
718 pub fn room_exits_data_get_exit_by_index_returns_some_for_exits_except_top_edge() {
719 let room_name = RoomName::new("W0N0").unwrap();
720
721 let wall_edge = [Terrain::Wall; 50];
722 let edge = [Terrain::Plain; 50];
723 let terrain = RoomEdgeTerrain::new_from_terrain_slices(&wall_edge, &edge, &edge, &edge).unwrap();
724 let exits_data = RoomExitsData::new_from_compressed_edge_terrain_data(terrain, room_name);
725
726 assert_eq!(0, exits_data.num_top_exits(), "Top exit count mismatch");
727 assert_eq!(1, exits_data.num_right_exits(), "Right exit count mismatch");
728 assert_eq!(1, exits_data.num_bottom_exits(), "Bottom exit count mismatch");
729 assert_eq!(1, exits_data.num_left_exits(), "Left exit count mismatch");
730
731 let exit_opt = exits_data.get_exit_by_index(0);
733 assert!(exit_opt.is_some(), "Exit should exist");
734
735 let exit = exit_opt.unwrap();
736
737 assert_eq!(exit.start(), 1, "Exit start position invalid");
738 assert_eq!(exit.end(), 48, "Exit end position invalid");
739 assert_eq!(exit.len(), 48, "Exit length invalid");
740 assert_eq!(exit.exit_direction(), ExitDirection::Right, "Exit direction invalid");
741
742 let exit_opt = exits_data.get_exit_by_index(1);
744 assert!(exit_opt.is_some(), "Exit should exist");
745
746 let exit = exit_opt.unwrap();
747
748 assert_eq!(exit.start(), 1, "Exit start position invalid");
749 assert_eq!(exit.end(), 48, "Exit end position invalid");
750 assert_eq!(exit.len(), 48, "Exit length invalid");
751 assert_eq!(exit.exit_direction(), ExitDirection::Bottom, "Exit direction invalid");
752
753 let exit_opt = exits_data.get_exit_by_index(2);
755 assert!(exit_opt.is_some(), "Exit should exist");
756
757 let exit = exit_opt.unwrap();
758
759 assert_eq!(exit.start(), 1, "Exit start position invalid");
760 assert_eq!(exit.end(), 48, "Exit end position invalid");
761 assert_eq!(exit.len(), 48, "Exit length invalid");
762 assert_eq!(exit.exit_direction(), ExitDirection::Left, "Exit direction invalid");
763 }
764
765 #[test]
766 pub fn room_exits_data_get_exit_by_index_returns_some_for_exits_except_right_edge() {
767 let room_name = RoomName::new("W0N0").unwrap();
768
769 let wall_edge = [Terrain::Wall; 50];
770 let edge = [Terrain::Plain; 50];
771 let terrain = RoomEdgeTerrain::new_from_terrain_slices(&edge, &wall_edge, &edge, &edge).unwrap();
772 let exits_data = RoomExitsData::new_from_compressed_edge_terrain_data(terrain, room_name);
773
774 assert_eq!(1, exits_data.num_top_exits(), "Top exit count mismatch");
775 assert_eq!(0, exits_data.num_right_exits(), "Right exit count mismatch");
776 assert_eq!(1, exits_data.num_bottom_exits(), "Bottom exit count mismatch");
777 assert_eq!(1, exits_data.num_left_exits(), "Left exit count mismatch");
778
779 let exit_opt = exits_data.get_exit_by_index(0);
781 assert!(exit_opt.is_some(), "Exit should exist");
782
783 let exit = exit_opt.unwrap();
784
785 assert_eq!(exit.start(), 1, "Exit start position invalid");
786 assert_eq!(exit.end(), 48, "Exit end position invalid");
787 assert_eq!(exit.len(), 48, "Exit length invalid");
788 assert_eq!(exit.exit_direction(), ExitDirection::Top, "Exit direction invalid");
789
790 let exit_opt = exits_data.get_exit_by_index(1);
792 assert!(exit_opt.is_some(), "Exit should exist");
793
794 let exit = exit_opt.unwrap();
795
796 assert_eq!(exit.start(), 1, "Exit start position invalid");
797 assert_eq!(exit.end(), 48, "Exit end position invalid");
798 assert_eq!(exit.len(), 48, "Exit length invalid");
799 assert_eq!(exit.exit_direction(), ExitDirection::Bottom, "Exit direction invalid");
800
801 let exit_opt = exits_data.get_exit_by_index(2);
803 assert!(exit_opt.is_some(), "Exit should exist");
804
805 let exit = exit_opt.unwrap();
806
807 assert_eq!(exit.start(), 1, "Exit start position invalid");
808 assert_eq!(exit.end(), 48, "Exit end position invalid");
809 assert_eq!(exit.len(), 48, "Exit length invalid");
810 assert_eq!(exit.exit_direction(), ExitDirection::Left, "Exit direction invalid");
811 }
812
813 #[test]
815 pub fn room_exits_data_get_exit_by_index_returns_some_for_exits_except_bottom_edge() {
816 let room_name = RoomName::new("W0N0").unwrap();
817
818 let wall_edge = [Terrain::Wall; 50];
819 let edge = [Terrain::Plain; 50];
820 let terrain = RoomEdgeTerrain::new_from_terrain_slices(&edge, &edge, &wall_edge, &edge).unwrap();
821 let exits_data = RoomExitsData::new_from_compressed_edge_terrain_data(terrain, room_name);
822
823 assert_eq!(1, exits_data.num_top_exits(), "Top exit count mismatch");
824 assert_eq!(1, exits_data.num_right_exits(), "Right exit count mismatch");
825 assert_eq!(0, exits_data.num_bottom_exits(), "Bottom exit count mismatch");
826 assert_eq!(1, exits_data.num_left_exits(), "Left exit count mismatch");
827
828 let exit_opt = exits_data.get_exit_by_index(0);
830 assert!(exit_opt.is_some(), "Exit should exist");
831
832 let exit = exit_opt.unwrap();
833
834 assert_eq!(exit.start(), 1, "Exit start position invalid");
835 assert_eq!(exit.end(), 48, "Exit end position invalid");
836 assert_eq!(exit.len(), 48, "Exit length invalid");
837 assert_eq!(exit.exit_direction(), ExitDirection::Top, "Exit direction invalid");
838
839 let exit_opt = exits_data.get_exit_by_index(1);
841 assert!(exit_opt.is_some(), "Exit should exist");
842
843 let exit = exit_opt.unwrap();
844
845 assert_eq!(exit.start(), 1, "Exit start position invalid");
846 assert_eq!(exit.end(), 48, "Exit end position invalid");
847 assert_eq!(exit.len(), 48, "Exit length invalid");
848 assert_eq!(exit.exit_direction(), ExitDirection::Right, "Exit direction invalid");
849
850 let exit_opt = exits_data.get_exit_by_index(2);
852 assert!(exit_opt.is_some(), "Exit should exist");
853
854 let exit = exit_opt.unwrap();
855
856 assert_eq!(exit.start(), 1, "Exit start position invalid");
857 assert_eq!(exit.end(), 48, "Exit end position invalid");
858 assert_eq!(exit.len(), 48, "Exit length invalid");
859 assert_eq!(exit.exit_direction(), ExitDirection::Left, "Exit direction invalid");
860 }
861
862 #[test]
864 pub fn room_exits_data_get_exit_by_index_returns_some_for_exits_except_left_edge() {
865 let room_name = RoomName::new("W0N0").unwrap();
866
867 let wall_edge = [Terrain::Wall; 50];
868 let edge = [Terrain::Plain; 50];
869 let terrain = RoomEdgeTerrain::new_from_terrain_slices(&edge, &edge, &edge, &wall_edge).unwrap();
870 let exits_data = RoomExitsData::new_from_compressed_edge_terrain_data(terrain, room_name);
871
872 assert_eq!(1, exits_data.num_top_exits(), "Top exit count mismatch");
873 assert_eq!(1, exits_data.num_right_exits(), "Right exit count mismatch");
874 assert_eq!(1, exits_data.num_bottom_exits(), "Bottom exit count mismatch");
875 assert_eq!(0, exits_data.num_left_exits(), "Left exit count mismatch");
876
877 let exit_opt = exits_data.get_exit_by_index(0);
879 assert!(exit_opt.is_some(), "Exit should exist");
880
881 let exit = exit_opt.unwrap();
882
883 assert_eq!(exit.start(), 1, "Exit start position invalid");
884 assert_eq!(exit.end(), 48, "Exit end position invalid");
885 assert_eq!(exit.len(), 48, "Exit length invalid");
886 assert_eq!(exit.exit_direction(), ExitDirection::Top, "Exit direction invalid");
887
888 let exit_opt = exits_data.get_exit_by_index(1);
890 assert!(exit_opt.is_some(), "Exit should exist");
891
892 let exit = exit_opt.unwrap();
893
894 assert_eq!(exit.start(), 1, "Exit start position invalid");
895 assert_eq!(exit.end(), 48, "Exit end position invalid");
896 assert_eq!(exit.len(), 48, "Exit length invalid");
897 assert_eq!(exit.exit_direction(), ExitDirection::Right, "Exit direction invalid");
898
899 let exit_opt = exits_data.get_exit_by_index(2);
901 assert!(exit_opt.is_some(), "Exit should exist");
902
903 let exit = exit_opt.unwrap();
904
905 assert_eq!(exit.start(), 1, "Exit start position invalid");
906 assert_eq!(exit.end(), 48, "Exit end position invalid");
907 assert_eq!(exit.len(), 48, "Exit length invalid");
908 assert_eq!(exit.exit_direction(), ExitDirection::Bottom, "Exit direction invalid");
909 }
910
911 #[test]
913 pub fn room_exits_data_get_exit_by_index_returns_some_for_exits_except_top_or_right() {
914 let room_name = RoomName::new("W0N0").unwrap();
915
916 let wall_edge = [Terrain::Wall; 50];
917 let edge = [Terrain::Plain; 50];
918 let terrain = RoomEdgeTerrain::new_from_terrain_slices(&wall_edge, &wall_edge, &edge, &edge).unwrap();
919 let exits_data = RoomExitsData::new_from_compressed_edge_terrain_data(terrain, room_name);
920
921 assert_eq!(0, exits_data.num_top_exits(), "Top exit count mismatch");
922 assert_eq!(0, exits_data.num_right_exits(), "Right exit count mismatch");
923 assert_eq!(1, exits_data.num_bottom_exits(), "Bottom exit count mismatch");
924 assert_eq!(1, exits_data.num_left_exits(), "Left exit count mismatch");
925
926 let exit_opt = exits_data.get_exit_by_index(0);
928 assert!(exit_opt.is_some(), "Exit should exist");
929
930 let exit = exit_opt.unwrap();
931
932 assert_eq!(exit.start(), 1, "Exit start position invalid");
933 assert_eq!(exit.end(), 48, "Exit end position invalid");
934 assert_eq!(exit.len(), 48, "Exit length invalid");
935 assert_eq!(exit.exit_direction(), ExitDirection::Bottom, "Exit direction invalid");
936
937 let exit_opt = exits_data.get_exit_by_index(1);
939 assert!(exit_opt.is_some(), "Exit should exist");
940
941 let exit = exit_opt.unwrap();
942
943 assert_eq!(exit.start(), 1, "Exit start position invalid");
944 assert_eq!(exit.end(), 48, "Exit end position invalid");
945 assert_eq!(exit.len(), 48, "Exit length invalid");
946 assert_eq!(exit.exit_direction(), ExitDirection::Left, "Exit direction invalid");
947 }
948
949 #[test]
951 pub fn room_exits_data_get_exit_by_index_returns_some_for_exits_except_top_or_right_or_bottom() {
952 let room_name = RoomName::new("W0N0").unwrap();
953
954 let wall_edge = [Terrain::Wall; 50];
955 let edge = [Terrain::Plain; 50];
956 let terrain = RoomEdgeTerrain::new_from_terrain_slices(&wall_edge, &wall_edge, &wall_edge, &edge).unwrap();
957 let exits_data = RoomExitsData::new_from_compressed_edge_terrain_data(terrain, room_name);
958
959 assert_eq!(0, exits_data.num_top_exits(), "Top exit count mismatch");
960 assert_eq!(0, exits_data.num_right_exits(), "Right exit count mismatch");
961 assert_eq!(0, exits_data.num_bottom_exits(), "Bottom exit count mismatch");
962 assert_eq!(1, exits_data.num_left_exits(), "Left exit count mismatch");
963
964 let exit_opt = exits_data.get_exit_by_index(0);
966 assert!(exit_opt.is_some(), "Exit should exist");
967
968 let exit = exit_opt.unwrap();
969
970 assert_eq!(exit.start(), 1, "Exit start position invalid");
971 assert_eq!(exit.end(), 48, "Exit end position invalid");
972 assert_eq!(exit.len(), 48, "Exit length invalid");
973 assert_eq!(exit.exit_direction(), ExitDirection::Left, "Exit direction invalid");
974 }
975}