|
|
|
|
@ -126,6 +126,14 @@ impl Piece {
|
|
|
|
|
})
|
|
|
|
|
.collect()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn value(&self) -> usize {
|
|
|
|
|
match self {
|
|
|
|
|
Piece::Pawn => 1,
|
|
|
|
|
Piece::Drone => 2,
|
|
|
|
|
Piece::Queen => 3,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Component, Clone, PartialEq)]
|
|
|
|
|
@ -153,6 +161,11 @@ pub(crate) struct BoardComponent;
|
|
|
|
|
#[derive(Debug, Component)]
|
|
|
|
|
pub(crate) struct ValidMove;
|
|
|
|
|
|
|
|
|
|
/// Marker for entity being captured
|
|
|
|
|
#[derive(Debug, Component)]
|
|
|
|
|
pub(crate) struct BeingCaptured;
|
|
|
|
|
|
|
|
|
|
/// Marker for component which is captured
|
|
|
|
|
#[derive(Debug, Component)]
|
|
|
|
|
pub(crate) struct Captured;
|
|
|
|
|
|
|
|
|
|
@ -180,35 +193,17 @@ pub(crate) enum GameError {
|
|
|
|
|
/// Tracks the score of each side of the game
|
|
|
|
|
#[derive(Debug, Resource, Default)]
|
|
|
|
|
pub(crate) struct Score {
|
|
|
|
|
a: usize,
|
|
|
|
|
b: usize,
|
|
|
|
|
score_a: usize,
|
|
|
|
|
score_b: usize,
|
|
|
|
|
captures_a: usize,
|
|
|
|
|
captures_b: usize,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Score {
|
|
|
|
|
fn increment(&mut self, side: Side, piece: Piece) {
|
|
|
|
|
let i = match piece {
|
|
|
|
|
Piece::Queen => 3,
|
|
|
|
|
Piece::Drone => 2,
|
|
|
|
|
Piece::Pawn => 1,
|
|
|
|
|
};
|
|
|
|
|
match side {
|
|
|
|
|
Side::A => {
|
|
|
|
|
self.captures_a += 1;
|
|
|
|
|
self.a += i;
|
|
|
|
|
}
|
|
|
|
|
Side::B => {
|
|
|
|
|
self.captures_b += 1;
|
|
|
|
|
self.b += i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(crate) fn _get(&self, side: Side) -> usize {
|
|
|
|
|
match side {
|
|
|
|
|
Side::A => self.a,
|
|
|
|
|
Side::B => self.b,
|
|
|
|
|
Side::A => self.score_a,
|
|
|
|
|
Side::B => self.score_b,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -633,10 +628,10 @@ impl Board {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mod test {
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn pawn_simple_moves() {
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
let board = Board::from_ascii(
|
|
|
|
|
r#"........
|
|
|
|
|
.#.#....
|
|
|
|
|
@ -652,6 +647,8 @@ mod test {
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn drone_simple_moves() {
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
let board = Board::from_ascii(
|
|
|
|
|
r#"..#.....
|
|
|
|
|
..#.....
|
|
|
|
|
@ -674,6 +671,8 @@ mod test {
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn queen_simple_moves() {
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
let board = Board::from_ascii(
|
|
|
|
|
r#"...###..
|
|
|
|
|
####q###
|
|
|
|
|
@ -706,6 +705,8 @@ mod test {
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn empty_moves() {
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
let board = Board::from_ascii(
|
|
|
|
|
r#"........
|
|
|
|
|
.p....q.
|
|
|
|
|
@ -721,6 +722,8 @@ mod test {
|
|
|
|
|
/// When a piece is blocked on all sides by friendly, cannot move
|
|
|
|
|
#[test]
|
|
|
|
|
fn blocking_friendly() {
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
let board = Board::from_ascii(
|
|
|
|
|
r#"p.......
|
|
|
|
|
..dp....
|
|
|
|
|
@ -751,6 +754,8 @@ mod test {
|
|
|
|
|
/// When an enemy is on one side, can move to capture just that piece
|
|
|
|
|
#[test]
|
|
|
|
|
fn case_01() {
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
let board = Board::from_ascii(
|
|
|
|
|
r#"........
|
|
|
|
|
........
|
|
|
|
|
@ -789,6 +794,8 @@ mod test {
|
|
|
|
|
/// Can move up to but not over friendlies
|
|
|
|
|
#[test]
|
|
|
|
|
fn case_02() {
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
let board = Board::from_ascii(
|
|
|
|
|
r#"..#...qq
|
|
|
|
|
dpp#d#d.
|
|
|
|
|
@ -819,6 +826,8 @@ mod test {
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn case_03() {
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
let board = Board::from_ascii(
|
|
|
|
|
r#".p.....q
|
|
|
|
|
dp.p.pdq
|
|
|
|
|
@ -856,6 +865,8 @@ mod test {
|
|
|
|
|
// a disconnect between board logic and display plumbing
|
|
|
|
|
#[test]
|
|
|
|
|
fn case_04() {
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
let mut board = Board::from_ascii(
|
|
|
|
|
r#".....dqq
|
|
|
|
|
dpp..pdq
|
|
|
|
|
@ -886,6 +897,8 @@ mod test {
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_piece_moves_at() {
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
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(),
|
|
|
|
|
@ -896,6 +909,8 @@ mod test {
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_capture_01() {
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
let board = Board::from_ascii(
|
|
|
|
|
r#"...p....
|
|
|
|
|
........
|
|
|
|
|
@ -932,6 +947,8 @@ mod test {
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_capture_02() {
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
let board = Board::from_ascii(
|
|
|
|
|
r#"........
|
|
|
|
|
..p.p...
|
|
|
|
|
@ -1055,7 +1072,7 @@ pub(crate) fn update_board(
|
|
|
|
|
commands
|
|
|
|
|
.entity(entity)
|
|
|
|
|
.remove::<BoardIndex>()
|
|
|
|
|
.insert(Captured);
|
|
|
|
|
.insert(BeingCaptured);
|
|
|
|
|
},
|
|
|
|
|
MoveType::Promotion(..) => {
|
|
|
|
|
commands
|
|
|
|
|
@ -1127,7 +1144,7 @@ fn set_endgame(
|
|
|
|
|
},
|
|
|
|
|
));
|
|
|
|
|
parent.spawn(TextBundle::from_section(
|
|
|
|
|
format!("BLUE {}", score.b),
|
|
|
|
|
format!("BLUE {}", score.score_b),
|
|
|
|
|
TextStyle {
|
|
|
|
|
font_size: 32.0,
|
|
|
|
|
color: Color::BLUE,
|
|
|
|
|
@ -1135,7 +1152,7 @@ fn set_endgame(
|
|
|
|
|
},
|
|
|
|
|
));
|
|
|
|
|
parent.spawn(TextBundle::from_section(
|
|
|
|
|
format!("RED {}", score.a),
|
|
|
|
|
format!("RED {}", score.score_a),
|
|
|
|
|
TextStyle {
|
|
|
|
|
font_size: 32.0,
|
|
|
|
|
color: Color::RED,
|
|
|
|
|
@ -1247,14 +1264,20 @@ 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(
|
|
|
|
|
events: Query<(&Side, &Piece), (Added<Captured>, With<display3d::Display3d>)>,
|
|
|
|
|
query: Query<(&Side, &Piece), (With<Captured>, With<display3d::Display3d>)>,
|
|
|
|
|
mut debug_info: ResMut<debug::DebugInfo>,
|
|
|
|
|
mut score: ResMut<Score>,
|
|
|
|
|
) {
|
|
|
|
|
events.iter().for_each(|(side, piece)| {
|
|
|
|
|
score.increment(!*side, *piece);
|
|
|
|
|
debug_info.set("score".into(), format!("A:{}|B:{}", score.a, score.b));
|
|
|
|
|
});
|
|
|
|
|
// Calculate number of captures performed by either side
|
|
|
|
|
score.captures_a = query.iter().filter(|(s, _)| **s == Side::B).count();
|
|
|
|
|
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());
|
|
|
|
|
|
|
|
|
|
// Debug this for good measure
|
|
|
|
|
debug_info.set("score".into(), format!("A:{}|B:{}", score.score_a, score.score_b));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(crate) fn set_side(
|
|
|
|
|
@ -1429,7 +1452,7 @@ fn reset_game(
|
|
|
|
|
.for_each(|(e, (i, p))| {
|
|
|
|
|
commands.entity(e)
|
|
|
|
|
.insert((*i, *p, Visibility::Inherited))
|
|
|
|
|
.remove::<(Captured, Promoted)>();
|
|
|
|
|
.remove::<(BeingCaptured, Captured, Promoted)>();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|