|
|
|
|
@ -157,6 +157,9 @@ pub(crate) struct ValidMove;
|
|
|
|
|
#[derive(Debug, Component)]
|
|
|
|
|
pub(crate) struct Captured;
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Component)]
|
|
|
|
|
pub(crate) struct Merged;
|
|
|
|
|
|
|
|
|
|
// manually for the type.
|
|
|
|
|
impl std::fmt::Display for Piece {
|
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
|
|
|
@ -242,6 +245,7 @@ pub(crate) struct Move {
|
|
|
|
|
pub epoch: usize,
|
|
|
|
|
pub from: BoardIndex,
|
|
|
|
|
pub to: Option<BoardIndex>,
|
|
|
|
|
pub move_type: MoveType,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Enum for the Capture event flow
|
|
|
|
|
@ -290,12 +294,13 @@ impl From<(usize, usize)> for BoardIndex {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
|
|
|
#[derive(Debug, PartialEq, Clone, Default)]
|
|
|
|
|
pub(crate) enum MoveType {
|
|
|
|
|
Valid,
|
|
|
|
|
#[default]
|
|
|
|
|
Invalid,
|
|
|
|
|
Capture,
|
|
|
|
|
Merge,
|
|
|
|
|
Merge(Piece),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Component, PartialEq, Clone, Copy, Eq, Hash, Default)]
|
|
|
|
|
@ -367,10 +372,15 @@ impl Board {
|
|
|
|
|
|
|
|
|
|
fn new() -> Board {
|
|
|
|
|
Board::from_ascii(
|
|
|
|
|
r#".....dqq
|
|
|
|
|
dpp..pdq
|
|
|
|
|
qdp..ppd
|
|
|
|
|
qqd....."#,
|
|
|
|
|
// r#".....dqq
|
|
|
|
|
// dpp..pdq
|
|
|
|
|
// qdp..ppd
|
|
|
|
|
// qqd....."#,
|
|
|
|
|
|
|
|
|
|
r#"........
|
|
|
|
|
..p.p...
|
|
|
|
|
...p....
|
|
|
|
|
........"#,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -429,11 +439,16 @@ impl Board {
|
|
|
|
|
} else {
|
|
|
|
|
match self.at(from) {
|
|
|
|
|
Some(from_piece) => {
|
|
|
|
|
// Check if this is a valid move for this piece
|
|
|
|
|
if self.valid_moves(from).contains(&to) {
|
|
|
|
|
let move_type = self.move_type(from, to);
|
|
|
|
|
match move_type {
|
|
|
|
|
MoveType::Invalid => {
|
|
|
|
|
Err(GameError::InvalidMove)
|
|
|
|
|
},
|
|
|
|
|
MoveType::Valid | MoveType::Capture | MoveType::Merge(..) => {
|
|
|
|
|
// The current epoch is the last epoch + 1
|
|
|
|
|
let epoch = self.current_epoch();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Local moves vec we can return
|
|
|
|
|
let mut moves = vec![];
|
|
|
|
|
|
|
|
|
|
@ -443,6 +458,7 @@ impl Board {
|
|
|
|
|
epoch,
|
|
|
|
|
from: to,
|
|
|
|
|
to: None,
|
|
|
|
|
move_type: move_type.clone(),
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -451,6 +467,7 @@ impl Board {
|
|
|
|
|
epoch,
|
|
|
|
|
from,
|
|
|
|
|
to: Some(to),
|
|
|
|
|
move_type: move_type.clone(),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
self.inner[to.y][to.x] = Some(*from_piece);
|
|
|
|
|
@ -459,8 +476,7 @@ impl Board {
|
|
|
|
|
self.moves.extend(moves.clone());
|
|
|
|
|
|
|
|
|
|
Ok(moves)
|
|
|
|
|
} else {
|
|
|
|
|
Err(GameError::InvalidMove)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
None => Err(GameError::NullMove),
|
|
|
|
|
@ -512,8 +528,10 @@ impl Board {
|
|
|
|
|
&self,
|
|
|
|
|
from: BoardIndex,
|
|
|
|
|
to: BoardIndex,
|
|
|
|
|
) -> Option<MoveType> {
|
|
|
|
|
self.line(from, to).all(|board_index| self.at(board_index).is_none()).then(|| {
|
|
|
|
|
) -> 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
|
|
|
|
|
@ -541,10 +559,10 @@ impl Board {
|
|
|
|
|
Some(to_piece) => {
|
|
|
|
|
match (piece, to_piece) {
|
|
|
|
|
(Piece::Pawn, Piece::Pawn) => {
|
|
|
|
|
(!side_has_drone).then_some(MoveType::Merge)
|
|
|
|
|
(!side_has_drone).then_some(MoveType::Merge(Piece::Drone))
|
|
|
|
|
}
|
|
|
|
|
(Piece::Drone, Piece::Pawn) | (Piece::Pawn, Piece::Drone) => {
|
|
|
|
|
(!side_has_queen).then_some(MoveType::Merge)
|
|
|
|
|
(!side_has_queen).then_some(MoveType::Merge(Piece::Queen))
|
|
|
|
|
},
|
|
|
|
|
_ => {
|
|
|
|
|
Some(MoveType::Invalid)
|
|
|
|
|
@ -581,7 +599,7 @@ impl Board {
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
}).flatten().flatten().or(Some(MoveType::Invalid))
|
|
|
|
|
}).flatten().flatten().or(Some(MoveType::Invalid)).unwrap()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Returns the possible moves the piece at this tile can make.
|
|
|
|
|
@ -591,8 +609,8 @@ impl Board {
|
|
|
|
|
// Get the move type (or none if totally invalid)
|
|
|
|
|
let result = self.move_type(current_board_index, *move_index);
|
|
|
|
|
match result {
|
|
|
|
|
None | Some(MoveType::Invalid) => None,
|
|
|
|
|
Some(MoveType::Capture) | Some(MoveType::Merge) | Some(MoveType::Valid) => {
|
|
|
|
|
MoveType::Invalid => None,
|
|
|
|
|
MoveType::Capture | MoveType::Merge(..) | MoveType::Valid => {
|
|
|
|
|
Some(*move_index)
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
@ -751,7 +769,7 @@ mod test {
|
|
|
|
|
assert_eq!(expected, given, "Pawn can merge to make a drone");
|
|
|
|
|
|
|
|
|
|
let move_type = board.move_type((0, 0).into(), (1, 1).into());
|
|
|
|
|
assert_eq!(Some(MoveType::Merge), move_type);
|
|
|
|
|
assert_eq!(MoveType::Merge(Piece::Drone), move_type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Pawn cannot merge with queen
|
|
|
|
|
@ -892,20 +910,17 @@ mod test {
|
|
|
|
|
let actual = board.valid_moves((3, 1).into());
|
|
|
|
|
assert_eq!(expected, actual);
|
|
|
|
|
|
|
|
|
|
// Merging drone and pawn to make a queen
|
|
|
|
|
let capture_move = board.move_type((3, 1).into(), (5, 1).into());
|
|
|
|
|
assert_eq!(Some(MoveType::Capture), capture_move);
|
|
|
|
|
assert_eq!(MoveType::Capture, capture_move);
|
|
|
|
|
|
|
|
|
|
// Merging does not cross the canal
|
|
|
|
|
let merge_move = board.move_type((3, 1).into(), (2, 1).into());
|
|
|
|
|
assert_eq!(Some(MoveType::Merge), merge_move);
|
|
|
|
|
assert_eq!(MoveType::Merge(Piece::Queen), merge_move);
|
|
|
|
|
|
|
|
|
|
let long_merge_move = board.move_type((3, 1).into(), (3, 3).into());
|
|
|
|
|
assert_eq!(Some(MoveType::Merge), long_merge_move);
|
|
|
|
|
assert_eq!(MoveType::Merge(Piece::Queen), long_merge_move);
|
|
|
|
|
|
|
|
|
|
// Still cannot cross over friendlies
|
|
|
|
|
let jump_move = board.move_type((3, 1).into(), (1, 1).into());
|
|
|
|
|
assert_eq!(Some(MoveType::Invalid), jump_move);
|
|
|
|
|
assert_eq!(MoveType::Invalid, jump_move);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -926,19 +941,19 @@ mod test {
|
|
|
|
|
|
|
|
|
|
// Pawn + Pawn on same side = Promotion
|
|
|
|
|
let merge_move = board.move_type((3, 1).into(), (2, 2).into());
|
|
|
|
|
assert_eq!(Some(MoveType::Merge), merge_move);
|
|
|
|
|
assert_eq!(MoveType::Merge(Piece::Drone), merge_move);
|
|
|
|
|
|
|
|
|
|
// Pawn + Pawn on other side = Capture
|
|
|
|
|
let capture_move = board.move_type((3, 1).into(), (4, 2).into());
|
|
|
|
|
assert_eq!(Some(MoveType::Capture), capture_move);
|
|
|
|
|
assert_eq!(MoveType::Capture, capture_move);
|
|
|
|
|
|
|
|
|
|
// Pawn + Empty = Move
|
|
|
|
|
let place_move = board.move_type((3, 1).into(), (2, 0).into());
|
|
|
|
|
assert_eq!(Some(MoveType::Valid), place_move);
|
|
|
|
|
assert_eq!(MoveType::Valid, place_move);
|
|
|
|
|
|
|
|
|
|
// Pawn + Invalid Empty = Invalid
|
|
|
|
|
let invalid_move = board.move_type((3, 1).into(), (7, 7).into());
|
|
|
|
|
assert_eq!(Some(MoveType::Invalid), invalid_move);
|
|
|
|
|
assert_eq!(MoveType::Invalid, invalid_move);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@ -992,30 +1007,63 @@ pub(crate) fn update_board(
|
|
|
|
|
curr_state: Res<State<TurnState>>,
|
|
|
|
|
mut next_state: ResMut<NextState<TurnState>>,
|
|
|
|
|
) {
|
|
|
|
|
events.read().for_each(|Move { from, to, .. }| {
|
|
|
|
|
// 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) => {
|
|
|
|
|
info!("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::Merge(piece) => {
|
|
|
|
|
error!("Hey Eli, this is where you are merging pieces");
|
|
|
|
|
// Here we insert a new Piece type based on the merge
|
|
|
|
|
// Other system should automatically update the model
|
|
|
|
|
commands
|
|
|
|
|
.entity(entity)
|
|
|
|
|
.insert(*piece);
|
|
|
|
|
}
|
|
|
|
|
_ => ()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let ns = !*curr_state.get();
|
|
|
|
|
info!("Piece moved, switching sides: {:?}", ns);
|
|
|
|
|
next_state.set(ns);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// We are moving off the board (e.g,. capture or merge)
|
|
|
|
|
None => {
|
|
|
|
|
match move_type {
|
|
|
|
|
MoveType::Capture => {
|
|
|
|
|
info!("Capturing piece {:?}", entity);
|
|
|
|
|
commands
|
|
|
|
|
.entity(entity)
|
|
|
|
|
.remove::<BoardIndex>()
|
|
|
|
|
.insert(Captured);
|
|
|
|
|
},
|
|
|
|
|
MoveType::Merge(..) => {
|
|
|
|
|
commands
|
|
|
|
|
.entity(entity)
|
|
|
|
|
.remove::<BoardIndex>()
|
|
|
|
|
.insert(Merged);
|
|
|
|
|
},
|
|
|
|
|
_ => {
|
|
|
|
|
panic!("How did you do this!?");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|