2024-05-27 19:54:58 +02:00
|
|
|
use std::collections::HashMap;
|
|
|
|
|
|
2024-05-26 12:00:30 +02:00
|
|
|
use crate::board::{Board, EMPTY_MOVE};
|
2024-05-20 19:04:46 +02:00
|
|
|
use crate::dice::Dice;
|
2024-05-25 19:56:38 +02:00
|
|
|
use crate::game_rules_moves::MoveRules;
|
|
|
|
|
use crate::player::Color;
|
|
|
|
|
use crate::CheckerMove;
|
|
|
|
|
use crate::Error;
|
2024-05-20 19:04:46 +02:00
|
|
|
|
2024-05-27 19:54:58 +02:00
|
|
|
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
|
2024-05-25 19:56:38 +02:00
|
|
|
enum Jan {
|
2024-05-26 12:00:30 +02:00
|
|
|
FilledQuarter,
|
2024-06-15 19:10:01 +02:00
|
|
|
TrueHitSmallJan,
|
|
|
|
|
TrueHitBigJan,
|
2024-05-21 21:22:04 +02:00
|
|
|
// jans de récompense :
|
|
|
|
|
// - battre une dame seule (par autant de façons de le faire, y compris
|
|
|
|
|
// utilisant une dame du coin de repos)
|
|
|
|
|
// - battre le coin adverse : si deux dames (hormis les deux dernière de son propre coin de
|
|
|
|
|
// repos) peuvent battre le coin vide adverse
|
|
|
|
|
// jans qui ne peut (pts pour l'adversaire) :
|
|
|
|
|
// - battre à faux : si on passe par une case pleine pour atteindre la
|
|
|
|
|
// case que l'on peut battre
|
|
|
|
|
// - si on ne peut pas jouer ses deux dés
|
2024-05-20 19:04:46 +02:00
|
|
|
}
|
|
|
|
|
|
2024-06-15 19:10:01 +02:00
|
|
|
impl Jan {
|
|
|
|
|
pub fn get_points(&self, is_double: bool) -> i8 {
|
|
|
|
|
match self {
|
|
|
|
|
Self::TrueHitBigJan => {
|
|
|
|
|
if is_double {
|
|
|
|
|
4
|
|
|
|
|
} else {
|
|
|
|
|
2
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
_ => {
|
|
|
|
|
if is_double {
|
|
|
|
|
6
|
|
|
|
|
} else {
|
|
|
|
|
4
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// « 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
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-28 18:52:49 +02:00
|
|
|
type PossibleJans = HashMap<Jan, Vec<(CheckerMove, CheckerMove)>>;
|
2024-05-27 19:54:58 +02:00
|
|
|
|
2024-05-28 18:52:49 +02:00
|
|
|
trait PossibleJansMethods {
|
|
|
|
|
fn push(&mut self, jan: Jan, cmoves: (CheckerMove, CheckerMove));
|
|
|
|
|
fn merge(&mut self, other: Self);
|
2024-06-15 19:10:01 +02:00
|
|
|
// fn get_points(&self) -> u8;
|
2024-05-28 18:52:49 +02:00
|
|
|
}
|
2024-05-27 19:54:58 +02:00
|
|
|
|
2024-05-28 18:52:49 +02:00
|
|
|
impl PossibleJansMethods for PossibleJans {
|
|
|
|
|
fn push(&mut self, jan: Jan, cmoves: (CheckerMove, CheckerMove)) {
|
|
|
|
|
if let Some(ways) = self.get_mut(&jan) {
|
2024-05-27 19:54:58 +02:00
|
|
|
if !ways.contains(&cmoves) {
|
|
|
|
|
ways.push(cmoves);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2024-05-28 18:52:49 +02:00
|
|
|
self.insert(jan, [cmoves].into());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn merge(&mut self, other: Self) {
|
|
|
|
|
for (jan, cmoves_list) in other {
|
|
|
|
|
for cmoves in cmoves_list {
|
|
|
|
|
self.push(jan.clone(), cmoves);
|
|
|
|
|
}
|
2024-05-27 19:54:58 +02:00
|
|
|
}
|
|
|
|
|
}
|
2024-05-25 19:56:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// PointsRules always consider that the current player is White
|
|
|
|
|
/// You must use 'mirror' function on board if player is Black
|
|
|
|
|
#[derive(Default)]
|
|
|
|
|
pub struct PointsRules {
|
|
|
|
|
pub board: Board,
|
|
|
|
|
pub dice: Dice,
|
|
|
|
|
pub move_rules: MoveRules,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl PointsRules {
|
|
|
|
|
/// Revert board if color is black
|
|
|
|
|
pub fn new(color: &Color, board: &Board, dice: Dice) -> Self {
|
|
|
|
|
let board = if *color == Color::Black {
|
|
|
|
|
board.mirror()
|
|
|
|
|
} else {
|
|
|
|
|
board.clone()
|
|
|
|
|
};
|
|
|
|
|
let move_rules = MoveRules::new(color, &board, dice);
|
|
|
|
|
|
|
|
|
|
// let move_rules = MoveRules::new(color, &self.board, dice, moves);
|
|
|
|
|
Self {
|
|
|
|
|
board,
|
|
|
|
|
dice,
|
|
|
|
|
move_rules,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-13 21:18:49 +02:00
|
|
|
fn get_jans(&self, board_ini: &Board, dices: &Vec<u8>) -> PossibleJans {
|
2024-05-27 19:54:58 +02:00
|
|
|
let mut jans = PossibleJans::default();
|
2024-05-25 19:56:38 +02:00
|
|
|
let mut dices = dices.clone();
|
|
|
|
|
if let Some(dice) = dices.pop() {
|
2024-05-28 18:52:49 +02:00
|
|
|
let color = Color::White;
|
2024-06-13 21:18:49 +02:00
|
|
|
let mut board = board_ini.clone();
|
|
|
|
|
let corner_field = board.get_color_corner(&color);
|
|
|
|
|
let adv_corner_field = board.get_color_corner(&Color::Black);
|
2024-05-28 18:52:49 +02:00
|
|
|
for (from, _) in board.get_color_fields(color) {
|
2024-05-25 19:56:38 +02:00
|
|
|
let to = if from + dice as usize > 24 {
|
|
|
|
|
0
|
|
|
|
|
} else {
|
|
|
|
|
from + dice as usize
|
|
|
|
|
};
|
|
|
|
|
if let Ok(cmove) = CheckerMove::new(from, to) {
|
2024-06-14 19:07:33 +02:00
|
|
|
// let res = state.moves_allowed(&moves);
|
|
|
|
|
// if res.is_ok() {
|
|
|
|
|
// println!("dice : {:?}, res : {:?}", dice, res);
|
|
|
|
|
// On vérifie que le mouvement n'est pas interdit par les règles des coins de
|
|
|
|
|
// repos :
|
|
|
|
|
// - on ne va pas sur le coin de l'adversaire
|
|
|
|
|
// - ni sur son propre coin de repos avec une seule dame
|
2024-06-15 19:10:01 +02:00
|
|
|
// - règle non prise en compte pour le battage des dames : on ne sort pas de son coin de repos s'il ni reste que deux dames
|
2024-06-13 21:18:49 +02:00
|
|
|
let (corner_count, _color) = board.get_field_checkers(corner_field).unwrap();
|
2024-06-15 19:10:01 +02:00
|
|
|
if to != adv_corner_field && (to != corner_field || corner_count > 1)
|
|
|
|
|
// && (from != corner_field || corner_count > 2)
|
2024-06-14 19:07:33 +02:00
|
|
|
{
|
2024-06-13 21:18:49 +02:00
|
|
|
// println!(
|
|
|
|
|
// "dice : {}, adv_corn_field : {:?}, from : {}, to : {}, corner_count : {}",
|
|
|
|
|
// dice, adv_corner_field, from, to, corner_count
|
|
|
|
|
// );
|
2024-06-15 19:10:01 +02:00
|
|
|
let mut can_try_toutdune = true;
|
2024-06-13 21:18:49 +02:00
|
|
|
match board.move_checker(&color, cmove) {
|
|
|
|
|
Err(Error::FieldBlockedByOne) => {
|
2024-06-15 19:10:01 +02:00
|
|
|
let jan = if Board::is_field_in_small_jan(to) {
|
|
|
|
|
Jan::TrueHitSmallJan
|
|
|
|
|
} else {
|
|
|
|
|
Jan::TrueHitBigJan
|
|
|
|
|
};
|
|
|
|
|
jans.push(jan, (cmove, EMPTY_MOVE));
|
2024-06-13 21:18:49 +02:00
|
|
|
}
|
|
|
|
|
Err(_) => {
|
2024-06-15 19:10:01 +02:00
|
|
|
can_try_toutdune = false;
|
2024-06-13 21:18:49 +02:00
|
|
|
// let next_dice_jan = self.get_jans(&board, &dices);
|
|
|
|
|
// jans possibles en tout d'une après un battage à vrai :
|
|
|
|
|
// truehit
|
|
|
|
|
}
|
2024-06-15 19:10:01 +02:00
|
|
|
Ok(()) => {}
|
|
|
|
|
}
|
|
|
|
|
if can_try_toutdune {
|
|
|
|
|
// Try tout d'une :
|
|
|
|
|
// - use original board before first die move
|
|
|
|
|
// - use a virtual dice by adding current dice to remaining dice
|
|
|
|
|
let next_dice_jan = self
|
|
|
|
|
.get_jans(&board_ini, &dices.iter().map(|d| d + dice).collect());
|
|
|
|
|
jans.merge(next_dice_jan);
|
2024-05-25 19:56:38 +02:00
|
|
|
}
|
|
|
|
|
}
|
2024-06-13 21:18:49 +02:00
|
|
|
// Second die
|
|
|
|
|
let next_dice_jan = self.get_jans(&board_ini, &dices);
|
2024-05-28 18:52:49 +02:00
|
|
|
jans.merge(next_dice_jan);
|
2024-05-25 19:56:38 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-05-28 18:52:49 +02:00
|
|
|
|
2024-05-27 19:54:58 +02:00
|
|
|
// TODO : mouvements en tout d'une asdf
|
|
|
|
|
// - faire un dé d1+d2 et regarder si hit
|
|
|
|
|
// - si hit : regarder s'il existe le truehit intermédiaire
|
|
|
|
|
// - regarder les TrueHit qui nécessitent deux mouvemments non nuls
|
2024-05-25 19:56:38 +02:00
|
|
|
// TODO : tout d'une (sans doublons avec 1 + 1) ?
|
|
|
|
|
jans
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-15 19:10:01 +02:00
|
|
|
pub fn get_points(&self) -> i8 {
|
|
|
|
|
let mut points: i8 = 0;
|
2024-05-25 19:56:38 +02:00
|
|
|
|
2024-06-13 21:18:49 +02:00
|
|
|
let mut jans = self.get_jans(&self.board, &vec![self.dice.values.0, self.dice.values.1]);
|
|
|
|
|
let jans_revert_dices =
|
|
|
|
|
self.get_jans(&self.board, &vec![self.dice.values.1, self.dice.values.0]);
|
|
|
|
|
jans.merge(jans_revert_dices);
|
2024-06-15 19:10:01 +02:00
|
|
|
points += jans.into_iter().fold(0, |acc: i8, (jan, moves)| {
|
|
|
|
|
acc + jan.get_points(self.dice.is_double()) * (moves.len() as i8)
|
|
|
|
|
});
|
2024-05-25 19:56:38 +02:00
|
|
|
|
|
|
|
|
// Jans de remplissage
|
|
|
|
|
let filling_moves_sequences = self.move_rules.get_quarter_filling_moves_sequences();
|
2024-06-15 19:10:01 +02:00
|
|
|
points += 4 * filling_moves_sequences.len() as i8;
|
2024-05-28 18:52:49 +02:00
|
|
|
// cf. https://fr.wikipedia.org/wiki/Trictrac
|
|
|
|
|
// Points par simple par moyen | Points par doublet par moyen Nombre de moyens possibles Bénéficiaire
|
2024-05-25 19:56:38 +02:00
|
|
|
// « JAN RARE »
|
|
|
|
|
// Jan de six tables 4 n/a 1 Joueur
|
|
|
|
|
// Jan de deux tables 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 mézéas 4 6 1 Adversaire
|
|
|
|
|
// « JAN DE RÉCOMPENSE »
|
2024-05-28 18:52:49 +02:00
|
|
|
// 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
|
2024-05-25 19:56:38 +02:00
|
|
|
// Battre le coin adverse 4 6 1 Joueur
|
|
|
|
|
// « JAN QUI NE PEUT »
|
|
|
|
|
// Battre à faux une dame
|
|
|
|
|
// située dans la table des grands jans 2 4 1 Adversaire
|
|
|
|
|
// Battre à faux une dame
|
|
|
|
|
// située dans la table des petits jans 4 6 1 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
|
|
|
|
|
// « AUTRE »
|
|
|
|
|
// Sortir le premier toutes ses dames 4 6 n/a Joueur
|
2024-05-20 19:04:46 +02:00
|
|
|
|
2024-05-25 19:56:38 +02:00
|
|
|
points
|
2024-05-20 19:04:46 +02:00
|
|
|
}
|
|
|
|
|
}
|
2024-05-28 18:52:49 +02:00
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use super::*;
|
|
|
|
|
#[test]
|
|
|
|
|
fn get_jans() {
|
|
|
|
|
let mut rules = PointsRules::default();
|
|
|
|
|
rules.board.set_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,
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
let jans = rules.get_jans(&rules.board, &vec![2, 3]);
|
|
|
|
|
assert_eq!(1, jans.len());
|
2024-06-15 19:10:01 +02:00
|
|
|
assert_eq!(3, jans.get(&Jan::TrueHitSmallJan).unwrap().len());
|
2024-05-28 18:52:49 +02:00
|
|
|
|
|
|
|
|
let jans = rules.get_jans(&rules.board, &vec![2, 2]);
|
|
|
|
|
assert_eq!(1, jans.len());
|
2024-06-15 19:10:01 +02:00
|
|
|
assert_eq!(1, jans.get(&Jan::TrueHitSmallJan).unwrap().len());
|
2024-05-28 18:52:49 +02:00
|
|
|
|
2024-06-15 19:10:01 +02:00
|
|
|
// On peut passer par un une dame battue pour battre une autre dame
|
|
|
|
|
// mais pas par une case remplie par l'adversaire
|
2024-05-28 18:52:49 +02:00
|
|
|
rules.board.set_positions([
|
2024-06-15 19:10:01 +02:00
|
|
|
2, 0, -1, -2, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
2024-05-28 18:52:49 +02:00
|
|
|
]);
|
|
|
|
|
|
2024-06-15 19:10:01 +02:00
|
|
|
let mut jans = rules.get_jans(&rules.board, &vec![2, 3]);
|
|
|
|
|
let jans_revert_dices = rules.get_jans(&rules.board, &vec![3, 2]);
|
2024-05-28 18:52:49 +02:00
|
|
|
assert_eq!(1, jans.len());
|
2024-06-15 19:10:01 +02:00
|
|
|
assert_eq!(1, jans_revert_dices.len());
|
|
|
|
|
jans.merge(jans_revert_dices);
|
|
|
|
|
assert_eq!(2, jans.get(&Jan::TrueHitSmallJan).unwrap().len());
|
2024-05-28 18:52:49 +02:00
|
|
|
|
|
|
|
|
rules.board.set_positions([
|
|
|
|
|
2, 0, -1, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
let jans = rules.get_jans(&rules.board, &vec![2, 3]);
|
|
|
|
|
assert_eq!(1, jans.len());
|
2024-06-15 19:10:01 +02:00
|
|
|
assert_eq!(2, jans.get(&Jan::TrueHitSmallJan).unwrap().len());
|
2024-05-28 18:52:49 +02:00
|
|
|
|
|
|
|
|
rules.board.set_positions([
|
|
|
|
|
2, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
let jans = rules.get_jans(&rules.board, &vec![2, 3]);
|
|
|
|
|
assert_eq!(1, jans.len());
|
2024-06-15 19:10:01 +02:00
|
|
|
assert_eq!(1, jans.get(&Jan::TrueHitSmallJan).unwrap().len());
|
2024-05-28 18:52:49 +02:00
|
|
|
|
|
|
|
|
rules.board.set_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,
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
let jans = rules.get_jans(&rules.board, &vec![2, 3]);
|
|
|
|
|
assert_eq!(1, jans.len());
|
2024-06-15 19:10:01 +02:00
|
|
|
assert_eq!(3, jans.get(&Jan::TrueHitSmallJan).unwrap().len());
|
2024-06-13 21:18:49 +02:00
|
|
|
|
|
|
|
|
// corners handling
|
|
|
|
|
|
|
|
|
|
// deux dés bloqués (coin de repos et coin de l'adversaire)
|
|
|
|
|
rules.board.set_positions([
|
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
|
]);
|
|
|
|
|
// le premier dé traité est le dernier du vecteur : 1
|
|
|
|
|
let jans = rules.get_jans(&rules.board, &vec![2, 1]);
|
|
|
|
|
// println!("jans (dés bloqués) : {:?}", jans.get(&Jan::TrueHit));
|
|
|
|
|
assert_eq!(0, jans.len());
|
|
|
|
|
|
2024-06-15 19:10:01 +02:00
|
|
|
// dé dans son coin de repos : peut tout de même battre à vrai
|
2024-06-14 19:07:33 +02:00
|
|
|
rules.board.set_positions([
|
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
|
]);
|
|
|
|
|
let mut jans = rules.get_jans(&rules.board, &vec![3, 3]);
|
2024-06-15 19:10:01 +02:00
|
|
|
assert_eq!(1, jans.len());
|
2024-06-14 19:07:33 +02:00
|
|
|
|
2024-06-13 21:18:49 +02:00
|
|
|
// premier dé bloqué, mais tout d'une possible en commençant par le second
|
|
|
|
|
rules.board.set_positions([
|
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
|
]);
|
|
|
|
|
let mut jans = rules.get_jans(&rules.board, &vec![3, 1]);
|
|
|
|
|
let jans_revert_dices = rules.get_jans(&rules.board, &vec![1, 3]);
|
|
|
|
|
assert_eq!(1, jans_revert_dices.len());
|
|
|
|
|
|
|
|
|
|
jans.merge(jans_revert_dices);
|
|
|
|
|
assert_eq!(1, jans.len());
|
2024-06-14 19:07:33 +02:00
|
|
|
// print!("jans (2) : {:?}", jans.get(&Jan::TrueHit));
|
2024-06-23 11:38:03 +02:00
|
|
|
|
|
|
|
|
// battage à faux : ne pas prendre en compte si en inversant l'ordre des dés il y a battage
|
|
|
|
|
// à vrai
|
2024-05-28 18:52:49 +02:00
|
|
|
}
|
2024-06-15 19:10:01 +02:00
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn get_points() {
|
|
|
|
|
let mut rules = PointsRules::default();
|
|
|
|
|
rules.board.set_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,
|
|
|
|
|
]);
|
|
|
|
|
rules.dice = Dice { values: (2, 3) };
|
|
|
|
|
assert_eq!(12, rules.get_points());
|
|
|
|
|
}
|
2024-05-28 18:52:49 +02:00
|
|
|
}
|