1use std::error::Error;
2use std::ops::Not;
3use std::{fmt, iter};
4
5#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
7pub enum Player {
8 X,
10 O,
12}
13
14impl fmt::Display for Player {
15 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
17 match *self {
18 Self::X => write!(f, "X"),
19 Self::O => write!(f, "O"),
20 }
21 }
22}
23
24impl Not for Player {
25 type Output = Self;
26
27 fn not(self) -> Self::Output {
28 match self {
29 Self::X => Self::O,
30 Self::O => Self::X,
31 }
32 }
33}
34
35#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
37pub enum Space {
38 #[default]
40 Empty,
41 Stone(Player),
43}
44
45impl fmt::Display for Space {
46 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48 match *self {
49 Self::Empty => write!(f, " "),
50 Self::Stone(player) => write!(f, "{player}"),
51 }
52 }
53}
54
55impl From<Option<Player>> for Space {
56 fn from(player: Option<Player>) -> Self {
58 player.map_or(Self::Empty, Self::Stone)
59 }
60}
61
62impl From<Space> for Option<Player> {
63 fn from(space: Space) -> Self {
66 match space {
67 Space::Empty => None,
68 Space::Stone(player) => Some(player),
69 }
70 }
71}
72
73#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
75#[non_exhaustive]
76pub enum PlaceError {
77 Occupied {
79 player: Player,
81 },
82 OutOfBounds {
84 row: usize,
86 column: usize,
88 },
89}
90
91impl fmt::Display for PlaceError {
92 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93 match self {
94 Self::Occupied { player } => {
95 write!(f, "already occupied by {player}")
96 }
97 Self::OutOfBounds { row, column } => {
98 write!(f, "out of bounds (row {row}, column {column})")
99 }
100 }
101 }
102}
103
104impl Error for PlaceError {}
105
106#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
115pub struct MnkBoard<const R: usize, const C: usize> {
116 row_array: [[Space; C]; R],
117}
118
119impl<const R: usize, const C: usize> MnkBoard<R, C> {
120 #[must_use]
122 pub const fn new() -> Self {
123 Self {
124 row_array: [[Space::Empty; C]; R],
125 }
126 }
127
128 #[must_use]
130 pub fn full(&self) -> bool {
131 self.row_array
132 .iter()
133 .all(|row| row.iter().all(|space| space != &Space::Empty))
134 }
135
136 pub fn place(&mut self, player: Player, row: usize, column: usize) -> Result<(), PlaceError> {
146 let location = self
147 .row_array
148 .get_mut(row)
149 .and_then(|row| row.get_mut(column));
150 location.map_or(
151 Err(PlaceError::OutOfBounds { row, column }),
152 |space| match space {
153 Space::Stone(player) => Err(PlaceError::Occupied { player: *player }),
154 Space::Empty => {
155 *space = Space::Stone(player);
156 Ok(())
157 }
158 },
159 )
160 }
161
162 pub unsafe fn place_unchecked(&mut self, player: Player, row: usize, column: usize) {
171 let location;
172 unsafe {
173 location = self
174 .row_array
175 .get_unchecked_mut(row)
176 .get_unchecked_mut(column);
177 }
178 *location = Space::Stone(player);
179 }
180
181 #[must_use]
185 pub fn get(&self, row: usize, column: usize) -> Option<&Space> {
186 self.row_array.get(row).and_then(|row| row.get(column))
187 }
188
189 #[must_use]
197 pub unsafe fn get_unchecked(&self, row: usize, column: usize) -> &Space {
198 unsafe { self.row_array.get_unchecked(row).get_unchecked(column) }
199 }
200
201 pub(crate) fn coords_to_spaces(
207 &self,
208 coords: impl Iterator<Item = (usize, usize)>,
209 ) -> impl Iterator<Item = &'_ Space> {
210 coords.map(move |(r, c)| &self.row_array[r][c])
211 }
212
213 pub(crate) fn rows(&self) -> impl Iterator<Item = impl Iterator<Item = &'_ Space>> {
215 self.row_array.iter().map(|row| row.iter())
216 }
217
218 pub(crate) fn columns(&self) -> impl Iterator<Item = impl Iterator<Item = &'_ Space>> {
220 (0..C).map(move |c| self.row_array.iter().map(move |row| &row[c]))
221 }
222
223 pub(crate) fn top_right_diagonals(
227 &self,
228 min_length: usize,
229 ) -> impl Iterator<Item = impl Iterator<Item = &'_ Space>> {
230 (0..=(C - min_length))
231 .map(move |left_col| self.coords_to_spaces(iter::zip(0..R, left_col..C)))
232 }
233
234 pub(crate) fn left_down_diagonals(
239 &self,
240 min_length: usize,
241 ) -> impl Iterator<Item = impl Iterator<Item = &'_ Space>> {
242 (1..=(R - min_length))
243 .map(move |top_row| self.coords_to_spaces(iter::zip(top_row..R, 0..C)))
244 }
245
246 pub(crate) fn top_left_diagonals(
250 &self,
251 min_length: usize,
252 ) -> impl Iterator<Item = impl Iterator<Item = &'_ Space>> {
253 ((min_length - 1)..C)
254 .map(move |last_col| self.coords_to_spaces(iter::zip(0..R, (0..=last_col).rev())))
255 }
256
257 pub(crate) fn right_down_diagonals(
262 &self,
263 min_length: usize,
264 ) -> impl Iterator<Item = impl Iterator<Item = &'_ Space>> {
265 (1..=(R - min_length))
266 .map(move |last_row| self.coords_to_spaces(iter::zip(last_row..R, (0..C).rev())))
267 }
268}
269
270impl<const R: usize, const C: usize> Default for MnkBoard<R, C> {
271 fn default() -> Self {
273 Self::new()
274 }
275}
276
277impl<const R: usize, const C: usize> From<[[Space; C]; R]> for MnkBoard<R, C> {
278 fn from(rows: [[Space; C]; R]) -> Self {
280 Self { row_array: rows }
281 }
282}
283
284impl<const R: usize, const C: usize> From<MnkBoard<R, C>> for [[Space; C]; R] {
285 fn from(game: MnkBoard<R, C>) -> Self {
287 game.row_array
288 }
289}
290
291impl<const R: usize, const C: usize> fmt::Display for MnkBoard<R, C> {
292 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
293 let border = "+-".repeat(C) + "+";
294 let vertical_sep = "\n".to_owned() + &border + "\n";
295 let middle_rows = self
296 .row_array
297 .map(|row| format!("|{}|", row.map(|square| square.to_string()).join("|")))
298 .join(&vertical_sep);
299 write!(f, "{border}\n{middle_rows}\n{border}")
300 }
301}
302
303#[cfg(test)]
304mod test_placers {
305 use super::*;
306
307 #[test]
308 fn place_success() {
309 let mut empty = MnkBoard::<2, 2>::new();
310
311 let top_left = empty.place(Player::X, 0, 0);
312 assert_eq!(top_left, Ok(()));
313 assert_eq!(
314 empty.row_array,
315 [
316 [Space::Stone(Player::X), Space::Empty],
317 [Space::Empty, Space::Empty]
318 ]
319 );
320
321 let top_right = empty.place(Player::O, 0, 1);
322 assert_eq!(top_right, Ok(()));
323 assert_eq!(
324 empty.row_array,
325 [
326 [Space::Stone(Player::X), Space::Stone(Player::O)],
327 [Space::Empty, Space::Empty]
328 ]
329 );
330
331 let bottom_left = empty.place(Player::O, 1, 0);
332 assert_eq!(bottom_left, Ok(()));
333 assert_eq!(
334 empty.row_array,
335 [
336 [Space::Stone(Player::X), Space::Stone(Player::O)],
337 [Space::Stone(Player::O), Space::Empty]
338 ]
339 );
340
341 let bottom_right = empty.place(Player::X, 1, 1);
342 assert_eq!(bottom_right, Ok(()));
343 assert_eq!(
344 empty.row_array,
345 [
346 [Space::Stone(Player::X), Space::Stone(Player::O)],
347 [Space::Stone(Player::O), Space::Stone(Player::X)]
348 ]
349 );
350 }
351
352 #[test]
353 fn place_occupied() {
354 let mut full = MnkBoard::from([
355 [Space::Stone(Player::X), Space::Stone(Player::O)],
356 [Space::Stone(Player::O), Space::Stone(Player::X)],
357 ]);
358
359 let top_left_x = full.place(Player::X, 0, 0);
360 assert_eq!(top_left_x, Err(PlaceError::Occupied { player: Player::X }));
361 let top_left_o = full.place(Player::O, 0, 0);
362 assert_eq!(top_left_o, Err(PlaceError::Occupied { player: Player::X }));
363
364 let top_right_x = full.place(Player::X, 0, 1);
365 assert_eq!(top_right_x, Err(PlaceError::Occupied { player: Player::O }));
366 let top_right_o = full.place(Player::O, 0, 1);
367 assert_eq!(top_right_o, Err(PlaceError::Occupied { player: Player::O }));
368
369 let bottom_left_x = full.place(Player::X, 1, 0);
370 assert_eq!(
371 bottom_left_x,
372 Err(PlaceError::Occupied { player: Player::O })
373 );
374 let bottom_left_o = full.place(Player::O, 1, 0);
375 assert_eq!(
376 bottom_left_o,
377 Err(PlaceError::Occupied { player: Player::O })
378 );
379
380 let bottom_right_x = full.place(Player::X, 1, 1);
381 assert_eq!(
382 bottom_right_x,
383 Err(PlaceError::Occupied { player: Player::X })
384 );
385 let bottom_right_o = full.place(Player::O, 1, 1);
386 assert_eq!(
387 bottom_right_o,
388 Err(PlaceError::Occupied { player: Player::X })
389 );
390 }
391
392 #[test]
393 fn place_out_of_bounds() {
394 let mut empty: MnkBoard<2, 2> = MnkBoard::new();
395
396 let high_row_x = empty.place(Player::X, 2, 0);
397 assert_eq!(
398 high_row_x,
399 Err(PlaceError::OutOfBounds { row: 2, column: 0 })
400 );
401 let high_row_o = empty.place(Player::O, 2, 0);
402 assert_eq!(
403 high_row_o,
404 Err(PlaceError::OutOfBounds { row: 2, column: 0 })
405 );
406
407 let high_column_x = empty.place(Player::X, 0, 2);
408 assert_eq!(
409 high_column_x,
410 Err(PlaceError::OutOfBounds { row: 0, column: 2 })
411 );
412 let high_column_o = empty.place(Player::O, 0, 2);
413 assert_eq!(
414 high_column_o,
415 Err(PlaceError::OutOfBounds { row: 0, column: 2 })
416 );
417 }
418
419 #[test]
420 fn place_unchecked_empty() {
421 let mut empty = MnkBoard::<2, 2>::new();
422
423 unsafe {
424 empty.place_unchecked(Player::X, 0, 0);
425 }
426 assert_eq!(
427 empty.row_array,
428 [
429 [Space::Stone(Player::X), Space::Empty],
430 [Space::Empty, Space::Empty]
431 ]
432 );
433
434 unsafe {
435 empty.place_unchecked(Player::O, 0, 1);
436 }
437 assert_eq!(
438 empty.row_array,
439 [
440 [Space::Stone(Player::X), Space::Stone(Player::O)],
441 [Space::Empty, Space::Empty]
442 ]
443 );
444
445 unsafe {
446 empty.place_unchecked(Player::O, 1, 0);
447 }
448 assert_eq!(
449 empty.row_array,
450 [
451 [Space::Stone(Player::X), Space::Stone(Player::O)],
452 [Space::Stone(Player::O), Space::Empty]
453 ]
454 );
455
456 unsafe {
457 empty.place_unchecked(Player::X, 1, 1);
458 }
459 assert_eq!(
460 empty.row_array,
461 [
462 [Space::Stone(Player::X), Space::Stone(Player::O)],
463 [Space::Stone(Player::O), Space::Stone(Player::X)]
464 ]
465 );
466 }
467
468 #[test]
469 fn place_unchecked_occupied() {
470 let mut all_x = MnkBoard::from([
471 [Space::Stone(Player::X), Space::Stone(Player::X)],
472 [Space::Stone(Player::X), Space::Stone(Player::X)],
473 ]);
474
475 unsafe {
476 all_x.place_unchecked(Player::O, 0, 0);
477 }
478 assert_eq!(
479 all_x.row_array,
480 [
481 [Space::Stone(Player::O), Space::Stone(Player::X)],
482 [Space::Stone(Player::X), Space::Stone(Player::X)],
483 ]
484 );
485
486 unsafe {
487 all_x.place_unchecked(Player::O, 0, 1);
488 }
489 assert_eq!(
490 all_x.row_array,
491 [
492 [Space::Stone(Player::O), Space::Stone(Player::O)],
493 [Space::Stone(Player::X), Space::Stone(Player::X)],
494 ]
495 );
496
497 unsafe {
498 all_x.place_unchecked(Player::O, 1, 0);
499 }
500 assert_eq!(
501 all_x.row_array,
502 [
503 [Space::Stone(Player::O), Space::Stone(Player::O)],
504 [Space::Stone(Player::O), Space::Stone(Player::X)],
505 ]
506 );
507
508 unsafe {
509 all_x.place_unchecked(Player::O, 1, 1);
510 }
511 assert_eq!(
512 all_x.row_array,
513 [
514 [Space::Stone(Player::O), Space::Stone(Player::O)],
515 [Space::Stone(Player::O), Space::Stone(Player::O)],
516 ]
517 );
518 }
519}
520
521#[cfg(test)]
522mod test_getters {
523 use super::*;
524
525 fn square() -> MnkBoard<2, 2> {
526 MnkBoard::from([
527 [Space::Stone(Player::X), Space::Empty],
528 [Space::Empty, Space::Stone(Player::O)],
529 ])
530 }
531
532 #[test]
533 fn get_in_bounds() {
534 let board = square();
535
536 assert_eq!(board.get(0, 0), Some(&Space::Stone(Player::X)));
537 assert_eq!(board.get(0, 1), Some(&Space::Empty));
538 assert_eq!(board.get(1, 0), Some(&Space::Empty));
539 assert_eq!(board.get(1, 1), Some(&Space::Stone(Player::O)));
540 }
541
542 #[test]
543 fn get_out_of_bounds() {
544 let board = square();
545
546 assert_eq!(board.get(2, 0), None);
547 assert_eq!(board.get(0, 2), None);
548 }
549
550 #[test]
551 fn get_unchecked() {
552 let board = square();
553
554 let top_left;
555 let top_right;
556 let bottom_left;
557 let bottom_right;
558 unsafe {
559 top_left = board.get_unchecked(0, 0);
560 top_right = board.get_unchecked(0, 1);
561 bottom_left = board.get_unchecked(1, 0);
562 bottom_right = board.get_unchecked(1, 1);
563 }
564 assert_eq!(top_left, &Space::Stone(Player::X));
565 assert_eq!(top_right, &Space::Empty);
566 assert_eq!(bottom_left, &Space::Empty);
567 assert_eq!(bottom_right, &Space::Stone(Player::O));
568 }
569}
570
571#[cfg(test)]
572mod test_square_board {
573 use super::*;
575
576 fn square_board() -> MnkBoard<5, 5> {
577 MnkBoard::from([
578 [
579 Space::Empty,
580 Space::Stone(Player::X),
581 Space::Stone(Player::O),
582 Space::Empty,
583 Space::Stone(Player::X),
584 ],
585 [
586 Space::Stone(Player::X),
587 Space::Stone(Player::O),
588 Space::Empty,
589 Space::Stone(Player::X),
590 Space::Stone(Player::O),
591 ],
592 [
593 Space::Stone(Player::O),
594 Space::Empty,
595 Space::Stone(Player::X),
596 Space::Stone(Player::O),
597 Space::Empty,
598 ],
599 [
600 Space::Stone(Player::O),
601 Space::Stone(Player::X),
602 Space::Empty,
603 Space::Stone(Player::O),
604 Space::Stone(Player::X),
605 ],
606 [
607 Space::Stone(Player::X),
608 Space::Stone(Player::O),
609 Space::Empty,
610 Space::Stone(Player::O),
611 Space::Stone(Player::X),
612 ],
613 ])
614 }
615
616 #[test]
617 fn rows() {
618 let board = square_board();
619 let rows: Vec<Vec<&Space>> = board.rows().map(Iterator::collect).collect();
620 assert_eq!(rows.len(), 5);
621
622 let top_row = vec![
623 &Space::Empty,
624 &Space::Stone(Player::X),
625 &Space::Stone(Player::O),
626 &Space::Empty,
627 &Space::Stone(Player::X),
628 ];
629 assert!(rows.contains(&top_row));
630
631 let second_row = vec![
632 &Space::Stone(Player::X),
633 &Space::Stone(Player::O),
634 &Space::Empty,
635 &Space::Stone(Player::X),
636 &Space::Stone(Player::O),
637 ];
638 assert!(rows.contains(&second_row));
639
640 let third_row = vec![
641 &Space::Stone(Player::O),
642 &Space::Empty,
643 &Space::Stone(Player::X),
644 &Space::Stone(Player::O),
645 &Space::Empty,
646 ];
647 assert!(rows.contains(&third_row));
648
649 let fourth_row = vec![
650 &Space::Stone(Player::O),
651 &Space::Stone(Player::X),
652 &Space::Empty,
653 &Space::Stone(Player::O),
654 &Space::Stone(Player::X),
655 ];
656 assert!(rows.contains(&fourth_row));
657
658 let fifth_row = vec![
659 &Space::Stone(Player::X),
660 &Space::Stone(Player::O),
661 &Space::Empty,
662 &Space::Stone(Player::O),
663 &Space::Stone(Player::X),
664 ];
665 assert!(rows.contains(&fifth_row));
666 }
667
668 #[test]
669 fn columns() {
670 let board = square_board();
671 let columns: Vec<Vec<&Space>> = board.columns().map(Iterator::collect).collect();
672 assert_eq!(columns.len(), 5);
673
674 let first_col = vec![
675 &Space::Empty,
676 &Space::Stone(Player::X),
677 &Space::Stone(Player::O),
678 &Space::Stone(Player::O),
679 &Space::Stone(Player::X),
680 ];
681 assert!(columns.contains(&first_col));
682
683 let second_col = vec![
684 &Space::Stone(Player::X),
685 &Space::Stone(Player::O),
686 &Space::Empty,
687 &Space::Stone(Player::X),
688 &Space::Stone(Player::O),
689 ];
690 assert!(columns.contains(&second_col));
691
692 let third_col = vec![
693 &Space::Stone(Player::O),
694 &Space::Empty,
695 &Space::Stone(Player::X),
696 &Space::Empty,
697 &Space::Empty,
698 ];
699 assert!(columns.contains(&third_col));
700
701 let fourth_col = vec![
702 &Space::Empty,
703 &Space::Stone(Player::X),
704 &Space::Stone(Player::O),
705 &Space::Stone(Player::O),
706 &Space::Stone(Player::O),
707 ];
708 assert!(columns.contains(&fourth_col));
709
710 let fifth_col = vec![
711 &Space::Stone(Player::X),
712 &Space::Stone(Player::O),
713 &Space::Empty,
714 &Space::Stone(Player::X),
715 &Space::Stone(Player::X),
716 ];
717 assert!(columns.contains(&fifth_col));
718 }
719
720 #[test]
721 fn top_right() {
722 let board = square_board();
723 let diags: Vec<Vec<&Space>> = board
724 .top_right_diagonals(3)
725 .map(Iterator::collect)
726 .collect();
727 assert_eq!(diags.len(), 3);
728
729 let first_diag = vec![
730 &Space::Empty,
731 &Space::Stone(Player::O),
732 &Space::Stone(Player::X),
733 &Space::Stone(Player::O),
734 &Space::Stone(Player::X),
735 ];
736 assert!(diags.contains(&first_diag));
737
738 let second_diag = vec![
739 &Space::Stone(Player::X),
740 &Space::Empty,
741 &Space::Stone(Player::O),
742 &Space::Stone(Player::X),
743 ];
744 assert!(diags.contains(&second_diag));
745
746 let third_diag = vec![
747 &Space::Stone(Player::O),
748 &Space::Stone(Player::X),
749 &Space::Empty,
750 ];
751 assert!(diags.contains(&third_diag));
752 }
753
754 #[test]
755 fn left_down() {
756 let board = square_board();
757 let diags: Vec<Vec<&Space>> = board
758 .left_down_diagonals(3)
759 .map(Iterator::collect)
760 .collect();
761 assert_eq!(diags.len(), 2);
762
763 let first_diag = vec![
764 &Space::Stone(Player::X),
765 &Space::Empty,
766 &Space::Empty,
767 &Space::Stone(Player::O),
768 ];
769 assert!(diags.contains(&first_diag));
770
771 let second_diag = vec![
772 &Space::Stone(Player::O),
773 &Space::Stone(Player::X),
774 &Space::Empty,
775 ];
776 assert!(diags.contains(&second_diag));
777 }
778
779 #[test]
780 fn top_left() {
781 let board = square_board();
782 let diags: Vec<Vec<&Space>> = board.top_left_diagonals(3).map(Iterator::collect).collect();
783 assert_eq!(diags.len(), 3);
784
785 let first_diag = vec![
786 &Space::Stone(Player::O),
787 &Space::Stone(Player::O),
788 &Space::Stone(Player::O),
789 ];
790 assert!(diags.contains(&first_diag));
791 let second_diag = vec![
792 &Space::Empty,
793 &Space::Empty,
794 &Space::Empty,
795 &Space::Stone(Player::O),
796 ];
797 assert!(diags.contains(&second_diag));
798
799 let third_diag = vec![
800 &Space::Stone(Player::X),
801 &Space::Stone(Player::X),
802 &Space::Stone(Player::X),
803 &Space::Stone(Player::X),
804 &Space::Stone(Player::X),
805 ];
806 assert!(diags.contains(&third_diag));
807 }
808
809 #[test]
810 fn right_down() {
811 let board = square_board();
812 let diags: Vec<Vec<&Space>> = board
813 .right_down_diagonals(3)
814 .map(Iterator::collect)
815 .collect();
816 assert_eq!(diags.len(), 2);
817
818 let first_diag = vec![
819 &Space::Stone(Player::O),
820 &Space::Stone(Player::O),
821 &Space::Empty,
822 &Space::Stone(Player::O),
823 ];
824 assert!(diags.contains(&first_diag));
825
826 let second_diag = vec![&Space::Empty, &Space::Stone(Player::O), &Space::Empty];
827 assert!(diags.contains(&second_diag));
828 }
829}
830
831#[cfg(test)]
832mod test_rectangular_boards {
833 use super::*;
834
835 fn tall_board() -> MnkBoard<5, 4> {
836 MnkBoard::from([
837 [
838 Space::Empty,
839 Space::Stone(Player::X),
840 Space::Stone(Player::O),
841 Space::Empty,
842 ],
843 [
844 Space::Stone(Player::X),
845 Space::Stone(Player::O),
846 Space::Empty,
847 Space::Stone(Player::X),
848 ],
849 [
850 Space::Stone(Player::O),
851 Space::Empty,
852 Space::Stone(Player::X),
853 Space::Stone(Player::O),
854 ],
855 [
856 Space::Stone(Player::O),
857 Space::Stone(Player::X),
858 Space::Empty,
859 Space::Stone(Player::O),
860 ],
861 [
862 Space::Stone(Player::X),
863 Space::Stone(Player::O),
864 Space::Empty,
865 Space::Stone(Player::O),
866 ],
867 ])
868 }
869
870 fn wide_board() -> MnkBoard<4, 5> {
871 MnkBoard::from([
872 [
873 Space::Empty,
874 Space::Stone(Player::X),
875 Space::Stone(Player::O),
876 Space::Empty,
877 Space::Stone(Player::X),
878 ],
879 [
880 Space::Stone(Player::X),
881 Space::Stone(Player::O),
882 Space::Empty,
883 Space::Stone(Player::X),
884 Space::Stone(Player::O),
885 ],
886 [
887 Space::Stone(Player::O),
888 Space::Empty,
889 Space::Stone(Player::X),
890 Space::Stone(Player::O),
891 Space::Empty,
892 ],
893 [
894 Space::Stone(Player::O),
895 Space::Stone(Player::X),
896 Space::Empty,
897 Space::Stone(Player::O),
898 Space::Stone(Player::X),
899 ],
900 ])
901 }
902
903 #[test]
904 fn tall_top_right_diags() {
905 let board = tall_board();
906 let diags: Vec<Vec<&Space>> = board
907 .top_right_diagonals(3)
908 .map(Iterator::collect)
909 .collect();
910 assert_eq!(diags.len(), 2);
911
912 let first_diag = vec![
913 &Space::Empty,
914 &Space::Stone(Player::O),
915 &Space::Stone(Player::X),
916 &Space::Stone(Player::O),
917 ];
918 assert!(diags.contains(&first_diag));
919
920 let second_diag = vec![
921 &Space::Stone(Player::X),
922 &Space::Empty,
923 &Space::Stone(Player::O),
924 ];
925 assert!(diags.contains(&second_diag));
926 }
927
928 #[test]
929 fn tall_left_down_diags() {
930 let board = tall_board();
931 let diags: Vec<Vec<&Space>> = board
932 .left_down_diagonals(3)
933 .map(Iterator::collect)
934 .collect();
935 assert_eq!(diags.len(), 2);
936
937 let first_diag = vec![
938 &Space::Stone(Player::X),
939 &Space::Empty,
940 &Space::Empty,
941 &Space::Stone(Player::O),
942 ];
943 assert!(diags.contains(&first_diag));
944
945 let second_diag = vec![
946 &Space::Stone(Player::O),
947 &Space::Stone(Player::X),
948 &Space::Empty,
949 ];
950 assert!(diags.contains(&second_diag));
951 }
952
953 #[test]
954 fn tall_top_left_diags() {
955 let board = tall_board();
956 let diags: Vec<Vec<&Space>> = board.top_left_diagonals(3).map(Iterator::collect).collect();
957 assert_eq!(diags.len(), 2);
958
959 let first_diag = vec![
960 &Space::Empty,
961 &Space::Empty,
962 &Space::Empty,
963 &Space::Stone(Player::O),
964 ];
965 assert!(diags.contains(&first_diag));
966
967 let second_diag = vec![
968 &Space::Stone(Player::O),
969 &Space::Stone(Player::O),
970 &Space::Stone(Player::O),
971 ];
972 assert!(diags.contains(&second_diag));
973 }
974
975 #[test]
976 fn tall_right_down_diags() {
977 let board = tall_board();
978 let diags: Vec<Vec<&Space>> = board
979 .right_down_diagonals(3)
980 .map(Iterator::collect)
981 .collect();
982 assert_eq!(diags.len(), 2);
983
984 let first_diag = vec![
985 &Space::Stone(Player::X),
986 &Space::Stone(Player::X),
987 &Space::Stone(Player::X),
988 &Space::Stone(Player::X),
989 ];
990 assert!(diags.contains(&first_diag));
991
992 let second_diag = vec![
993 &Space::Stone(Player::O),
994 &Space::Empty,
995 &Space::Stone(Player::O),
996 ];
997 assert!(diags.contains(&second_diag));
998 }
999
1000 #[test]
1001 fn wide_top_right_diags() {
1002 let board = wide_board();
1003 let diags: Vec<Vec<&Space>> = board
1004 .top_right_diagonals(3)
1005 .map(Iterator::collect)
1006 .collect();
1007 assert_eq!(diags.len(), 3);
1008
1009 let first_diag = vec![
1010 &Space::Empty,
1011 &Space::Stone(Player::O),
1012 &Space::Stone(Player::X),
1013 &Space::Stone(Player::O),
1014 ];
1015 assert!(diags.contains(&first_diag));
1016
1017 let second_diag = vec![
1018 &Space::Stone(Player::X),
1019 &Space::Empty,
1020 &Space::Stone(Player::O),
1021 &Space::Stone(Player::X),
1022 ];
1023 assert!(diags.contains(&second_diag));
1024
1025 let third_diag = vec![
1026 &Space::Stone(Player::O),
1027 &Space::Stone(Player::X),
1028 &Space::Empty,
1029 ];
1030 assert!(diags.contains(&third_diag));
1031 }
1032
1033 #[test]
1034 fn wide_left_down_diags() {
1035 let board = wide_board();
1036 let diags: Vec<Vec<&Space>> = board
1037 .left_down_diagonals(3)
1038 .map(Iterator::collect)
1039 .collect();
1040 let diag = vec![&Space::Stone(Player::X), &Space::Empty, &Space::Empty];
1041 assert_eq!(diags, [diag]);
1042 }
1043
1044 #[test]
1045 fn wide_top_left_diags() {
1046 let board = wide_board();
1047 let diags: Vec<Vec<&Space>> = board.top_left_diagonals(3).map(Iterator::collect).collect();
1048 assert_eq!(diags.len(), 3);
1049
1050 let first_diag = vec![
1051 &Space::Stone(Player::X),
1052 &Space::Stone(Player::X),
1053 &Space::Stone(Player::X),
1054 &Space::Stone(Player::X),
1055 ];
1056 assert!(diags.contains(&first_diag));
1057
1058 let second_diag = vec![
1059 &Space::Empty,
1060 &Space::Empty,
1061 &Space::Empty,
1062 &Space::Stone(Player::O),
1063 ];
1064 assert!(diags.contains(&second_diag));
1065
1066 let third_diag = vec![
1067 &Space::Stone(Player::O),
1068 &Space::Stone(Player::O),
1069 &Space::Stone(Player::O),
1070 ];
1071 assert!(diags.contains(&third_diag));
1072 }
1073
1074 #[test]
1075 fn wide_right_up_diags() {
1076 let board = wide_board();
1077 let diags: Vec<Vec<&Space>> = board
1078 .right_down_diagonals(3)
1079 .map(Iterator::collect)
1080 .collect();
1081 let diag = vec![
1082 &Space::Stone(Player::O),
1083 &Space::Stone(Player::O),
1084 &Space::Empty,
1085 ];
1086 assert_eq!(diags, [diag]);
1087 }
1088}
1089
1090#[cfg(test)]
1091mod test_mnk_board_display {
1092 use super::*;
1093
1094 #[test]
1095 fn squares() {
1096 let one = MnkBoard::from([[Space::Stone(Player::X)]]);
1097 assert_eq!(
1098 one.to_string(),
1099 "+-+\n\
1100 |X|\n\
1101 +-+"
1102 );
1103
1104 let two = MnkBoard::from([
1105 [Space::Stone(Player::X), Space::Empty],
1106 [Space::Empty, Space::Stone(Player::O)],
1107 ]);
1108 assert_eq!(
1109 two.to_string(),
1110 "+-+-+\n\
1111 |X| |\n\
1112 +-+-+\n\
1113 | |O|\n\
1114 +-+-+"
1115 );
1116
1117 let three = MnkBoard::from([
1118 [
1119 Space::Stone(Player::X),
1120 Space::Empty,
1121 Space::Stone(Player::O),
1122 ],
1123 [
1124 Space::Stone(Player::O),
1125 Space::Stone(Player::X),
1126 Space::Empty,
1127 ],
1128 [
1129 Space::Stone(Player::X),
1130 Space::Stone(Player::O),
1131 Space::Empty,
1132 ],
1133 ]);
1134 assert_eq!(
1135 three.to_string(),
1136 "+-+-+-+\n\
1137 |X| |O|\n\
1138 +-+-+-+\n\
1139 |O|X| |\n\
1140 +-+-+-+\n\
1141 |X|O| |\n\
1142 +-+-+-+"
1143 );
1144 }
1145
1146 #[test]
1147 fn rectangles() {
1148 let tall = MnkBoard::from([
1149 [Space::Stone(Player::X), Space::Empty],
1150 [Space::Empty, Space::Stone(Player::O)],
1151 [Space::Stone(Player::X), Space::Stone(Player::O)],
1152 ]);
1153 assert_eq!(
1154 tall.to_string(),
1155 "+-+-+\n\
1156 |X| |\n\
1157 +-+-+\n\
1158 | |O|\n\
1159 +-+-+\n\
1160 |X|O|\n\
1161 +-+-+"
1162 );
1163
1164 let wide = MnkBoard::from([
1165 [
1166 Space::Stone(Player::X),
1167 Space::Empty,
1168 Space::Stone(Player::X),
1169 ],
1170 [
1171 Space::Empty,
1172 Space::Stone(Player::O),
1173 Space::Stone(Player::O),
1174 ],
1175 ]);
1176 assert_eq!(
1177 wide.to_string(),
1178 "+-+-+-+\n\
1179 |X| |X|\n\
1180 +-+-+-+\n\
1181 | |O|O|\n\
1182 +-+-+-+"
1183 );
1184 }
1185}