wip règles de sortie

This commit is contained in:
Henri Bourcereau 2024-05-09 21:49:56 +02:00
parent a62e91a233
commit 05b9d54908

View file

@ -1,11 +1,9 @@
//! # Play a TricTrac Game //! # Play a TricTrac Game
use crate::board::{Board, CheckerMove, Field}; use crate::board::{Board, CheckerMove, Field};
use crate::dice::{Dice, DiceRoller, Roll}; use crate::dice::Dice;
use crate::player::{Color, Player, PlayerId}; use crate::player::{Color, Player, PlayerId};
use crate::Error; use log::error;
use log::{error, info};
use std::cmp; use std::cmp;
use std::fmt::Display;
// use itertools::Itertools; // use itertools::Itertools;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -199,15 +197,14 @@ impl GameState {
return false; return false;
} }
} }
EndGame { reason } => match reason { EndGame { reason } => {
EndGameReason::PlayerWon { winner: _ } => { if let EndGameReason::PlayerWon { winner: _ } = reason {
// Check that the game has started before someone wins it // Check that the game has started before someone wins it
if self.stage != Stage::InGame { if self.stage != Stage::InGame {
return false; return false;
} }
} }
_ => {} }
},
PlayerJoined { player_id, name: _ } => { PlayerJoined { player_id, name: _ } => {
// Check that there isn't another player with the same id // Check that there isn't another player with the same id
if self.players.contains_key(player_id) { if self.players.contains_key(player_id) {
@ -230,7 +227,10 @@ impl GameState {
return false; return false;
} }
} }
Mark { player_id, points } => { Mark {
player_id,
points: _,
} => {
// Check player exists // Check player exists
if !self.players.contains_key(player_id) { if !self.players.contains_key(player_id) {
return false; return false;
@ -281,9 +281,8 @@ impl GameState {
} }
// Chained_move : "Tout d'une" // Chained_move : "Tout d'une"
let chained_move = moves.0.chain(moves.1); if let Ok(chained_move) = moves.0.chain(moves.1) {
if chained_move.is_ok() { if !self.board.move_possible(color, &chained_move) {
if !self.board.move_possible(color, &chained_move.unwrap()) {
return false; return false;
} }
} else if !self.board.move_possible(color, &moves.1) { } else if !self.board.move_possible(color, &moves.1) {
@ -292,74 +291,63 @@ impl GameState {
true true
} }
fn moves_follows_dices(&self, color: &Color, moves: &(CheckerMove, CheckerMove)) -> bool { fn get_move_compatible_dices(&self, color: &Color, cmove: &CheckerMove) -> Vec<u8> {
let (dice1, dice2) = self.dice.values; let (dice1, dice2) = self.dice.values;
let (move1, move2): &(CheckerMove, CheckerMove) = moves;
let dist1 = (move1.get_to() as i8 - move1.get_from() as i8).abs() as u8; let mut move_dices = Vec::new();
let dist2 = (move2.get_to() as i8 - move2.get_from() as i8).abs() as u8; if cmove.get_to() == 0 {
// print!("{}, {}, {}, {}", dist1, dist2, dice1, dice2); // Exits
// exceptions let min_dist = match color {
// - prise de coin par puissance Color::White => 25 - cmove.get_from(),
Color::Black => cmove.get_from(),
};
if dice1 as usize >= min_dist {
move_dices.push(dice1);
}
if dice2 as usize >= min_dist {
move_dices.push(dice2);
}
} else {
let dist = (cmove.get_to() as i8 - cmove.get_from() as i8).unsigned_abs();
if dice1 == dist {
move_dices.push(dice1);
}
if dice2 == dist {
move_dices.push(dice2);
}
}
move_dices
}
fn moves_follows_dices(&self, color: &Color, moves: &(CheckerMove, CheckerMove)) -> bool {
// Prise de coin par puissance
if self.is_move_by_puissance(color, moves) { if self.is_move_by_puissance(color, moves) {
return true; return true;
} }
// - sorties
// default : must be same number let (dice1, dice2) = self.dice.values;
if cmp::min(dist1, dist2) != cmp::min(dice1, dice2) let (move1, move2): &(CheckerMove, CheckerMove) = moves;
|| cmp::max(dist1, dist2) != cmp::max(dice1, dice2)
let move1_dices = self.get_move_compatible_dices(color, move1);
if move1_dices.is_empty() {
return false;
}
let move2_dices = self.get_move_compatible_dices(color, move2);
if move2_dices.is_empty() {
return false;
}
if move1_dices.len() == 1
&& move2_dices.len() == 1
&& move1_dices[0] == move2_dices[0]
&& dice1 != dice2
{ {
return false; return false;
} }
// no rule was broken // no rule was broken
true true
} }
fn is_move_by_puissance(&self, color: &Color, moves: &(CheckerMove, CheckerMove)) -> bool {
let (dice1, dice2) = self.dice.values;
let (move1, move2): &(CheckerMove, CheckerMove) = moves.into();
let dist1 = (move1.get_to() as i8 - move1.get_from() as i8).abs() as u8;
let dist2 = (move2.get_to() as i8 - move2.get_from() as i8).abs() as u8;
// Both corners must be empty
let (count1, _color) = self.board.get_field_checkers(12).unwrap();
let (count2, _color2) = self.board.get_field_checkers(13).unwrap();
if count1 > 0 || count2 > 0 {
return false;
}
move1.get_to() == move2.get_to()
&& move1.get_to() == self.board.get_color_corner(color)
&& ((*color == Color::White
&& cmp::min(dist1, dist2) == cmp::min(dice1, dice2) - 1
&& cmp::max(dist1, dist2) == cmp::max(dice1, dice2) - 1)
|| (*color == Color::Black
&& cmp::min(dist1, dist2) == cmp::min(dice1, dice2) + 1
&& cmp::max(dist1, dist2) == cmp::max(dice1, dice2) + 1))
}
fn can_take_corner_by_effect(&self, color: &Color) -> bool {
// return false if corner already taken
let corner_field: Field = self.board.get_color_corner(color);
let (count, _col) = self.board.get_field_checkers(corner_field).unwrap();
if count > 0 {
return false;
}
let (dice1, dice2) = self.dice.values;
let (field1, field2) = match color {
Color::White => (12 - dice1, 12 - dice2),
Color::Black => (13 + dice1, 13 + dice2),
};
let res1 = self.board.get_field_checkers(field1.into());
let res2 = self.board.get_field_checkers(field2.into());
if res1.is_err() || res2.is_err() {
return false;
}
let (count1, opt_color1) = res1.unwrap();
let (count2, opt_color2) = res2.unwrap();
count1 > 0 && count2 > 0 && opt_color1 == Some(color) && opt_color2 == Some(color)
}
fn moves_allowed(&self, color: &Color, moves: &(CheckerMove, CheckerMove)) -> bool { fn moves_allowed(&self, color: &Color, moves: &(CheckerMove, CheckerMove)) -> bool {
// ------- corner rules ---------- // ------- corner rules ----------
let corner_field: Field = self.board.get_color_corner(color); let corner_field: Field = self.board.get_color_corner(color);
@ -400,6 +388,52 @@ impl GameState {
true true
} }
fn is_move_by_puissance(&self, color: &Color, moves: &(CheckerMove, CheckerMove)) -> bool {
let (dice1, dice2) = self.dice.values;
let (move1, move2): &(CheckerMove, CheckerMove) = moves;
let dist1 = (move1.get_to() as i8 - move1.get_from() as i8).unsigned_abs();
let dist2 = (move2.get_to() as i8 - move2.get_from() as i8).unsigned_abs();
// Both corners must be empty
let (count1, _color) = self.board.get_field_checkers(12).unwrap();
let (count2, _color2) = self.board.get_field_checkers(13).unwrap();
if count1 > 0 || count2 > 0 {
return false;
}
move1.get_to() == move2.get_to()
&& move1.get_to() == self.board.get_color_corner(color)
&& ((*color == Color::White
&& cmp::min(dist1, dist2) == cmp::min(dice1, dice2) - 1
&& cmp::max(dist1, dist2) == cmp::max(dice1, dice2) - 1)
|| (*color == Color::Black
&& cmp::min(dist1, dist2) == cmp::min(dice1, dice2) + 1
&& cmp::max(dist1, dist2) == cmp::max(dice1, dice2) + 1))
}
fn can_take_corner_by_effect(&self, color: &Color) -> bool {
// return false if corner already taken
let corner_field: Field = self.board.get_color_corner(color);
let (count, _col) = self.board.get_field_checkers(corner_field).unwrap();
if count > 0 {
return false;
}
let (dice1, dice2) = self.dice.values;
let (field1, field2) = match color {
Color::White => (12 - dice1, 12 - dice2),
Color::Black => (13 + dice1, 13 + dice2),
};
let res1 = self.board.get_field_checkers(field1.into());
let res2 = self.board.get_field_checkers(field2.into());
if res1.is_err() || res2.is_err() {
return false;
}
let (count1, opt_color1) = res1.unwrap();
let (count2, opt_color2) = res2.unwrap();
count1 > 0 && count2 > 0 && opt_color1 == Some(color) && opt_color2 == Some(color)
}
// ---------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------
// State updates // State updates
// ---------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------
@ -457,7 +491,7 @@ impl GameState {
} }
EndGame { reason: _ } => self.stage = Stage::Ended, EndGame { reason: _ } => self.stage = Stage::Ended,
PlayerJoined { player_id, name } => { PlayerJoined { player_id, name } => {
let color = if self.players.len() > 0 { let color = if !self.players.is_empty() {
Color::White Color::White
} else { } else {
Color::Black Color::Black
@ -494,12 +528,7 @@ impl GameState {
let player = self.players.get(player_id).unwrap(); let player = self.players.get(player_id).unwrap();
self.board.move_checker(&player.color, moves.0).unwrap(); self.board.move_checker(&player.color, moves.0).unwrap();
self.board.move_checker(&player.color, moves.1).unwrap(); self.board.move_checker(&player.color, moves.1).unwrap();
self.active_player_id = self self.active_player_id = *self.players.keys().find(|id| *id != player_id).unwrap();
.players
.keys()
.find(|id| *id != player_id)
.unwrap()
.clone();
self.turn_stage = TurnStage::RollDice; self.turn_stage = TurnStage::RollDice;
} }
} }
@ -514,7 +543,7 @@ impl GameState {
fn mark_points(&mut self, player_id: PlayerId, points: u8) { fn mark_points(&mut self, player_id: PlayerId, points: u8) {
self.players.get_mut(&player_id).map(|p| { self.players.get_mut(&player_id).map(|p| {
p.points = p.points + points; p.points += points;
p p
}); });
} }
@ -713,4 +742,41 @@ mod tests {
assert!(!state.is_move_by_puissance(&Color::White, &moves)); assert!(!state.is_move_by_puissance(&Color::White, &moves));
assert!(!state.moves_follows_dices(&Color::White, &moves)); assert!(!state.moves_follows_dices(&Color::White, &moves));
} }
#[test]
fn test_exit() {
let mut state = GameState::default();
let player1 = Player::new("player1".into(), Color::White);
let player_id = 1;
state.add_player(player_id, player1);
state.add_player(2, Player::new("player2".into(), Color::Black));
state.consume(&GameEvent::BeginGame {
goes_first: player_id,
});
state.consume(&GameEvent::Roll { player_id });
// exit ok
state.board.set_positions([
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0,
]);
state.dice.values = (5, 5);
let moves = (
CheckerMove::new(20, 0).unwrap(),
CheckerMove::new(20, 0).unwrap(),
);
assert!(state.moves_possible(&Color::White, &moves));
assert!(state.moves_follows_dices(&Color::White, &moves));
assert!(state.moves_allowed(&Color::White, &moves));
// toutes les dames doivent être dans le jan de retour
state.board.set_positions([
0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0,
]);
state.dice.values = (5, 5);
let moves = (
CheckerMove::new(20, 0).unwrap(),
CheckerMove::new(20, 0).unwrap(),
);
assert!(!state.moves_allowed(&Color::White, &moves));
}
} }