diff options
Diffstat (limited to 'model')
| -rw-r--r-- | model/src/board.rs | 3 | ||||
| -rw-r--r-- | model/src/board/tests.rs | 33 | ||||
| -rw-r--r-- | model/src/moves.rs | 109 | ||||
| -rw-r--r-- | model/src/possible_moves.rs | 312 |
4 files changed, 416 insertions, 41 deletions
diff --git a/model/src/board.rs b/model/src/board.rs index b67c13c..f95d837 100644 --- a/model/src/board.rs +++ b/model/src/board.rs @@ -137,6 +137,7 @@ impl CheckersBitBoard { /// * `row` - The row. The a file is row 0 /// * `col` - The column. The first rank is column 0 #[must_use] + // TODO test pub fn get_at_row_col(self, row: usize, col: usize) -> Option<Piece> { if row > 32 || col > 32 { None @@ -319,6 +320,7 @@ impl CheckersBitBoard { /// Change whose turn it is, without modifying the board #[must_use] + // TODO test pub const fn flip_turn(self) -> Self { CheckersBitBoard::new(self.pieces, self.color, self.kings, self.turn.flip()) } @@ -525,6 +527,7 @@ impl CheckersBitBoard { /// Moving from the left side of the board results in undefined behavior. /// Moving from the top of the board results in undefined behavior #[must_use] + // TODO test the edge cases of the below if statement pub const unsafe fn jump_piece_forward_left_unchecked(self, value: usize) -> Self { let is_king = self.king_at_unchecked(value); let board = self diff --git a/model/src/board/tests.rs b/model/src/board/tests.rs index e4d1f62..c3ea2cd 100644 --- a/model/src/board/tests.rs +++ b/model/src/board/tests.rs @@ -64,7 +64,9 @@ proptest! { kings: k, turn: PieceColor::Dark }; - board.piece_at(v); + + // just test for no crash + let _ = board.piece_at(v); } #[test] @@ -75,7 +77,9 @@ proptest! { kings: k, turn: PieceColor::Dark }; - unsafe {board.color_at_unchecked(v);} + + // just test for no crash + unsafe {let _ = board.color_at_unchecked(v);} } #[test] @@ -86,7 +90,7 @@ proptest! { kings: k, turn: PieceColor::Dark }; - unsafe {board.king_at_unchecked(v);} + unsafe {let _ = board.king_at_unchecked(v);} } #[test] @@ -97,7 +101,9 @@ proptest! { kings: k, turn: PieceColor::Dark }; - board.color_at(v); + + // just testing for no crash + let _ = board.color_at(v); } #[test] @@ -108,7 +114,9 @@ proptest! { kings: k, turn: PieceColor::Dark }; - board.king_at(v); + + // just testing for no crash + let _ = board.king_at(v); } #[test] @@ -555,12 +563,11 @@ fn test_move_piece_forward_standard() { #[test] fn test_move_piece_forward_wrap() { let board = CheckersBitBoard::default(); - let board = unsafe { board.move_piece_forward_unchecked(31, 10) }; // go to 9 - assert!(!board.piece_at(31)); - assert!(board.piece_at(9)); - // TODO always move a dark squared piece - assert_eq!(board.color_at(9).unwrap(), PieceColor::Light); - assert!(!board.king_at(9).unwrap()); + let board = unsafe { board.move_piece_forward_unchecked(26, 8) }; // go to 9 + assert!(!board.piece_at(26)); + assert!(board.piece_at(2)); + assert_eq!(board.color_at(2).unwrap(), PieceColor::Dark); + assert!(!board.king_at(2).unwrap()); assert_eq!(board.turn, PieceColor::Light); } @@ -582,13 +589,13 @@ fn test_move_piece_backward_left_to_king() { #[test] fn test_move_piece_backward_standard() { - let board = CheckersBitBoard::default(); + let board = CheckersBitBoard::default().flip_turn(); let board = unsafe { board.move_piece_backward_unchecked(29, 14) }; // go to 15 assert!(!board.piece_at(29)); assert!(board.piece_at(15)); assert_eq!(board.color_at(15).unwrap(), PieceColor::Light); assert!(!board.king_at(15).unwrap()); - assert_eq!(board.turn, PieceColor::Light); + assert_eq!(board.turn, PieceColor::Dark); } #[test] diff --git a/model/src/moves.rs b/model/src/moves.rs index 12a26db..720e11a 100644 --- a/model/src/moves.rs +++ b/model/src/moves.rs @@ -30,7 +30,6 @@ impl Move { /// * `direction` - The direction the piece should move in /// * `jump` - Whether or not the piece should jump pub const fn new(start: usize, direction: MoveDirection, jump: bool) -> Self { - // TODO what are the semantics of usize as u32? Self { start: start as u32, direction, @@ -38,10 +37,21 @@ impl Move { } } + /// The stating position of the move pub const fn start(self) -> u32 { self.start } + /// The direction the move goes in + pub const fn direction(self) -> MoveDirection { + self.direction + } + + /// Returns `true` if the move is a jump + pub const fn is_jump(self) -> bool { + self.jump + } + /// Calculates the value of the end position of the move pub const fn end_position(self) -> usize { let dest = match self.jump { @@ -145,5 +155,102 @@ mod tests { assert_eq!(move_test.direction, direction); assert_eq!(move_test.jump, jump); } + + #[test] + fn start(start in 0usize..32, jump in proptest::bool::ANY) { + let direction = MoveDirection::ForwardLeft; + let move_test = Move::new(start, direction, jump); + assert_eq!(move_test.start(), start as u32); + } + + #[test] + fn direction(start in 0usize..32, jump in proptest::bool::ANY) { + let direction = MoveDirection::ForwardLeft; + let move_test = Move::new(start, direction, jump); + assert_eq!(move_test.direction(), direction); + + let direction = MoveDirection::ForwardRight; + let move_test = Move::new(start, direction, jump); + assert_eq!(move_test.direction(), direction); + + let direction = MoveDirection::BackwardLeft; + let move_test = Move::new(start, direction, jump); + assert_eq!(move_test.direction(), direction); + + let direction = MoveDirection::BackwardRight; + let move_test = Move::new(start, direction, jump); + assert_eq!(move_test.direction(), direction); + } + + #[test] + fn is_jump(start in 0usize..32, jump in proptest::bool::ANY) { + let direction = MoveDirection::ForwardLeft; + let move_test = Move::new(start, direction, jump); + assert_eq!(move_test.is_jump(), jump); + } + } + + #[test] + fn end_position_forward_left_slide() { + let direction = MoveDirection::ForwardLeft; + let start = 8; + let move_test = Move::new(start, direction, false); + assert_eq!(move_test.end_position(), 15); + } + + #[test] + fn end_position_forward_right_slide() { + let direction = MoveDirection::ForwardRight; + let start = 26; + let move_test = Move::new(start, direction, false); + assert_eq!(move_test.end_position(), 27); + } + + #[test] + fn end_position_backward_right_slide() { + let direction = MoveDirection::BackwardRight; + let start = 2; + let move_test = Move::new(start, direction, false); + assert_eq!(move_test.end_position(), 27); + } + + #[test] + fn end_position_backward_left_slide() { + let direction = MoveDirection::BackwardLeft; + let start = 16; + let move_test = Move::new(start, direction, false); + assert_eq!(move_test.end_position(), 15); + } + + #[test] + fn end_position_forward_left_jump() { + let direction = MoveDirection::ForwardLeft; + let start = 8; + let move_test = Move::new(start, direction, true); + assert_eq!(move_test.end_position(), 22); + } + + #[test] + fn end_position_forward_right_jump() { + let direction = MoveDirection::ForwardRight; + let start = 26; + let move_test = Move::new(start, direction, true); + assert_eq!(move_test.end_position(), 28); + } + + #[test] + fn end_position_backward_right_jump() { + let direction = MoveDirection::BackwardRight; + let start = 2; + let move_test = Move::new(start, direction, true); + assert_eq!(move_test.end_position(), 20); + } + + #[test] + fn end_position_backward_left_jump() { + let direction = MoveDirection::BackwardLeft; + let start = 16; + let move_test = Move::new(start, direction, true); + assert_eq!(move_test.end_position(), 14); } } diff --git a/model/src/possible_moves.rs b/model/src/possible_moves.rs index f50c03e..50ad774 100644 --- a/model/src/possible_moves.rs +++ b/model/src/possible_moves.rs @@ -4,6 +4,7 @@ use std::alloc::{alloc, dealloc, handle_alloc_error, Layout}; use std::mem::MaybeUninit; use std::ptr::NonNull; +// The maximum number of available moves in any given position const POSSIBLE_MOVES_ITER_SIZE: usize = 42; /// A struct containing the possible moves in a particular checkers position @@ -118,6 +119,7 @@ impl IntoIterator for PossibleMoves { type Item = Move; type IntoIter = PossibleMovesIter; + // TODO test fn into_iter(self) -> Self::IntoIter { let layout = Layout::array::<MaybeUninit<Move>>(POSSIBLE_MOVES_ITER_SIZE).unwrap(); let allocated_mem = unsafe { alloc(layout) }; @@ -318,7 +320,9 @@ impl IntoIterator for PossibleMoves { } impl PossibleMoves { + // TODO test const fn slides_dark(board: CheckersBitBoard) -> Self { + // TODO maybe remove these? const FORWARD_LEFT_MASK: u32 = 0b01111001111110111111001111011011; const FORWARD_RIGHT_MASK: u32 = 0b01111101111111011111010111011101; const BACKWARD_LEFT_MASK: u32 = 0b11111011111110111110101110111010; @@ -582,39 +586,54 @@ impl PossibleMoves { pub const fn can_jump(self) -> bool { (self.backward_right_movers & 2) != 0 } - - /// Returns the pieces who can move forward left, - /// with undefined behavior for bits where a forward left move is impossible - /// - /// # Safety - /// - /// This function is inherently unsafe because some bits are undefined - // TODO make this unsafe - pub const fn forward_left_bits(self) -> u32 { - self.forward_left_movers - } - - /// Gets the bits for a certain direction, - /// with undefined behavior for bits where the given move is impossible - /// - /// # Safety - /// - /// This function is inherently unsafe because some bits are undefined - // TODO make this unsafe - pub const fn get_direction_bits(self, direction: MoveDirection) -> u32 { - match direction { - MoveDirection::ForwardLeft => self.forward_left_movers, - MoveDirection::ForwardRight => self.forward_right_movers, - MoveDirection::BackwardLeft => self.backward_left_movers, - MoveDirection::BackwardRight => self.backward_right_movers, - } - } } #[cfg(test)] mod tests { use super::*; + fn setup_empty_iter() -> PossibleMovesIter { + let layout = Layout::array::<MaybeUninit<Move>>(POSSIBLE_MOVES_ITER_SIZE).unwrap(); + let allocated_mem = unsafe { alloc(layout) }; + let ptr = + match NonNull::new(allocated_mem as *mut [MaybeUninit<Move>; POSSIBLE_MOVES_ITER_SIZE]) + { + Some(p) => p, + None => handle_alloc_error(layout), + }; + let iter = PossibleMovesIter { + moves: ptr, + index: 0, + length: 0, + }; + + iter + } + + fn setup_add_move_to_iter_invalid() -> (PossibleMovesIter, PossibleMoves) { + let moves = PossibleMoves { + forward_left_movers: 0, + forward_right_movers: 0, + backward_left_movers: 0, + backward_right_movers: 0, + }; + let iter = setup_empty_iter(); + + (iter, moves) + } + + fn setup_add_move_to_iter_valid() -> (PossibleMovesIter, PossibleMoves) { + let moves = PossibleMoves { + forward_left_movers: u32::MAX, + forward_right_movers: u32::MAX, + backward_left_movers: u32::MAX, + backward_right_movers: u32::MAX, + }; + let iter = setup_empty_iter(); + + (iter, moves) + } + #[test] fn same() { let start = CheckersBitBoard::new( @@ -635,4 +654,243 @@ mod tests { PossibleMoves::has_jumps(flip) ) } + + #[test] + fn iter_next() { + let test_move1 = Move::new(8, MoveDirection::ForwardLeft, false); + let test_move2 = Move::new(26, MoveDirection::ForwardRight, true); + let mut iter = setup_empty_iter(); + iter.length = 2; + + let ptr = unsafe { iter.moves.as_mut() }.get_mut(0).unwrap(); + *ptr = MaybeUninit::new(test_move1); + + let ptr = unsafe { iter.moves.as_mut() }.get_mut(1).unwrap(); + *ptr = MaybeUninit::new(test_move2); + + let recieved_move = iter.next(); + assert!(recieved_move.is_some()); + assert_eq!(recieved_move.unwrap(), test_move1); + + let recieved_move = iter.next(); + assert!(recieved_move.is_some()); + assert_eq!(recieved_move.unwrap(), test_move2); + + let recieved_move = iter.next(); + assert!(recieved_move.is_none()); + } + + #[test] + fn add_slide_forward_left_to_iter_invalid() { + const START: usize = 8; + let (mut iter, moves) = setup_add_move_to_iter_invalid(); + iter.add_slide_forward_left::<START>(moves); + + assert_eq!(iter.index, 0); + assert_eq!(iter.length, 0); + } + + #[test] + fn add_slide_forward_left_to_iter_valid() { + const START: usize = 8; + let (mut iter, moves) = setup_add_move_to_iter_valid(); + iter.add_slide_forward_left::<START>(moves); + + assert_eq!(iter.index, 0); + assert_eq!(iter.length, 1); + + let new_move = iter.next().unwrap(); + assert_eq!(new_move.start(), START as u32); + assert_eq!(new_move.direction(), MoveDirection::ForwardLeft); + assert!(!new_move.is_jump()); + } + + #[test] + fn add_slide_forward_right_to_iter_invalid() { + const START: usize = 26; + let (mut iter, moves) = setup_add_move_to_iter_invalid(); + iter.add_slide_forward_right::<START>(moves); + + assert_eq!(iter.index, 0); + assert_eq!(iter.length, 0); + } + + #[test] + fn add_slide_forward_right_to_iter_valid() { + const START: usize = 26; + let (mut iter, moves) = setup_add_move_to_iter_valid(); + iter.add_slide_forward_right::<START>(moves); + + assert_eq!(iter.index, 0); + assert_eq!(iter.length, 1); + + let new_move = iter.next().unwrap(); + assert_eq!(new_move.start(), START as u32); + assert_eq!(new_move.direction(), MoveDirection::ForwardRight); + assert!(!new_move.is_jump()); + } + + #[test] + fn add_slide_backward_left_to_iter_invalid() { + const START: usize = 17; + let (mut iter, moves) = setup_add_move_to_iter_invalid(); + iter.add_slide_backward_left::<START>(moves); + + assert_eq!(iter.index, 0); + assert_eq!(iter.length, 0); + } + + #[test] + fn add_slide_backward_left_to_iter_valid() { + const START: usize = 17; + let (mut iter, moves) = setup_add_move_to_iter_valid(); + iter.add_slide_backward_left::<START>(moves); + + assert_eq!(iter.index, 0); + assert_eq!(iter.length, 1); + + let new_move = iter.next().unwrap(); + assert_eq!(new_move.start(), START as u32); + assert_eq!(new_move.direction(), MoveDirection::BackwardLeft); + assert!(!new_move.is_jump()); + } + + #[test] + fn add_slide_backward_right_to_iter_invalid() { + const START: usize = 3; + let (mut iter, moves) = setup_add_move_to_iter_invalid(); + iter.add_slide_backward_right::<START>(moves); + + assert_eq!(iter.index, 0); + assert_eq!(iter.length, 0); + } + + #[test] + fn add_slide_backward_right_to_iter_valid() { + const START: usize = 3; + let (mut iter, moves) = setup_add_move_to_iter_valid(); + iter.add_slide_backward_right::<START>(moves); + + assert_eq!(iter.index, 0); + assert_eq!(iter.length, 1); + + let new_move = iter.next().unwrap(); + assert_eq!(new_move.start(), START as u32); + assert_eq!(new_move.direction(), MoveDirection::BackwardRight); + assert!(!new_move.is_jump()); + } + + #[test] + fn add_jump_forward_left_to_iter_invalid() { + const START: usize = 8; + let (mut iter, moves) = setup_add_move_to_iter_invalid(); + iter.add_jump_forward_left::<START>(moves); + + assert_eq!(iter.index, 0); + assert_eq!(iter.length, 0); + } + + #[test] + fn add_jump_forward_left_to_iter_valid() { + const START: usize = 8; + let (mut iter, moves) = setup_add_move_to_iter_valid(); + iter.add_jump_forward_left::<START>(moves); + + assert_eq!(iter.index, 0); + assert_eq!(iter.length, 1); + + let new_move = iter.next().unwrap(); + assert_eq!(new_move.start(), START as u32); + assert_eq!(new_move.direction(), MoveDirection::ForwardLeft); + assert!(new_move.is_jump()); + } + + #[test] + fn add_jump_forward_right_to_iter_invalid() { + const START: usize = 26; + let (mut iter, moves) = setup_add_move_to_iter_invalid(); + iter.add_jump_forward_right::<START>(moves); + + assert_eq!(iter.index, 0); + assert_eq!(iter.length, 0); + } + + #[test] + fn add_jump_forward_right_to_iter_valid() { + const START: usize = 26; + let (mut iter, moves) = setup_add_move_to_iter_valid(); + iter.add_jump_forward_right::<START>(moves); + + assert_eq!(iter.index, 0); + assert_eq!(iter.length, 1); + + let new_move = iter.next().unwrap(); + assert_eq!(new_move.start(), START as u32); + assert_eq!(new_move.direction(), MoveDirection::ForwardRight); + assert!(new_move.is_jump()); + } + + #[test] + fn add_jump_backward_left_to_iter_invalid() { + const START: usize = 17; + let (mut iter, moves) = setup_add_move_to_iter_invalid(); + iter.add_jump_backward_left::<START>(moves); + + assert_eq!(iter.index, 0); + assert_eq!(iter.length, 0); + } + + #[test] + fn add_jump_backward_left_to_iter_valid() { + const START: usize = 17; + let (mut iter, moves) = setup_add_move_to_iter_valid(); + iter.add_jump_backward_left::<START>(moves); + + assert_eq!(iter.index, 0); + assert_eq!(iter.length, 1); + + let new_move = iter.next().unwrap(); + assert_eq!(new_move.start(), START as u32); + assert_eq!(new_move.direction(), MoveDirection::BackwardLeft); + assert!(new_move.is_jump()); + } + + #[test] + fn add_jump_backward_right_to_iter_invalid() { + const START: usize = 3; + let (mut iter, moves) = setup_add_move_to_iter_invalid(); + iter.add_jump_backward_right::<START>(moves); + + assert_eq!(iter.index, 0); + assert_eq!(iter.length, 0); + } + + #[test] + fn add_jump_backward_right_to_iter_valid() { + const START: usize = 3; + let (mut iter, moves) = setup_add_move_to_iter_valid(); + iter.add_jump_backward_right::<START>(moves); + + assert_eq!(iter.index, 0); + assert_eq!(iter.length, 1); + + let new_move = iter.next().unwrap(); + assert_eq!(new_move.start(), START as u32); + assert_eq!(new_move.direction(), MoveDirection::BackwardRight); + assert!(new_move.is_jump()); + } + + #[test] + fn test_send() { + fn assert_send<T: Send>() {} + assert_send::<PossibleMoves>(); + // TODO iterator + } + + #[test] + fn test_sync() { + fn assert_sync<T: Sync>() {} + assert_sync::<PossibleMoves>(); + // TODO iterator + } } |
