1use std::iter::{FusedIterator, IntoIterator, Iterator};
2
3use bitvec::prelude::*;
4
5use crate::{Point2D, Size2D};
6
7#[derive(Debug, Clone, PartialEq, Eq)]
54pub struct BoardArea {
55 board_size: Size2D,
56 data: BitVec,
57}
58
59impl BoardArea {
60 pub fn new(for_board_size: Size2D) -> Self {
62 Self {
63 board_size: for_board_size,
64 data: BitVec::repeat(false, for_board_size.size()),
65 }
66 }
67
68 pub fn from_points<I: IntoIterator<Item = Point2D>>(for_board_size: Size2D, points: I) -> Self {
77 let mut retv = Self::new(for_board_size);
78 retv.extend(points);
79 retv
80 }
81
82 pub fn board_size(&self) -> Size2D { self.board_size }
86
87 pub fn len(&self) -> usize { self.data.count_ones() }
89
90 pub fn is_empty(&self) -> bool { self.len() == 0 }
92
93 pub fn clear(&mut self) { self.data.fill(false); }
96
97 pub fn contains(&self, value: Point2D) -> bool {
102 match self.bit_idx(value) {
103 Some(idx) => self.data.get(idx).as_deref() == Some(&true),
104 None => false,
105 }
106 }
107
108 pub fn add_point(&mut self, value: Point2D) -> bool {
131 match self.bit_idx(value) {
132 Some(idx) => !self.data.replace(idx, true),
133 None => false,
134 }
135 }
136
137 pub fn remove_point(&mut self, value: Point2D) -> bool {
142 match self.bit_idx(value) {
143 Some(idx) => self.data.replace(idx, false),
144 None => false,
145 }
146 }
147
148 pub fn bounds(&self) -> Option<(Point2D, Point2D)> {
172 let mut ones = self.data.iter_ones();
173
174 let first = Point2D::new_from_idx(ones.next()?, &self.board_size);
176 let mut top_left = first;
177 let mut bottom_right = first;
178
179 for idx in ones {
180 let pos = Point2D::new_from_idx(idx, &self.board_size);
181 top_left.x = top_left.x.min(pos.x);
182 bottom_right.x = bottom_right.x.max(pos.x);
183 bottom_right.y = pos.y;
186 }
187
188 Some((top_left, bottom_right))
189 }
190
191 pub fn min_x(&self) -> Option<usize> { self.bounds().map(|(top_left, _)| top_left.x) }
195
196 pub fn min_y(&self) -> Option<usize> { self.bounds().map(|(top_left, _)| top_left.y) }
200
201 pub fn max_x(&self) -> Option<usize> { self.bounds().map(|(_, bottom_right)| bottom_right.x) }
205
206 pub fn max_y(&self) -> Option<usize> { self.bounds().map(|(_, bottom_right)| bottom_right.y) }
210
211 pub fn union(&self, other: &Self) -> Option<Self> {
234 let mut retv = self.cloned_for_op_with(other)?;
235 retv.data |= &other.data;
236 Some(retv)
237 }
238
239 pub fn intersection(&self, other: &Self) -> Option<Self> {
243 let mut retv = self.cloned_for_op_with(other)?;
244 retv.data &= &other.data;
245 Some(retv)
246 }
247
248 pub fn difference(&self, other: &Self) -> Option<Self> {
252 let mut retv = self.cloned_for_op_with(other)?;
253 retv.data &= !other.data.clone();
255 Some(retv)
256 }
257
258 pub fn symmetric_difference(&self, other: &Self) -> Option<Self> {
262 let mut retv = self.cloned_for_op_with(other)?;
263 retv.data ^= &other.data;
264 Some(retv)
265 }
266
267 pub fn is_subset(&self, other: &Self) -> bool {
273 self.board_size == other.board_size
274 && self
275 .data
276 .iter_ones()
277 .all(|idx| other.data.get(idx).as_deref() == Some(&true))
278 }
279
280 pub fn is_superset(&self, other: &Self) -> bool { other.is_subset(self) }
285
286 pub fn is_disjoint(&self, other: &Self) -> bool {
293 self.board_size != other.board_size
294 || !self
295 .data
296 .iter_ones()
297 .any(|idx| other.data.get(idx).as_deref() == Some(&true))
298 }
299
300 pub fn iter(&self) -> BoardAreaIter<'_> { BoardAreaIter { idx: 0, obj: self } }
302
303 fn bit_idx(&self, value: Point2D) -> Option<usize> {
305 value
307 .is_on_board(self.board_size)
308 .then(|| value.idx(self.board_size))
309 }
310
311 fn cloned_for_op_with(&self, other: &Self) -> Option<Self> {
316 (self.board_size == other.board_size).then(|| self.clone())
317 }
318}
319
320impl<'a> Extend<&'a Point2D> for BoardArea {
323 fn extend<T: IntoIterator<Item = &'a Point2D>>(&mut self, iter: T) {
324 for pos in iter {
325 self.add_point(*pos);
326 }
327 }
328}
329
330impl Extend<Point2D> for BoardArea {
331 fn extend<T: IntoIterator<Item = Point2D>>(&mut self, iter: T) {
332 for pos in iter {
333 self.add_point(pos);
334 }
335 }
336}
337
338impl<'a> IntoIterator for &'a BoardArea {
339 type Item = Point2D;
340 type IntoIter = BoardAreaIter<'a>;
341
342 fn into_iter(self) -> Self::IntoIter { self.iter() }
343}
344
345impl IntoIterator for BoardArea {
346 type Item = Point2D;
347 type IntoIter = BoardAreaIntoIter;
348
349 fn into_iter(self) -> Self::IntoIter { BoardAreaIntoIter { idx: 0, obj: self } }
350}
351
352#[derive(Debug, Clone)]
358pub struct BoardAreaIter<'a> {
359 obj: &'a BoardArea,
360 idx: usize,
361}
362
363impl Iterator for BoardAreaIter<'_> {
364 type Item = Point2D;
365
366 fn next(&mut self) -> Option<Self::Item> {
367 let found = self.idx + self.obj.data.get(self.idx..)?.first_one()?;
368 self.idx = found + 1;
369 Some(Point2D::new_from_idx(found, &self.obj.board_size))
370 }
371
372 fn size_hint(&self) -> (usize, Option<usize>) {
373 let remaining = self
374 .obj
375 .data
376 .get(self.idx..)
377 .map_or(0, bitvec::slice::BitSlice::count_ones);
378 (remaining, Some(remaining))
379 }
380}
381
382impl ExactSizeIterator for BoardAreaIter<'_> {}
383
384impl FusedIterator for BoardAreaIter<'_> {}
385
386#[derive(Debug, Clone)]
392pub struct BoardAreaIntoIter {
393 obj: BoardArea,
394 idx: usize,
395}
396
397impl Iterator for BoardAreaIntoIter {
398 type Item = Point2D;
399
400 fn next(&mut self) -> Option<Self::Item> {
401 let found = self.idx + self.obj.data.get(self.idx..)?.first_one()?;
402 self.idx = found + 1;
403 Some(Point2D::new_from_idx(found, &self.obj.board_size))
404 }
405
406 fn size_hint(&self) -> (usize, Option<usize>) {
407 let remaining = self
408 .obj
409 .data
410 .get(self.idx..)
411 .map_or(0, bitvec::slice::BitSlice::count_ones);
412 (remaining, Some(remaining))
413 }
414}
415
416impl ExactSizeIterator for BoardAreaIntoIter {}
417
418impl FusedIterator for BoardAreaIntoIter {}
419
420#[cfg(test)]
421mod tests {
422 use super::*;
423
424 fn board_size() -> Size2D { Size2D::new(10, 10) }
425
426 #[test]
427 fn it_can_check_if_it_is_empty() {
428 let mut area = BoardArea::new(board_size());
429
430 assert!(area.is_empty());
431 assert_eq!(area.len(), 0);
432
433 area.add_point(Point2D::new(5, 5));
434 assert!(!area.is_empty());
435 assert_eq!(area.len(), 1);
436 }
437
438 #[test]
439 fn it_can_be_cleared_while_keeping_board_size() {
440 let mut area = BoardArea::from_points(board_size(), [Point2D::new(5, 5)]);
441
442 area.clear();
443 assert!(area.is_empty());
444 assert_eq!(area.board_size(), board_size());
445 }
446
447 #[test]
448 fn it_can_add_points() {
449 let mut area = BoardArea::new(board_size());
450 let pos = Point2D::new(5, 5);
451
452 assert!(area.add_point(pos));
453 assert!(area.contains(pos));
454
455 assert!(!area.add_point(pos));
457 assert_eq!(area.len(), 1);
458 }
459
460 #[test]
461 fn it_can_remove_points() {
462 let mut area = BoardArea::new(board_size());
463 let pos = Point2D::new(5, 5);
464
465 area.add_point(pos);
466 assert!(area.contains(pos));
467
468 assert!(area.remove_point(pos));
469 assert!(!area.contains(pos));
470
471 assert!(!area.remove_point(pos));
473 }
474
475 #[test]
476 fn it_can_be_extended_from_points_and_from_references() {
477 let mut area = BoardArea::new(board_size());
478
479 area.extend([Point2D::new(1, 1), Point2D::new(2, 2)]);
480
481 let more = [Point2D::new(3, 3)];
482 area.extend(more.iter());
483
484 assert_eq!(
485 Vec::from_iter(&area),
486 vec![Point2D::new(1, 1), Point2D::new(2, 2), Point2D::new(3, 3)]
487 );
488 }
489
490 #[test]
491 fn it_can_iterate_over_points() {
492 let mut area = BoardArea::new(board_size());
493 let pos1 = Point2D::new(5, 5);
494 let pos2 = Point2D::new(6, 6);
495
496 area.add_point(pos1);
497 area.add_point(pos2);
498
499 let mut data: Vec<Point2D> = vec![];
500 for pos in &area {
501 data.push(pos);
502 }
503 assert_eq!(data, vec![pos1, pos2]);
504
505 let mut data: Vec<Point2D> = vec![];
506 for pos in &area {
507 data.push(pos);
508 }
509 assert_eq!(data, vec![pos1, pos2]);
510
511 let data: Vec<Point2D> = Vec::from_iter(area);
512 assert_eq!(data, vec![pos1, pos2]);
513 }
514
515 #[test]
516 fn it_iterates_in_row_major_order() {
517 let area = BoardArea::from_points(
518 board_size(),
519 [Point2D::new(9, 0), Point2D::new(0, 9), Point2D::new(4, 4)],
520 );
521
522 assert_eq!(
523 Vec::from_iter(&area),
524 vec![Point2D::new(9, 0), Point2D::new(4, 4), Point2D::new(0, 9)]
525 );
526 }
527
528 #[test]
529 fn it_reports_count_of_remaining_points_while_iterating() {
530 let area = BoardArea::from_points(board_size(), [Point2D::new(5, 5), Point2D::new(6, 6)]);
531
532 let mut iter = area.iter();
533 assert_eq!(iter.len(), 2);
534 assert_eq!(iter.size_hint(), (2, Some(2)));
535
536 iter.next();
537 assert_eq!(iter.len(), 1);
538 assert_eq!(iter.size_hint(), (1, Some(1)));
539
540 iter.next();
541 assert_eq!(iter.len(), 0);
542 assert_eq!(iter.size_hint(), (0, Some(0)));
543
544 assert!(iter.next().is_none());
546 assert_eq!(iter.len(), 0);
547 }
548
549 #[test]
550 fn owning_iterator_reports_count_of_remaining_points_while_iterating() {
551 let area = BoardArea::from_points(board_size(), [Point2D::new(5, 5), Point2D::new(6, 6)]);
552
553 let mut iter = area.into_iter();
554 assert_eq!(iter.len(), 2);
555 assert_eq!(iter.size_hint(), (2, Some(2)));
556
557 iter.next();
558 assert_eq!(iter.len(), 1);
559 assert_eq!(iter.size_hint(), (1, Some(1)));
560
561 iter.next();
562 assert_eq!(iter.len(), 0);
563 assert_eq!(iter.size_hint(), (0, Some(0)));
564
565 assert!(iter.next().is_none());
566 assert_eq!(iter.len(), 0);
567 }
568
569 #[test]
570 fn when_point_is_off_board_it_is_not_a_member() {
571 let mut area = BoardArea::new(board_size());
572
573 let off_board_x = Point2D::new(15, 0);
575 assert!(!area.add_point(off_board_x));
576 assert!(!area.contains(off_board_x));
577 assert!(!area.contains(Point2D::new(5, 1)));
578 assert!(area.is_empty());
579
580 let off_board_y = Point2D::new(0, 15);
582 assert!(!area.add_point(off_board_y));
583 assert!(!area.contains(off_board_y));
584 assert!(area.is_empty());
585
586 assert!(!area.remove_point(off_board_x));
587 assert!(!area.remove_point(off_board_y));
588 }
589
590 #[test]
591 fn when_board_has_no_tiles_area_is_always_empty() {
592 for size in [Size2D::new(0, 0), Size2D::new(0, 5), Size2D::new(5, 0)] {
593 let mut area = BoardArea::new(size);
594
595 assert!(area.is_empty());
596 assert!(!area.add_point(Point2D::origin()));
597 assert!(!area.contains(Point2D::origin()));
598 assert_eq!(area.bounds(), None);
599 assert!(area.iter().next().is_none());
600 }
601 }
602
603 #[test]
604 fn when_area_is_empty_it_has_no_bounds() {
605 let area = BoardArea::new(board_size());
606
607 assert_eq!(area.bounds(), None);
608 assert_eq!(area.min_x(), None);
609 assert_eq!(area.min_y(), None);
610 assert_eq!(area.max_x(), None);
611 assert_eq!(area.max_y(), None);
612 }
613
614 #[test]
615 fn it_calculates_bounds_of_single_point_area() {
616 let area = BoardArea::from_points(board_size(), [Point2D::new(3, 7)]);
617
618 assert_eq!(area.bounds(), Some((Point2D::new(3, 7), Point2D::new(3, 7))));
619 assert_eq!(area.min_x(), Some(3));
620 assert_eq!(area.max_x(), Some(3));
621 assert_eq!(area.min_y(), Some(7));
622 assert_eq!(area.max_y(), Some(7));
623 }
624
625 #[test]
626 fn it_calculates_bounds_when_extremes_are_in_different_rows() {
627 let area = BoardArea::from_points(
630 board_size(),
631 [Point2D::new(7, 2), Point2D::new(3, 4), Point2D::new(5, 8)],
632 );
633
634 assert_eq!(area.bounds(), Some((Point2D::new(3, 2), Point2D::new(7, 8))));
635 assert_eq!(area.min_x(), Some(3));
636 assert_eq!(area.max_x(), Some(7));
637 assert_eq!(area.min_y(), Some(2));
638 assert_eq!(area.max_y(), Some(8));
639 }
640
641 #[test]
642 fn it_calculates_set_operations() {
643 let size = board_size();
644 let left = BoardArea::from_points(size, [Point2D::new(1, 1), Point2D::new(2, 2)]);
645 let right = BoardArea::from_points(size, [Point2D::new(2, 2), Point2D::new(3, 3)]);
646
647 assert_eq!(
648 left.union(&right),
649 Some(BoardArea::from_points(
650 size,
651 [Point2D::new(1, 1), Point2D::new(2, 2), Point2D::new(3, 3)]
652 ))
653 );
654 assert_eq!(
655 left.intersection(&right),
656 Some(BoardArea::from_points(size, [Point2D::new(2, 2)]))
657 );
658 assert_eq!(
659 left.difference(&right),
660 Some(BoardArea::from_points(size, [Point2D::new(1, 1)]))
661 );
662 assert_eq!(
663 left.symmetric_difference(&right),
664 Some(BoardArea::from_points(size, [Point2D::new(1, 1), Point2D::new(3, 3)]))
665 );
666 }
667
668 #[test]
669 fn it_calculates_set_operations_on_disjoint_and_equal_areas() {
670 let size = board_size();
671 let left = BoardArea::from_points(size, [Point2D::new(1, 1)]);
672 let right = BoardArea::from_points(size, [Point2D::new(9, 9)]);
673 let empty = BoardArea::new(size);
674
675 assert_eq!(left.intersection(&right), Some(empty.clone()));
676 assert_eq!(left.difference(&right), Some(left.clone()));
677 assert_eq!(left.symmetric_difference(&right), left.union(&right));
678
679 assert_eq!(left.union(&left), Some(left.clone()));
680 assert_eq!(left.intersection(&left), Some(left.clone()));
681 assert_eq!(left.difference(&left), Some(empty.clone()));
682 assert_eq!(left.symmetric_difference(&left), Some(empty));
683 }
684
685 #[test]
686 fn it_checks_subset_superset_and_disjoint() {
687 let size = board_size();
688 let whole = BoardArea::from_points(
689 size,
690 [Point2D::new(1, 1), Point2D::new(2, 2), Point2D::new(3, 3)],
691 );
692 let part = BoardArea::from_points(size, [Point2D::new(2, 2)]);
693 let elsewhere = BoardArea::from_points(size, [Point2D::new(9, 9)]);
694
695 assert!(part.is_subset(&whole));
696 assert!(whole.is_superset(&part));
697 assert!(!whole.is_subset(&part));
698 assert!(!part.is_superset(&whole));
699
700 assert!(whole.is_subset(&whole));
702 assert!(whole.is_superset(&whole));
703
704 assert!(!part.is_disjoint(&whole));
705 assert!(elsewhere.is_disjoint(&whole));
706 assert!(whole.is_disjoint(&elsewhere));
707 }
708
709 #[test]
710 fn it_treats_empty_area_as_subset_of_every_area_of_same_board_size() {
711 let size = board_size();
712 let empty = BoardArea::new(size);
713 let area = BoardArea::from_points(size, [Point2D::new(1, 1)]);
714
715 assert!(empty.is_subset(&area));
716 assert!(empty.is_subset(&empty));
717 assert!(area.is_superset(&empty));
718 assert!(empty.is_disjoint(&area));
719 assert!(empty.is_disjoint(&empty));
720 }
721
722 #[test]
723 fn when_board_sizes_differ_set_operations_return_none() {
724 let left = BoardArea::from_points(board_size(), [Point2D::new(1, 1)]);
725 let right = BoardArea::from_points(Size2D::new(5, 5), [Point2D::new(1, 1)]);
726
727 assert_eq!(left.union(&right), None);
728 assert_eq!(left.intersection(&right), None);
729 assert_eq!(left.difference(&right), None);
730 assert_eq!(left.symmetric_difference(&right), None);
731
732 assert_eq!(right.union(&left), None);
733 assert_eq!(right.intersection(&left), None);
734 assert_eq!(right.difference(&left), None);
735 assert_eq!(right.symmetric_difference(&left), None);
736 }
737
738 #[test]
739 fn when_board_sizes_differ_areas_are_not_comparable() {
740 let left = BoardArea::from_points(board_size(), [Point2D::new(1, 1)]);
741 let right = BoardArea::from_points(Size2D::new(5, 5), [Point2D::new(1, 1)]);
742
743 assert!(!left.is_subset(&right));
745 assert!(!left.is_superset(&right));
746 assert!(left.is_disjoint(&right));
747
748 let empty = BoardArea::new(Size2D::new(5, 5));
750 assert!(!empty.is_subset(&left));
751 assert!(!left.is_superset(&empty));
752
753 let other_empty = BoardArea::new(board_size());
756 assert!(!empty.is_subset(&other_empty));
757 assert!(!empty.is_superset(&other_empty));
758 assert!(empty.is_disjoint(&other_empty));
759 }
760}