From 05b9d5490868bba633112926a07bf2a0389728fb Mon Sep 17 00:00:00 2001 From: Henri Bourcereau Date: Thu, 9 May 2024 21:49:56 +0200 Subject: [PATCH] =?UTF-8?q?wip=20r=C3=A8gles=20de=20sortie?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- store/src/game.rs | 220 ++++++++++++++++++++++++++++++---------------- 1 file changed, 143 insertions(+), 77 deletions(-) diff --git a/store/src/game.rs b/store/src/game.rs index 2a76745..0f0e5ec 100644 --- a/store/src/game.rs +++ b/store/src/game.rs @@ -1,11 +1,9 @@ //! # Play a TricTrac Game use crate::board::{Board, CheckerMove, Field}; -use crate::dice::{Dice, DiceRoller, Roll}; +use crate::dice::Dice; use crate::player::{Color, Player, PlayerId}; -use crate::Error; -use log::{error, info}; +use log::error; use std::cmp; -use std::fmt::Display; // use itertools::Itertools; use serde::{Deserialize, Serialize}; @@ -199,15 +197,14 @@ impl GameState { return false; } } - EndGame { reason } => match reason { - EndGameReason::PlayerWon { winner: _ } => { + EndGame { reason } => { + if let EndGameReason::PlayerWon { winner: _ } = reason { // Check that the game has started before someone wins it if self.stage != Stage::InGame { return false; } } - _ => {} - }, + } PlayerJoined { player_id, name: _ } => { // Check that there isn't another player with the same id if self.players.contains_key(player_id) { @@ -230,7 +227,10 @@ impl GameState { return false; } } - Mark { player_id, points } => { + Mark { + player_id, + points: _, + } => { // Check player exists if !self.players.contains_key(player_id) { return false; @@ -281,9 +281,8 @@ impl GameState { } // Chained_move : "Tout d'une" - let chained_move = moves.0.chain(moves.1); - if chained_move.is_ok() { - if !self.board.move_possible(color, &chained_move.unwrap()) { + if let Ok(chained_move) = moves.0.chain(moves.1) { + if !self.board.move_possible(color, &chained_move) { return false; } } else if !self.board.move_possible(color, &moves.1) { @@ -292,74 +291,63 @@ impl GameState { true } - fn moves_follows_dices(&self, color: &Color, moves: &(CheckerMove, CheckerMove)) -> bool { + fn get_move_compatible_dices(&self, color: &Color, cmove: &CheckerMove) -> Vec { 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 dist2 = (move2.get_to() as i8 - move2.get_from() as i8).abs() as u8; - // print!("{}, {}, {}, {}", dist1, dist2, dice1, dice2); - // exceptions - // - prise de coin par puissance + + let mut move_dices = Vec::new(); + if cmove.get_to() == 0 { + // Exits + let min_dist = match color { + 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) { return true; } - // - sorties - // default : must be same number - if cmp::min(dist1, dist2) != cmp::min(dice1, dice2) - || cmp::max(dist1, dist2) != cmp::max(dice1, dice2) + + let (dice1, dice2) = self.dice.values; + let (move1, move2): &(CheckerMove, CheckerMove) = moves; + + 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; } + // no rule was broken 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 { // ------- corner rules ---------- let corner_field: Field = self.board.get_color_corner(color); @@ -400,6 +388,52 @@ impl GameState { 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 // ---------------------------------------------------------------------------------- @@ -457,7 +491,7 @@ impl GameState { } EndGame { reason: _ } => self.stage = Stage::Ended, PlayerJoined { player_id, name } => { - let color = if self.players.len() > 0 { + let color = if !self.players.is_empty() { Color::White } else { Color::Black @@ -494,12 +528,7 @@ impl GameState { let player = self.players.get(player_id).unwrap(); self.board.move_checker(&player.color, moves.0).unwrap(); self.board.move_checker(&player.color, moves.1).unwrap(); - self.active_player_id = self - .players - .keys() - .find(|id| *id != player_id) - .unwrap() - .clone(); + self.active_player_id = *self.players.keys().find(|id| *id != player_id).unwrap(); self.turn_stage = TurnStage::RollDice; } } @@ -514,7 +543,7 @@ impl GameState { fn mark_points(&mut self, player_id: PlayerId, points: u8) { self.players.get_mut(&player_id).map(|p| { - p.points = p.points + points; + p.points += points; p }); } @@ -713,4 +742,41 @@ mod tests { assert!(!state.is_move_by_puissance(&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)); + } }