|
|
|
|
@ -15,7 +15,7 @@ impl Plugin for GamePlugin {
|
|
|
|
|
(
|
|
|
|
|
manage_state_entities::<GameState>().run_if(state_changed::<GameState>),
|
|
|
|
|
undo_move.run_if(just_pressed(KeyCode::KeyU)),
|
|
|
|
|
)
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
.add_systems(
|
|
|
|
|
Update,
|
|
|
|
|
@ -29,19 +29,19 @@ impl Plugin for GamePlugin {
|
|
|
|
|
hide_valid_moves.run_if(any_component_removed::<Selected>()),
|
|
|
|
|
manage_score.run_if(any_component_added::<Captured>()),
|
|
|
|
|
check_endgame.run_if(resource_changed::<Board>),
|
|
|
|
|
).run_if(in_state(GameState::Play)),
|
|
|
|
|
)
|
|
|
|
|
.run_if(in_state(GameState::Play)),
|
|
|
|
|
)
|
|
|
|
|
.add_systems(Update,
|
|
|
|
|
.add_systems(
|
|
|
|
|
Update,
|
|
|
|
|
assert_piece_consistency
|
|
|
|
|
.run_if(in_state(GameState::Play))
|
|
|
|
|
.run_if(resource_changed::<Score>)
|
|
|
|
|
.run_if(resource_changed::<Score>),
|
|
|
|
|
)
|
|
|
|
|
.add_systems(Update, reset_game.run_if(in_state(GameState::Restart)))
|
|
|
|
|
.add_systems(OnEnter(GameState::Endgame),
|
|
|
|
|
(
|
|
|
|
|
manage_score,
|
|
|
|
|
set_endgame.after(manage_score),
|
|
|
|
|
)
|
|
|
|
|
.add_systems(
|
|
|
|
|
OnEnter(GameState::Endgame),
|
|
|
|
|
(manage_score, set_endgame.after(manage_score)),
|
|
|
|
|
)
|
|
|
|
|
.add_systems(OnExit(GameState::Endgame), clear_endgame)
|
|
|
|
|
.add_systems(
|
|
|
|
|
@ -132,12 +132,16 @@ impl Piece {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn moves_at(&self, from: &BoardIndex) -> HashSet<BoardIndex> {
|
|
|
|
|
self.moves().filter_map(|(x, y)| {
|
|
|
|
|
let bi = (from.x as isize + x, from.y as isize + y);
|
|
|
|
|
// Check if this goes out of bounds, if so exclude from the list of possible moves
|
|
|
|
|
(bi.0 <= 7 && bi.0 >= 0 && bi.1 <= 3 && bi.1 >= 0).then_some(BoardIndex { x: bi.0 as usize, y: bi.1 as usize })
|
|
|
|
|
})
|
|
|
|
|
.collect()
|
|
|
|
|
self.moves()
|
|
|
|
|
.filter_map(|(x, y)| {
|
|
|
|
|
let bi = (from.x as isize + x, from.y as isize + y);
|
|
|
|
|
// Check if this goes out of bounds, if so exclude from the list of possible moves
|
|
|
|
|
(bi.0 <= 7 && bi.0 >= 0 && bi.1 <= 3 && bi.1 >= 0).then_some(BoardIndex {
|
|
|
|
|
x: bi.0 as usize,
|
|
|
|
|
y: bi.1 as usize,
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
.collect()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn value(&self) -> usize {
|
|
|
|
|
@ -434,9 +438,7 @@ impl Board {
|
|
|
|
|
Some(from_piece) => {
|
|
|
|
|
let move_type = self.move_type(from, to);
|
|
|
|
|
match move_type {
|
|
|
|
|
MoveType::Invalid => {
|
|
|
|
|
Err(GameError::InvalidMove)
|
|
|
|
|
},
|
|
|
|
|
MoveType::Invalid => Err(GameError::InvalidMove),
|
|
|
|
|
MoveType::Valid | MoveType::Capture | MoveType::Promotion(..) => {
|
|
|
|
|
// The current epoch is the last epoch + 1
|
|
|
|
|
let epoch = self.current_epoch();
|
|
|
|
|
@ -465,18 +467,15 @@ impl Board {
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
self.inner[to.y][to.x] = match move_type {
|
|
|
|
|
MoveType::Promotion(_) => {
|
|
|
|
|
match (self.at(from), self.at(to)) {
|
|
|
|
|
(Some(Piece::Pawn), Some(Piece::Pawn)) => {
|
|
|
|
|
Some(Piece::Drone)
|
|
|
|
|
},
|
|
|
|
|
(Some(Piece::Pawn), Some(Piece::Drone)) | (Some(Piece::Drone), Some(Piece::Pawn)) => {
|
|
|
|
|
Some(Piece::Queen)
|
|
|
|
|
},
|
|
|
|
|
_ => panic!("Merges can only happen between pawn+pawn or pawn+drone!")
|
|
|
|
|
}
|
|
|
|
|
MoveType::Promotion(_) => match (self.at(from), self.at(to)) {
|
|
|
|
|
(Some(Piece::Pawn), Some(Piece::Pawn)) => Some(Piece::Drone),
|
|
|
|
|
(Some(Piece::Pawn), Some(Piece::Drone))
|
|
|
|
|
| (Some(Piece::Drone), Some(Piece::Pawn)) => Some(Piece::Queen),
|
|
|
|
|
_ => panic!(
|
|
|
|
|
"Merges can only happen between pawn+pawn or pawn+drone!"
|
|
|
|
|
),
|
|
|
|
|
},
|
|
|
|
|
_ => Some(*from_piece)
|
|
|
|
|
_ => Some(*from_piece),
|
|
|
|
|
};
|
|
|
|
|
self.inner[from.y][from.x] = None;
|
|
|
|
|
|
|
|
|
|
@ -554,100 +553,101 @@ impl Board {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Determine given a piece, a to, and a from, what type of move this would be
|
|
|
|
|
pub(crate) fn move_type(
|
|
|
|
|
&self,
|
|
|
|
|
from: BoardIndex,
|
|
|
|
|
to: BoardIndex,
|
|
|
|
|
) -> MoveType {
|
|
|
|
|
pub(crate) fn move_type(&self, from: BoardIndex, to: BoardIndex) -> MoveType {
|
|
|
|
|
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::Promotion(Piece::Drone))
|
|
|
|
|
}
|
|
|
|
|
(Piece::Drone, Piece::Pawn) | (Piece::Pawn, Piece::Drone) => {
|
|
|
|
|
(!side_has_queen).then_some(MoveType::Promotion(Piece::Queen))
|
|
|
|
|
},
|
|
|
|
|
_ => {
|
|
|
|
|
Some(MoveType::Invalid)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
// Any other spot is valid
|
|
|
|
|
None => {
|
|
|
|
|
Some(MoveType::Valid)
|
|
|
|
|
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::Promotion(Piece::Drone)),
|
|
|
|
|
(Piece::Drone, Piece::Pawn)
|
|
|
|
|
| (Piece::Pawn, Piece::Drone) => (!side_has_queen)
|
|
|
|
|
.then_some(MoveType::Promotion(Piece::Queen)),
|
|
|
|
|
_ => Some(MoveType::Invalid),
|
|
|
|
|
},
|
|
|
|
|
// 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(_) => {
|
|
|
|
|
Some(MoveType::Capture)
|
|
|
|
|
}
|
|
|
|
|
None => {
|
|
|
|
|
debug!("Last move: {:?}", self.moves.last());
|
|
|
|
|
// move is valid if it does not un-do the previous move for this piece
|
|
|
|
|
match self.moves.last() {
|
|
|
|
|
Some(previous) => {
|
|
|
|
|
let is_undo = previous.from == to && previous.to == Some(from);
|
|
|
|
|
(!is_undo).then_some(MoveType::Valid)
|
|
|
|
|
}
|
|
|
|
|
// First move in the game, this is valid (and impossible)
|
|
|
|
|
None => {
|
|
|
|
|
// the move is valid
|
|
|
|
|
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 => {
|
|
|
|
|
debug!("Last move: {:?}", self.moves.last());
|
|
|
|
|
// move is valid if it does not un-do the previous move for this piece
|
|
|
|
|
match self.moves.last() {
|
|
|
|
|
Some(previous) => {
|
|
|
|
|
let is_undo = previous.from == to
|
|
|
|
|
&& previous.to == Some(from);
|
|
|
|
|
(!is_undo).then_some(MoveType::Valid)
|
|
|
|
|
}
|
|
|
|
|
// First move in the game, this is valid (and impossible)
|
|
|
|
|
None => {
|
|
|
|
|
// the move is valid
|
|
|
|
|
Some(MoveType::Valid)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
}).flatten().flatten().or(Some(MoveType::Invalid)).unwrap()
|
|
|
|
|
})
|
|
|
|
|
.flatten()
|
|
|
|
|
.flatten()
|
|
|
|
|
.or(Some(MoveType::Invalid))
|
|
|
|
|
.unwrap()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Returns the possible moves the piece at this tile can make.
|
|
|
|
|
pub(crate) fn valid_moves(&self, current_board_index: BoardIndex) -> HashSet<BoardIndex> {
|
|
|
|
|
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)
|
|
|
|
|
let result = self.move_type(current_board_index, *move_index);
|
|
|
|
|
match result {
|
|
|
|
|
MoveType::Invalid => None,
|
|
|
|
|
MoveType::Capture | MoveType::Promotion(..) | MoveType::Valid => {
|
|
|
|
|
Some(*move_index)
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.collect()
|
|
|
|
|
piece
|
|
|
|
|
.moves_at(¤t_board_index)
|
|
|
|
|
.iter()
|
|
|
|
|
.filter_map(|move_index| {
|
|
|
|
|
// Get the move type (or none if totally invalid)
|
|
|
|
|
let result = self.move_type(current_board_index, *move_index);
|
|
|
|
|
match result {
|
|
|
|
|
MoveType::Invalid => None,
|
|
|
|
|
MoveType::Capture | MoveType::Promotion(..) | MoveType::Valid => {
|
|
|
|
|
Some(*move_index)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.collect()
|
|
|
|
|
} else {
|
|
|
|
|
HashSet::new()
|
|
|
|
|
}
|
|
|
|
|
@ -730,7 +730,11 @@ mod test {
|
|
|
|
|
(7, 2).into(),
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
assert_eq!(expected, Piece::Queen.moves_at(&(4, 2).into()), "Generated queen moves");
|
|
|
|
|
assert_eq!(
|
|
|
|
|
expected,
|
|
|
|
|
Piece::Queen.moves_at(&(4, 2).into()),
|
|
|
|
|
"Generated queen moves"
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(expected, given, "Basic queen moves");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -851,7 +855,8 @@ mod test {
|
|
|
|
|
// Pawn cannot merge with queen
|
|
|
|
|
{
|
|
|
|
|
let given = board.valid_moves((2, 1).into());
|
|
|
|
|
let expected: HashSet<BoardIndex> = HashSet::from([(3, 0).into(), (3, 2).into(), (1, 2).into()]);
|
|
|
|
|
let expected: HashSet<BoardIndex> =
|
|
|
|
|
HashSet::from([(3, 0).into(), (3, 2).into(), (1, 2).into()]);
|
|
|
|
|
assert_eq!(expected, given, "Pawn cannot merge with queen");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@ -870,18 +875,17 @@ mod test {
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
let given = board.valid_moves((4, 1).into());
|
|
|
|
|
let expected: HashSet<BoardIndex> =
|
|
|
|
|
HashSet::from([
|
|
|
|
|
(1, 1).into(),
|
|
|
|
|
(2, 1).into(),
|
|
|
|
|
(2, 3).into(),
|
|
|
|
|
(3, 0).into(),
|
|
|
|
|
(3, 1).into(),
|
|
|
|
|
(3, 2).into(),
|
|
|
|
|
(4, 0).into(),
|
|
|
|
|
(5, 0).into(),
|
|
|
|
|
(5, 2).into(),
|
|
|
|
|
]);
|
|
|
|
|
let expected: HashSet<BoardIndex> = HashSet::from([
|
|
|
|
|
(1, 1).into(),
|
|
|
|
|
(2, 1).into(),
|
|
|
|
|
(2, 3).into(),
|
|
|
|
|
(3, 0).into(),
|
|
|
|
|
(3, 1).into(),
|
|
|
|
|
(3, 2).into(),
|
|
|
|
|
(4, 0).into(),
|
|
|
|
|
(5, 0).into(),
|
|
|
|
|
(5, 2).into(),
|
|
|
|
|
]);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
expected, given,
|
|
|
|
|
"Mostly blocked queen, moves include captures"
|
|
|
|
|
@ -908,20 +912,13 @@ mod test {
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
let given = board.valid_moves((1, 0).into());
|
|
|
|
|
let expected: HashSet<BoardIndex> =
|
|
|
|
|
HashSet::from([
|
|
|
|
|
(2, 1).into()
|
|
|
|
|
]);
|
|
|
|
|
let expected: HashSet<BoardIndex> = HashSet::from([(2, 1).into()]);
|
|
|
|
|
assert_eq!(expected, given);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
let given = board.valid_moves((2, 0).into());
|
|
|
|
|
let expected: HashSet<BoardIndex> =
|
|
|
|
|
HashSet::from([
|
|
|
|
|
(2, 1).into(),
|
|
|
|
|
(2, 2).into(),
|
|
|
|
|
]);
|
|
|
|
|
let expected: HashSet<BoardIndex> = HashSet::from([(2, 1).into(), (2, 2).into()]);
|
|
|
|
|
assert_eq!(expected, given);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@ -950,7 +947,6 @@ mod test {
|
|
|
|
|
qqd....."#,
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(expected.inner, board.inner);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
@ -966,7 +962,13 @@ mod test {
|
|
|
|
|
|
|
|
|
|
let given = Piece::Drone.moves_at(&(4, 1).into());
|
|
|
|
|
let expected: HashSet<BoardIndex> = HashSet::from([
|
|
|
|
|
(4, 0).into(), (2, 1).into(), (3, 1).into(), (5, 1).into(), (6, 1).into(), (4, 2).into(), (4, 3).into(),
|
|
|
|
|
(4, 0).into(),
|
|
|
|
|
(2, 1).into(),
|
|
|
|
|
(3, 1).into(),
|
|
|
|
|
(5, 1).into(),
|
|
|
|
|
(6, 1).into(),
|
|
|
|
|
(4, 2).into(),
|
|
|
|
|
(4, 3).into(),
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
assert_eq!(expected, given, "Drone moves at");
|
|
|
|
|
@ -1024,7 +1026,8 @@ mod test {
|
|
|
|
|
{
|
|
|
|
|
// All normal moves for pawn are valid
|
|
|
|
|
let given = board.valid_moves((3, 1).into());
|
|
|
|
|
let expected: HashSet<BoardIndex> = HashSet::from([(2, 0).into(), (4, 0).into(), (2, 2).into(), (4, 2).into()]);
|
|
|
|
|
let expected: HashSet<BoardIndex> =
|
|
|
|
|
HashSet::from([(2, 0).into(), (4, 0).into(), (2, 2).into(), (4, 2).into()]);
|
|
|
|
|
assert_eq!(expected, given);
|
|
|
|
|
|
|
|
|
|
// Pawn + Pawn on same side = Promotion
|
|
|
|
|
@ -1096,42 +1099,45 @@ pub(crate) fn update_board(
|
|
|
|
|
mut next_state: ResMut<NextState<TurnState>>,
|
|
|
|
|
) {
|
|
|
|
|
// Each move event we get
|
|
|
|
|
events.read().for_each(|Move { from, to, move_type, .. }| {
|
|
|
|
|
// Iterate over all pieces
|
|
|
|
|
pieces.iter_mut().for_each(|(entity, mut index)| {
|
|
|
|
|
// If the current index is the 'from' for the move
|
|
|
|
|
// All moves cover From -> To (captures and merges have to: None)
|
|
|
|
|
if *index == *from {
|
|
|
|
|
match to {
|
|
|
|
|
// If we are moving on the board...
|
|
|
|
|
Some(to_idx) => {
|
|
|
|
|
debug!("Moving piece {:?} {:?} -> {:?}", entity, from, to_idx);
|
|
|
|
|
// Update the piece's index
|
|
|
|
|
*index = *to_idx;
|
|
|
|
|
// Play audio sfx
|
|
|
|
|
if !(*played) {
|
|
|
|
|
audio_events.send(audio::AudioEvent::PutDown);
|
|
|
|
|
audio_events.send(audio::AudioEvent::StopIdle);
|
|
|
|
|
*played = true;
|
|
|
|
|
}
|
|
|
|
|
if *from != *to_idx {
|
|
|
|
|
match move_type {
|
|
|
|
|
MoveType::Promotion(piece) => {
|
|
|
|
|
commands
|
|
|
|
|
.entity(entity)
|
|
|
|
|
.insert(*piece);
|
|
|
|
|
}
|
|
|
|
|
_ => ()
|
|
|
|
|
events.read().for_each(
|
|
|
|
|
|Move {
|
|
|
|
|
from,
|
|
|
|
|
to,
|
|
|
|
|
move_type,
|
|
|
|
|
..
|
|
|
|
|
}| {
|
|
|
|
|
// Iterate over all pieces
|
|
|
|
|
pieces.iter_mut().for_each(|(entity, mut index)| {
|
|
|
|
|
// If the current index is the 'from' for the move
|
|
|
|
|
// All moves cover From -> To (captures and merges have to: None)
|
|
|
|
|
if *index == *from {
|
|
|
|
|
match to {
|
|
|
|
|
// If we are moving on the board...
|
|
|
|
|
Some(to_idx) => {
|
|
|
|
|
debug!("Moving piece {:?} {:?} -> {:?}", entity, from, to_idx);
|
|
|
|
|
// Update the piece's index
|
|
|
|
|
*index = *to_idx;
|
|
|
|
|
// Play audio sfx
|
|
|
|
|
if !(*played) {
|
|
|
|
|
audio_events.send(audio::AudioEvent::PutDown);
|
|
|
|
|
audio_events.send(audio::AudioEvent::StopIdle);
|
|
|
|
|
*played = true;
|
|
|
|
|
}
|
|
|
|
|
if *from != *to_idx {
|
|
|
|
|
match move_type {
|
|
|
|
|
MoveType::Promotion(piece) => {
|
|
|
|
|
commands.entity(entity).insert(*piece);
|
|
|
|
|
}
|
|
|
|
|
_ => (),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let ns = !*curr_state.get();
|
|
|
|
|
debug!("Piece moved, switching sides: {:?}", ns);
|
|
|
|
|
next_state.set(ns);
|
|
|
|
|
let ns = !*curr_state.get();
|
|
|
|
|
debug!("Piece moved, switching sides: {:?}", ns);
|
|
|
|
|
next_state.set(ns);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// We are moving off the board (e.g,. capture or promotion/merge)
|
|
|
|
|
None => {
|
|
|
|
|
match move_type {
|
|
|
|
|
// We are moving off the board (e.g,. capture or promotion/merge)
|
|
|
|
|
None => match move_type {
|
|
|
|
|
MoveType::Capture => {
|
|
|
|
|
debug!("Capturing piece {:?}", entity);
|
|
|
|
|
commands
|
|
|
|
|
@ -1139,26 +1145,26 @@ pub(crate) fn update_board(
|
|
|
|
|
.remove::<BoardIndex>()
|
|
|
|
|
.insert(BeingCaptured);
|
|
|
|
|
audio_events.send(AudioEvent::Captured);
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
MoveType::Promotion(..) => {
|
|
|
|
|
commands
|
|
|
|
|
.entity(entity)
|
|
|
|
|
.remove::<BoardIndex>()
|
|
|
|
|
.insert((Promoted, Visibility::Hidden));
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
_ => {
|
|
|
|
|
panic!("How did you do this!?");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
selected.iter().for_each(|entity| {
|
|
|
|
|
debug!("De-selecting selected piece {:?}", entity);
|
|
|
|
|
commands.entity(entity).remove::<Selected>();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
selected.iter().for_each(|entity| {
|
|
|
|
|
debug!("De-selecting selected piece {:?}", entity);
|
|
|
|
|
commands.entity(entity).remove::<Selected>();
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
*played = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -1331,7 +1337,13 @@ fn clear_endgame(query: Query<Entity, With<Endgame>>, mut commands: Commands) {
|
|
|
|
|
/// * All captured pieces have their captured side preserved
|
|
|
|
|
/// We can iterate over these pieces and calculate the score on the fly
|
|
|
|
|
fn manage_score(
|
|
|
|
|
query: Query<(&Side, &Piece), (Or<(With<Captured>, With<BeingCaptured>)>, With<display3d::Display3d>)>,
|
|
|
|
|
query: Query<
|
|
|
|
|
(&Side, &Piece),
|
|
|
|
|
(
|
|
|
|
|
Or<(With<Captured>, With<BeingCaptured>)>,
|
|
|
|
|
With<display3d::Display3d>,
|
|
|
|
|
),
|
|
|
|
|
>,
|
|
|
|
|
mut debug_info: ResMut<debug::DebugInfo>,
|
|
|
|
|
mut score: ResMut<Score>,
|
|
|
|
|
) {
|
|
|
|
|
@ -1340,11 +1352,20 @@ fn manage_score(
|
|
|
|
|
score.captures_b = query.iter().filter(|(s, _)| **s == Side::A).count();
|
|
|
|
|
|
|
|
|
|
// Calculate score based on the piece values
|
|
|
|
|
score.score_a = query.iter().filter(|(s, _)| **s == Side::B).fold(0, |acc, (_, piece)| acc + piece.value());
|
|
|
|
|
score.score_b = query.iter().filter(|(s, _)| **s == Side::A).fold(0, |acc, (_, piece)| acc + piece.value());
|
|
|
|
|
score.score_a = query
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|(s, _)| **s == Side::B)
|
|
|
|
|
.fold(0, |acc, (_, piece)| acc + piece.value());
|
|
|
|
|
score.score_b = query
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|(s, _)| **s == Side::A)
|
|
|
|
|
.fold(0, |acc, (_, piece)| acc + piece.value());
|
|
|
|
|
|
|
|
|
|
// Debug this for good measure
|
|
|
|
|
debug_info.set("score".into(), format!("A:{}|B:{}", score.score_a, score.score_b));
|
|
|
|
|
debug_info.set(
|
|
|
|
|
"score".into(),
|
|
|
|
|
format!("A:{}|B:{}", score.score_a, score.score_b),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(crate) fn set_side(
|
|
|
|
|
@ -1517,7 +1538,8 @@ fn reset_game(
|
|
|
|
|
.iter()
|
|
|
|
|
.zip(board.pieces().iter())
|
|
|
|
|
.for_each(|(e, (i, p))| {
|
|
|
|
|
commands.entity(e)
|
|
|
|
|
commands
|
|
|
|
|
.entity(e)
|
|
|
|
|
.insert((*i, *p, Visibility::Inherited))
|
|
|
|
|
.remove::<(BeingCaptured, Captured, Promoted)>();
|
|
|
|
|
});
|
|
|
|
|
@ -1540,7 +1562,10 @@ fn assert_piece_consistency(
|
|
|
|
|
let active_count = active.iter().len();
|
|
|
|
|
let being_captured_count = being_captured.iter().len();
|
|
|
|
|
let captured_count = captured.iter().len();
|
|
|
|
|
debug!("Active: {} | being captured: {} | captured: {}", active_count, being_captured_count, captured_count);
|
|
|
|
|
debug!(
|
|
|
|
|
"Active: {} | being captured: {} | captured: {}",
|
|
|
|
|
active_count, being_captured_count, captured_count
|
|
|
|
|
);
|
|
|
|
|
let total_count = active_count + being_captured_count + captured_count;
|
|
|
|
|
assert_eq!(total_count, 18, "Pieces does does not add up!");
|
|
|
|
|
}
|
|
|
|
|
@ -1557,45 +1582,38 @@ fn undo_move(
|
|
|
|
|
// Keep track of the current side in case we need to go back
|
|
|
|
|
let mut side = turn.get().0;
|
|
|
|
|
|
|
|
|
|
board
|
|
|
|
|
.undo_move()
|
|
|
|
|
.iter()
|
|
|
|
|
.for_each(|Move { epoch, from, to, .. }| {
|
|
|
|
|
board.undo_move().iter().for_each(
|
|
|
|
|
|Move {
|
|
|
|
|
epoch, from, to, ..
|
|
|
|
|
}| {
|
|
|
|
|
// If we have any moves to do, go back to the opposite side
|
|
|
|
|
if side == turn.get().0 {
|
|
|
|
|
side = !turn.get().0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
debug!("Reverting move {:?} {:?} -> {:?}", epoch, from, to);
|
|
|
|
|
match to { Some(to_idx) => {
|
|
|
|
|
match to {
|
|
|
|
|
Some(to_idx) => {
|
|
|
|
|
debug!("Moving piece back from {:?}", to_idx);
|
|
|
|
|
|
|
|
|
|
// Find piece currently at "to_idx" and update it's position to "from"
|
|
|
|
|
active_pieces
|
|
|
|
|
.iter_mut()
|
|
|
|
|
.filter(|idx| {
|
|
|
|
|
(idx.x, idx.y) == (to_idx.x, to_idx.y)
|
|
|
|
|
})
|
|
|
|
|
.for_each(|mut idx| {
|
|
|
|
|
*idx = *from
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
.filter(|idx| (idx.x, idx.y) == (to_idx.x, to_idx.y))
|
|
|
|
|
.for_each(|mut idx| *idx = *from);
|
|
|
|
|
}
|
|
|
|
|
None => {
|
|
|
|
|
captured_pieces
|
|
|
|
|
.iter()
|
|
|
|
|
.find_map(|(entity, captured)| {
|
|
|
|
|
(captured.epoch == *epoch).then_some(entity)
|
|
|
|
|
})
|
|
|
|
|
.find_map(|(entity, captured)| (captured.epoch == *epoch).then_some(entity))
|
|
|
|
|
.iter()
|
|
|
|
|
.for_each(|entity| {
|
|
|
|
|
commands
|
|
|
|
|
.entity(*entity)
|
|
|
|
|
.remove::<Captured>()
|
|
|
|
|
.insert(*from);
|
|
|
|
|
commands.entity(*entity).remove::<Captured>().insert(*from);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Set the turn state (may be a no-op)
|
|
|
|
|
next_turn.set(TurnState(side));
|
|
|
|
|
|