filled quarters points
This commit is contained in:
parent
41f8b0ac6e
commit
97f8678645
|
|
@ -446,13 +446,51 @@ impl Board {
|
||||||
let fields = self.get_quarter_fields(field);
|
let fields = self.get_quarter_fields(field);
|
||||||
!fields.iter().any(|field| {
|
!fields.iter().any(|field| {
|
||||||
if color == Color::White {
|
if color == Color::White {
|
||||||
self.positions[field - 1] < 1
|
self.positions[field - 1] < 2
|
||||||
} else {
|
} else {
|
||||||
self.positions[field - 1] > -1
|
self.positions[field - 1] > -2
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_quarter_filling_candidate(&self, color: Color) -> Vec<Field> {
|
||||||
|
let mut missing = vec![];
|
||||||
|
// first quarter
|
||||||
|
for quarter in [1..7, 7..13, 13..19, 19..25] {
|
||||||
|
missing = vec![];
|
||||||
|
for field in quarter {
|
||||||
|
let field_count = if color == Color::Black {
|
||||||
|
0 - self.positions[field - 1]
|
||||||
|
} else {
|
||||||
|
self.positions[field - 1]
|
||||||
|
};
|
||||||
|
if field_count < 0 {
|
||||||
|
// opponent checker found : this quarter cannot be filled
|
||||||
|
missing = vec![];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if field_count == 0 {
|
||||||
|
missing.push(field);
|
||||||
|
missing.push(field);
|
||||||
|
} else if field_count == 1 {
|
||||||
|
missing.push(field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if missing.len() < 3 {
|
||||||
|
// fillable quarter found (no more than two missing checkers)
|
||||||
|
if let Some(field) = missing.first() {
|
||||||
|
// We check that there are sufficient checkers left to fill the quarter
|
||||||
|
if !self.is_quarter_fillable(color, *field) {
|
||||||
|
missing = vec![];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// there will be no other fillable quarter
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
missing
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns whether the `color` player can still fill the quarter containing the `field`
|
/// Returns whether the `color` player can still fill the quarter containing the `field`
|
||||||
/// * `color` - color of the player
|
/// * `color` - color of the player
|
||||||
/// * `field` - field belonging to the quarter
|
/// * `field` - field belonging to the quarter
|
||||||
|
|
@ -636,4 +674,13 @@ mod tests {
|
||||||
]);
|
]);
|
||||||
assert!(board.is_quarter_fillable(Color::Black, 16));
|
assert!(board.is_quarter_fillable(Color::Black, 16));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn get_quarter_filling_candidate() {
|
||||||
|
let mut board = Board::new();
|
||||||
|
board.set_positions([
|
||||||
|
3, 1, 2, 2, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
]);
|
||||||
|
assert_eq!(vec![2], board.get_quarter_filling_candidate(Color::White));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,12 +41,22 @@ pub struct MoveRules {
|
||||||
impl MoveRules {
|
impl MoveRules {
|
||||||
/// Revert board if color is black
|
/// Revert board if color is black
|
||||||
pub fn new(color: &Color, board: &Board, dice: Dice) -> Self {
|
pub fn new(color: &Color, board: &Board, dice: Dice) -> Self {
|
||||||
let board = if *color == Color::Black {
|
Self {
|
||||||
|
board: Self::get_board_from_color(color, board),
|
||||||
|
dice,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_board(&mut self, color: &Color, board: &Board) {
|
||||||
|
self.board = Self::get_board_from_color(color, board);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_board_from_color(color: &Color, board: &Board) -> Board {
|
||||||
|
if *color == Color::Black {
|
||||||
board.mirror()
|
board.mirror()
|
||||||
} else {
|
} else {
|
||||||
board.clone()
|
board.clone()
|
||||||
};
|
}
|
||||||
Self { board, dice }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn moves_follow_rules(&self, moves: &(CheckerMove, CheckerMove)) -> bool {
|
pub fn moves_follow_rules(&self, moves: &(CheckerMove, CheckerMove)) -> bool {
|
||||||
|
|
@ -155,7 +165,6 @@ impl MoveRules {
|
||||||
// Si possible, les deux dés doivent être joués
|
// Si possible, les deux dés doivent être joués
|
||||||
if moves.0.get_from() == 0 || moves.1.get_from() == 0 {
|
if moves.0.get_from() == 0 || moves.1.get_from() == 0 {
|
||||||
let mut possible_moves_sequences = self.get_possible_moves_sequences(true);
|
let mut possible_moves_sequences = self.get_possible_moves_sequences(true);
|
||||||
println!("{:?}", possible_moves_sequences);
|
|
||||||
possible_moves_sequences.retain(|moves| self.check_exit_rules(moves).is_ok());
|
possible_moves_sequences.retain(|moves| self.check_exit_rules(moves).is_ok());
|
||||||
// possible_moves_sequences.retain(|moves| self.check_corner_rules(moves).is_ok());
|
// possible_moves_sequences.retain(|moves| self.check_corner_rules(moves).is_ok());
|
||||||
if !possible_moves_sequences.contains(&moves) && !possible_moves_sequences.is_empty() {
|
if !possible_moves_sequences.contains(&moves) && !possible_moves_sequences.is_empty() {
|
||||||
|
|
@ -313,14 +322,66 @@ impl MoveRules {
|
||||||
moves_seqs
|
moves_seqs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_scoring_quarter_filling_moves_sequences(&self) -> Vec<(CheckerMove, CheckerMove)> {
|
||||||
|
let all_seqs = self.get_quarter_filling_moves_sequences();
|
||||||
|
if all_seqs.len() == 0 {
|
||||||
|
return vec![];
|
||||||
|
}
|
||||||
|
let missing_fields = self.board.get_quarter_filling_candidate(Color::White);
|
||||||
|
match missing_fields.len() {
|
||||||
|
// preserve an already filled quarter : return one sequence
|
||||||
|
0 => vec![*all_seqs.last().unwrap()],
|
||||||
|
// two fields, two dices : all_seqs should already contain only one possibility
|
||||||
|
2 => all_seqs,
|
||||||
|
1 => {
|
||||||
|
let dest_field = missing_fields.first().unwrap();
|
||||||
|
let mut filling_moves_origins = vec![];
|
||||||
|
all_seqs.iter().fold(vec![], |mut acc, seq| {
|
||||||
|
let origins = self.get_sequence_origin_from_destination(*seq, *dest_field);
|
||||||
|
for origin in origins {
|
||||||
|
if !filling_moves_origins.contains(&origin) {
|
||||||
|
filling_moves_origins.push(origin);
|
||||||
|
acc.push(*seq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
acc
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => vec![], // cannot be
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_sequence_origin_from_destination(
|
||||||
|
&self,
|
||||||
|
sequence: (CheckerMove, CheckerMove),
|
||||||
|
destination: Field,
|
||||||
|
) -> Vec<Field> {
|
||||||
|
let mut origin = vec![];
|
||||||
|
if sequence.0.get_to() == destination {
|
||||||
|
origin.push(sequence.0.get_from());
|
||||||
|
}
|
||||||
|
if sequence.1.get_to() == destination {
|
||||||
|
if sequence.0.get_to() == sequence.1.get_from() {
|
||||||
|
// tout d'une
|
||||||
|
origin.push(sequence.0.get_from());
|
||||||
|
} else {
|
||||||
|
origin.push(sequence.1.get_from());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
origin
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all moves filling a quarter or preserving a filled quarter
|
||||||
pub fn get_quarter_filling_moves_sequences(&self) -> Vec<(CheckerMove, CheckerMove)> {
|
pub fn get_quarter_filling_moves_sequences(&self) -> Vec<(CheckerMove, CheckerMove)> {
|
||||||
let mut moves_seqs = Vec::new();
|
let mut moves_seqs = Vec::new();
|
||||||
let color = &Color::White;
|
let color = &Color::White;
|
||||||
|
let all_moves_seqs = self.get_possible_moves_sequences(true);
|
||||||
for moves in self.get_possible_moves_sequences(true) {
|
for moves in self.get_possible_moves_sequences(true) {
|
||||||
let mut board = self.board.clone();
|
let mut board = self.board.clone();
|
||||||
board.move_checker(color, moves.0).unwrap();
|
board.move_checker(color, moves.0).unwrap();
|
||||||
board.move_checker(color, moves.1).unwrap();
|
board.move_checker(color, moves.1).unwrap();
|
||||||
if board.any_quarter_filled(*color) {
|
// println!("get_quarter_filling_moves_sequences board : {:?}", board);
|
||||||
|
if board.any_quarter_filled(*color) && !moves_seqs.contains(&moves) {
|
||||||
moves_seqs.push(moves);
|
moves_seqs.push(moves);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -811,4 +872,57 @@ mod tests {
|
||||||
);
|
);
|
||||||
assert!(state.moves_possible(&moves));
|
assert!(state.moves_possible(&moves));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn filling_moves_sequences() {
|
||||||
|
let mut state = MoveRules::default();
|
||||||
|
state.board.set_positions([
|
||||||
|
3, 3, 1, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
]);
|
||||||
|
state.dice.values = (2, 1);
|
||||||
|
let filling_moves_sequences = state.get_quarter_filling_moves_sequences();
|
||||||
|
// println!(
|
||||||
|
// "test filling_moves_sequences : {:?}",
|
||||||
|
// filling_moves_sequences
|
||||||
|
// );
|
||||||
|
assert_eq!(2, filling_moves_sequences.len());
|
||||||
|
|
||||||
|
state.board.set_positions([
|
||||||
|
3, 2, 3, 2, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
]);
|
||||||
|
state.dice.values = (2, 2);
|
||||||
|
let filling_moves_sequences = state.get_quarter_filling_moves_sequences();
|
||||||
|
// println!("{:?}", filling_moves_sequences);
|
||||||
|
assert_eq!(2, filling_moves_sequences.len());
|
||||||
|
|
||||||
|
state.board.set_positions([
|
||||||
|
3, 1, 2, 2, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
]);
|
||||||
|
state.dice.values = (2, 1);
|
||||||
|
let filling_moves_sequences = state.get_quarter_filling_moves_sequences();
|
||||||
|
// println!(
|
||||||
|
// "test filling_moves_sequences 2 : {:?}",
|
||||||
|
// filling_moves_sequences
|
||||||
|
// );
|
||||||
|
assert_eq!(2, filling_moves_sequences.len());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn scoring_filling_moves_sequences() {
|
||||||
|
let mut state = MoveRules::default();
|
||||||
|
|
||||||
|
state.board.set_positions([
|
||||||
|
3, 1, 2, 2, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
]);
|
||||||
|
state.dice.values = (2, 1);
|
||||||
|
assert_eq!(1, state.get_scoring_quarter_filling_moves_sequences().len());
|
||||||
|
|
||||||
|
state.board.set_positions([
|
||||||
|
2, 3, 3, 3, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
]);
|
||||||
|
state.dice.values = (2, 1);
|
||||||
|
let filling_moves_sequences = state.get_scoring_quarter_filling_moves_sequences();
|
||||||
|
// println!("{:?}", filling_moves_sequences);
|
||||||
|
assert_eq!(3, filling_moves_sequences.len());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -104,13 +104,36 @@ impl PointsRules {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_jans(&self, board_ini: &Board, dices: &Vec<u8>) -> PossibleJans {
|
pub fn set_dice(&mut self, dice: Dice) {
|
||||||
let mut dices_reversed = dices.clone();
|
self.dice = dice;
|
||||||
dices_reversed.reverse();
|
self.move_rules.dice = dice;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_positions(&mut self, positions: [i8; 24]) {
|
||||||
|
self.board.set_positions(positions);
|
||||||
|
self.move_rules.board.set_positions(positions);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_jans(&self, board_ini: &Board) -> PossibleJans {
|
||||||
|
let dices = &vec![self.dice.values.0, self.dice.values.1];
|
||||||
|
let dices_reversed = &vec![self.dice.values.1, self.dice.values.0];
|
||||||
|
|
||||||
|
// « JAN DE RÉCOMPENSE »
|
||||||
|
// Battre à vrai une dame située dans la table des grands jans
|
||||||
|
// Battre à vrai une dame située dans la table des petits jans
|
||||||
let mut jans = self.get_jans_by_dice_order(board_ini, dices);
|
let mut jans = self.get_jans_by_dice_order(board_ini, dices);
|
||||||
let jans_revert_dices = self.get_jans_by_dice_order(board_ini, &dices_reversed);
|
let jans_revert_dices = self.get_jans_by_dice_order(board_ini, dices_reversed);
|
||||||
jans.merge(jans_revert_dices);
|
jans.merge(jans_revert_dices);
|
||||||
|
|
||||||
|
// « JAN DE REMPLISSAGE »
|
||||||
|
// Faire un petit jan, un grand jan ou un jan de retour
|
||||||
|
let filling_moves_sequences = self
|
||||||
|
.move_rules
|
||||||
|
.get_scoring_quarter_filling_moves_sequences();
|
||||||
|
if !filling_moves_sequences.is_empty() {
|
||||||
|
jans.insert(Jan::FilledQuarter, filling_moves_sequences);
|
||||||
|
}
|
||||||
|
|
||||||
jans
|
jans
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -192,14 +215,20 @@ impl PointsRules {
|
||||||
pub fn get_points(&self) -> i8 {
|
pub fn get_points(&self) -> i8 {
|
||||||
let mut points: i8 = 0;
|
let mut points: i8 = 0;
|
||||||
|
|
||||||
let jans = self.get_jans(&self.board, &vec![self.dice.values.0, self.dice.values.1]);
|
// « JAN DE RÉCOMPENSE »
|
||||||
|
// Battre à vrai une dame située dans la table des grands jans
|
||||||
|
// Battre à vrai une dame située dans la table des petits jans
|
||||||
|
// TODO : Battre le coin adverse
|
||||||
|
let jans = self.get_jans(&self.board);
|
||||||
points += jans.into_iter().fold(0, |acc: i8, (jan, moves)| {
|
points += jans.into_iter().fold(0, |acc: i8, (jan, moves)| {
|
||||||
acc + jan.get_points(self.dice.is_double()) * (moves.len() as i8)
|
acc + jan.get_points(self.dice.is_double()) * (moves.len() as i8)
|
||||||
});
|
});
|
||||||
|
|
||||||
// Jans de remplissage
|
// « JAN DE REMPLISSAGE »
|
||||||
let filling_moves_sequences = self.move_rules.get_quarter_filling_moves_sequences();
|
// Faire un petit jan, un grand jan ou un jan de retour
|
||||||
points += 4 * filling_moves_sequences.len() as i8;
|
// let filling_moves_sequences = self.move_rules.get_quarter_filling_moves_sequences();
|
||||||
|
// points += 4 * filling_moves_sequences.len() as i8;
|
||||||
|
|
||||||
// cf. https://fr.wikipedia.org/wiki/Trictrac
|
// cf. https://fr.wikipedia.org/wiki/Trictrac
|
||||||
// Points par simple par moyen | Points par doublet par moyen Nombre de moyens possibles Bénéficiaire
|
// Points par simple par moyen | Points par doublet par moyen Nombre de moyens possibles Bénéficiaire
|
||||||
// « JAN RARE »
|
// « JAN RARE »
|
||||||
|
|
@ -208,19 +237,12 @@ impl PointsRules {
|
||||||
// Jan de mézéas 4 6 1 Joueur
|
// Jan de mézéas 4 6 1 Joueur
|
||||||
// Contre jan de deux tables 4 6 1 Adversaire
|
// Contre jan de deux tables 4 6 1 Adversaire
|
||||||
// Contre jan de mézéas 4 6 1 Adversaire
|
// Contre jan de mézéas 4 6 1 Adversaire
|
||||||
// « JAN DE RÉCOMPENSE »
|
|
||||||
// Battre à vrai une dame située dans la table des grands jans 2 | 4 1, 2 ou 3 (sauf doublet) Joueur
|
|
||||||
// Battre à vrai une dame située dans la table des petits jans 4 | 6 1, 2 ou 3 Joueur
|
|
||||||
// Battre le coin adverse 4 6 1 Joueur
|
|
||||||
// « JAN QUI NE PEUT »
|
// « JAN QUI NE PEUT »
|
||||||
// Battre à faux une dame
|
// Battre à faux une dame
|
||||||
// située dans la table des grands jans 2 4 1 Adversaire
|
// située dans la table des grands jans 2 4 1 Adversaire
|
||||||
// Battre à faux une dame
|
// Battre à faux une dame
|
||||||
// située dans la table des petits jans 4 6 1 Adversaire
|
// située dans la table des petits jans 4 6 1 Adversaire
|
||||||
// Pour chaque dé non jouable (dame impuissante) 2 2 n/a Adversaire
|
// Pour chaque dé non jouable (dame impuissante) 2 2 n/a Adversaire
|
||||||
// « JAN DE REMPLISSAGE »
|
|
||||||
// Faire un petit jan, un grand jan ou un jan de retour 4 1, 2, ou 3 Joueur
|
|
||||||
// 6 1 ou 2 Joueur
|
|
||||||
// Conserver un petit jan, un grand jan ou un jan de retour 4 6 1 Joueur
|
// Conserver un petit jan, un grand jan ou un jan de retour 4 6 1 Joueur
|
||||||
// « AUTRE »
|
// « AUTRE »
|
||||||
// Sortir le premier toutes ses dames 4 6 n/a Joueur
|
// Sortir le premier toutes ses dames 4 6 n/a Joueur
|
||||||
|
|
@ -320,11 +342,39 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn get_points() {
|
fn get_points() {
|
||||||
|
// ----- Jan de récompense
|
||||||
let mut rules = PointsRules::default();
|
let mut rules = PointsRules::default();
|
||||||
rules.board.set_positions([
|
rules.update_positions([
|
||||||
2, 0, -1, -1, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
2, 0, -1, -1, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
]);
|
]);
|
||||||
rules.dice = Dice { values: (2, 3) };
|
rules.set_dice(Dice { values: (2, 3) });
|
||||||
assert_eq!(12, rules.get_points());
|
assert_eq!(12, rules.get_points());
|
||||||
|
|
||||||
|
// ---- Jan de remplissage
|
||||||
|
rules.update_positions([
|
||||||
|
3, 1, 2, 2, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
]);
|
||||||
|
rules.set_dice(Dice { values: (2, 1) });
|
||||||
|
assert_eq!(1, rules.get_jans(&rules.board).len());
|
||||||
|
assert_eq!(4, rules.get_points());
|
||||||
|
|
||||||
|
rules.update_positions([
|
||||||
|
2, 3, 1, 2, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
]);
|
||||||
|
rules.set_dice(Dice { values: (1, 1) });
|
||||||
|
assert_eq!(6, rules.get_points());
|
||||||
|
|
||||||
|
rules.update_positions([
|
||||||
|
3, 3, 1, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
]);
|
||||||
|
rules.set_dice(Dice { values: (1, 1) });
|
||||||
|
assert_eq!(12, rules.get_points());
|
||||||
|
|
||||||
|
// conservation jan rempli
|
||||||
|
rules.update_positions([
|
||||||
|
3, 3, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
]);
|
||||||
|
rules.set_dice(Dice { values: (1, 1) });
|
||||||
|
assert_eq!(6, rules.get_points());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue