From 098df2998baa9984bde7065c3ef56b8fd33bd5b8 Mon Sep 17 00:00:00 2001 From: "Elijah C. Voigt" Date: Sat, 4 May 2024 22:27:46 -0700 Subject: [PATCH] Fixed moves correctness! Sans merging which we still haven't wired into the real game... --- src/debug.rs | 2 +- src/game.rs | 162 +++++++++++++++++++++++++++++---------------------- 2 files changed, 92 insertions(+), 72 deletions(-) diff --git a/src/debug.rs b/src/debug.rs index 853de4f..5caff6a 100644 --- a/src/debug.rs +++ b/src/debug.rs @@ -231,7 +231,7 @@ fn debug_piece( Option<&display3d::Animating>, Option<&Selected>, ), - With, + With, >, pointer: Res, mut root: Query<(&mut Text, &mut Visibility), With>, diff --git a/src/game.rs b/src/game.rs index 6e9eab0..8363605 100644 --- a/src/game.rs +++ b/src/game.rs @@ -513,35 +513,32 @@ impl Board { from: BoardIndex, to: BoardIndex, ) -> Option { - self.at(from).map(|piece| { - // Given that the side does not have a queen||drone - // And the piece is a drone||pawn - // We can do field promotions - let side = Board::side(from).expect("Piece has valid index"); - let side_has_queen = self.on(side).iter().any(|(piece, _)| **piece == Piece::Queen); - let side_has_drone = self.on(side).iter().any(|(piece, _)| **piece == Piece::Drone); - - // Iterate over the piece's moves - piece - .moves_at(&from) - .iter() - // Find if the given `to` move is one of those - .find(|idx| to == **idx) - // Determine if this is valid/legal in this situation - .and_then(|_| { - let dest_at = self.at(to); - let curr_side = Board::side(from).unwrap(); - let dest_side = Board::side(to).unwrap(); - - match (curr_side, dest_side) { - (Side::A, Side::A) | (Side::B, Side::B) => { - match dest_at { - // Cannot move on top of a friendly - Some(to_piece) => { - (!self.line(from, to) - .any(|board_index| self.at(board_index) - .is_some()) - ).then(|| { + self.line(from, to).all(|board_index| self.at(board_index).is_none()).then(|| { + self.at(from).map(|piece| { + // Given that the side does not have a queen||drone + // And the piece is a drone||pawn + // We can do field promotions + let side = Board::side(from).expect("Piece has valid index"); + let side_has_queen = self.on(side).iter().any(|(piece, _)| **piece == Piece::Queen); + let side_has_drone = self.on(side).iter().any(|(piece, _)| **piece == Piece::Drone); + + // Iterate over the piece's moves + piece + .moves_at(&from) + .iter() + // Find if the given `to` move is one of those + .find(|idx| to == **idx) + // Determine if this is valid/legal in this situation + .and_then(|_| { + let dest_at = self.at(to); + let curr_side = Board::side(from).unwrap(); + let dest_side = Board::side(to).unwrap(); + + match (curr_side, dest_side) { + (Side::A, Side::A) | (Side::B, Side::B) => { + match dest_at { + // Cannot move on top of a friendly + Some(to_piece) => { match (piece, to_piece) { (Piece::Pawn, Piece::Pawn) => { (!side_has_drone).then_some(MoveType::Merge) @@ -553,50 +550,38 @@ impl Board { Some(MoveType::Invalid) } } - }).flatten() - }, - // Any other spot is valid - None => { - // If there is another piece between A and B - (!self - .line(from, to) - .any(|board_index| self.at(board_index).is_some())) - .then_some(MoveType::Valid) + }, + // Any other spot is valid + None => { + Some(MoveType::Valid) + } } } - } - // Check for moving across the canal - (Side::A, Side::B) | (Side::B, Side::A) => { - match dest_at { - Some(_) => { - // If there is no other piece between A and B - (!self - .line(from, to) - .any(|board_index| self.at(board_index).is_some())) - .then_some(MoveType::Capture) - } - None => { - // move is valid if it does not un-do the previous move - match self.moves.last() { - Some(previous) => { - (previous.from != to).then_some(MoveType::Valid) - } - // First move in the game, this is valid (and impossible) - None => { - // If there is no other pieces between A and B - // the move is valid - (!self - .line(from, to) - .any(|board_index| self.at(board_index).is_some())) - .then_some(MoveType::Valid) + // Check for moving across the canal + (Side::A, Side::B) | (Side::B, Side::A) => { + match dest_at { + Some(_) => { + Some(MoveType::Capture) + } + None => { + // move is valid if it does not un-do the previous move + match self.moves.last() { + Some(previous) => { + (previous.from != to).then_some(MoveType::Valid) + } + // First move in the game, this is valid (and impossible) + None => { + // the move is valid + Some(MoveType::Valid) + } } } } } } - } + }) }) - }).flatten().or(Some(MoveType::Invalid)) + }).flatten().flatten().or(Some(MoveType::Invalid)) } /// Returns the possible moves the piece at this tile can make. @@ -604,9 +589,12 @@ impl Board { if let Some(piece) = self.at(current_board_index) { piece.moves_at(¤t_board_index).iter().filter_map(|move_index| { // Get the move type (or none if totally invalid) - match self.move_type(current_board_index, *move_index) { + let result = self.move_type(current_board_index, *move_index); + match result { None | Some(MoveType::Invalid) => None, - _ => Some(*move_index), + Some(MoveType::Capture) | Some(MoveType::Merge) | Some(MoveType::Valid) => { + Some(*move_index) + }, } }) .collect() @@ -738,7 +726,7 @@ mod test { /// When an enemy is on one side, can move to capture just that piece #[test] - fn case_03() { + fn case_01() { let board = Board::from_ascii( r#"........ ........ @@ -798,7 +786,6 @@ mod test { (5, 0).into(), (5, 2).into(), ]); - println!("Moves: {:?}", Piece::Queen.moves_at(&(4, 1).into())); assert_eq!( expected, given, "Mostly blocked queen, moves include captures" @@ -807,7 +794,7 @@ mod test { } #[test] - fn case_01() { + fn case_03() { let board = Board::from_ascii( r#".p.....q dp.p.pdq @@ -839,7 +826,38 @@ mod test { ]); assert_eq!(expected, given); } + } + + // A strange failure mode in game which I suspect comes from + // a disconnect between board logic and display plumbing + #[test] + fn case_04() { + let mut board = Board::from_ascii( + r#".....dqq + dpp..pdq + qdp....d + qqd....."#, + ); + + { + board.move_piece((5, 3).into(), (4, 3).into()).unwrap(); + board.move_piece((2, 2).into(), (1, 3).into()).unwrap(); + + let expected = Board::from_ascii( + r#".p..d#qq + dp...pdq + qdp....d + qqd....."#, + ); + assert_eq!(expected.inner, board.inner); + + } + { + let given = board.valid_moves((6, 3).into()); + let expected: HashSet = HashSet::from([(5, 3).into()]); + assert_eq!(expected, given); + } } #[test] @@ -1328,7 +1346,9 @@ fn show_valid_moves( events.iter().for_each(|idx| { // Iterate over all ValidMove entities // For each one with a ValidMove index, make it visible - board.valid_moves(*idx).iter().for_each(|idx| { + let valid_moves = board.valid_moves(*idx); + info!("Showing valid moves for {:?}: {:?}", idx, valid_moves); + valid_moves.iter().for_each(|idx| { indicators.iter_mut().for_each(|(i, mut vis)| { if i == idx { *vis = Visibility::Inherited;