diff --git a/python/socha/_socha.pyi b/python/socha/_socha.pyi index 828df5a..953efea 100644 --- a/python/socha/_socha.pyi +++ b/python/socha/_socha.pyi @@ -374,6 +374,8 @@ class Move: ... def __repr__(self) -> str: ... + def __eq__(self) -> bool: ... + class GameState: """ Repräsentiert den aktuellen Zustand des Spiels. @@ -441,6 +443,15 @@ class GameState: """ ... + def possible_moves_old(self) -> List[Move]: + """ + Gibt eine Liste aller möglichen Züge zurück. + + Returns: + List[Move]: Eine Liste aller möglichen Züge. + """ + ... + def possible_moves(self) -> List[Move]: """ Gibt eine Liste aller möglichen Züge zurück. diff --git a/src/plugin/action/card.rs b/src/plugin/action/card.rs index 3cb8fe2..32fad01 100644 --- a/src/plugin/action/card.rs +++ b/src/plugin/action/card.rs @@ -35,6 +35,11 @@ impl Card { cards: Vec, ) -> Result<(), PyErr> { let distance = target_position as isize - player.position as isize; + + if target_position == PluginConstants::NUM_FIELDS - 1 && (player.carrots > 10 || player.salads > 0) { + return Err(HUIError::new_err("Too many carrots or salads to jump to goal")); + } + RulesEngine::can_move_to( &state.board, distance, diff --git a/src/plugin/field.rs b/src/plugin/field.rs index 99e5411..1435589 100644 --- a/src/plugin/field.rs +++ b/src/plugin/field.rs @@ -28,15 +28,15 @@ pub enum Field { impl std::fmt::Display for Field { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Field::Position1 => write!(f, "1"), - Field::Position2 => write!(f, "2"), - Field::Hedgehog => write!(f, "I"), - Field::Salad => write!(f, "S"), - Field::Carrots => write!(f, "C"), - Field::Hare => write!(f, "H"), - Field::Market => write!(f, "M"), - Field::Goal => write!(f, "G"), - Field::Start => write!(f, "S"), + Field::Position1 => write!(f, "Pos1"), + Field::Position2 => write!(f, "Pos2"), + Field::Hedgehog => write!(f, "Hedgehog"), + Field::Salad => write!(f, "Salad"), + Field::Carrots => write!(f, "Carrot"), + Field::Hare => write!(f, "Hare"), + Field::Market => write!(f, "Market"), + Field::Goal => write!(f, "Goal"), + Field::Start => write!(f, "Start"), } } } diff --git a/src/plugin/game_state.rs b/src/plugin/game_state.rs index 8b63043..d69fd41 100644 --- a/src/plugin/game_state.rs +++ b/src/plugin/game_state.rs @@ -5,6 +5,7 @@ use super::action::advance::Advance; use super::action::eat_salad::EatSalad; use super::action::exchange_carrots::ExchangeCarrots; use super::action::fall_back::FallBack; +use super::action::card::Card; use super::action::Action; use super::board::Board; use super::constants::PluginConstants; @@ -105,6 +106,17 @@ impl GameState { player_one_in_goal || player_two_in_goal && both_had_last_chance || rounds_exceeded } + pub fn possible_moves_old(&self) -> Vec { + let mut moves = Vec::new(); + + moves.append(&mut self.possible_advance_moves_old()); + moves.append(&mut self.possible_eat_salad_moves()); + moves.append(&mut self.possible_exchange_carrots_moves()); + moves.append(&mut self.possible_fall_back_moves()); + + moves + } + pub fn possible_moves(&self) -> Vec { let mut moves = Vec::new(); @@ -146,7 +158,8 @@ impl GameState { .collect() } - fn possible_advance_moves(&self) -> Vec { + fn possible_advance_moves_old(&self) -> Vec { + let current_player = self.clone_current_player(); let max_distance = (((-1.0 + (1 + 8 * current_player.carrots) as f64).sqrt()) / 2.0) as usize; @@ -189,6 +202,79 @@ impl GameState { .collect() } + fn possible_advance_moves(&self) -> Vec { + + let current_player = self.clone_current_player(); + let max_distance = + (((-1.0 + (1 + 8 * current_player.carrots) as f64).sqrt()) / 2.0) as usize; + + + let mut card_permutations = Vec::new(); + + for k in 0..=current_player.cards.len() { + for permutation in current_player.cards.iter().permutations(k).unique() { + + // change permutation cards to owned + let owned_permutation: Vec = permutation.iter().map(|&card| *card).collect(); + + // if minimum one card in permutation, save permutation and add all market cards to it + if !owned_permutation.is_empty() { + card_permutations.push(owned_permutation.clone()); + + for card in PluginConstants::MARKET_SELECTION { + let mut extended_permutation = owned_permutation.clone(); + extended_permutation.push(card); // card is already owned + card_permutations.push(extended_permutation); + } + } + } + } + + let mut moves: Vec = Vec::new(); + + for distance in 1..=max_distance { + // destination of advance + let target_pos: usize = current_player.position + distance; + + // out of range, skip + if target_pos > self.board.track.len() - 1 { + continue; + } + + // destination field of advance + let target_field: Field = self.board.track[target_pos]; + + // add card / no card advances for each field type + match target_field { + Field::Hare => { + for permutation in &card_permutations { + moves.push(Move::new(Action::Advance(Advance::new( + distance, + permutation.to_vec(), + )))); + } + }, + Field::Market => { + for card in PluginConstants::MARKET_SELECTION { + moves.push(Move::new(Action::Advance(Advance::new( + distance, + vec![card], + )))); + } + }, + _ => { + moves.push(Move::new(Action::Advance(Advance::new(distance, vec![])))); + } + } + } + + moves + .into_iter() + .unique() + .filter(|m| m.perform(&mut self.clone()).is_ok()) + .collect() + } + pub fn __repr__(&self) -> String { format!("{:?}", self) } diff --git a/src/plugin/move.rs b/src/plugin/move.rs index 4960bbd..a3a758d 100644 --- a/src/plugin/move.rs +++ b/src/plugin/move.rs @@ -31,6 +31,10 @@ impl Move { fn __repr__(&self) -> PyResult { Ok(format!("Move(action={:?})", self.action)) } + + fn __eq__(&self, other: &Move) -> PyResult { + Ok(self == other) + } } impl std::fmt::Display for Move { diff --git a/src/plugin/test/card_test.rs b/src/plugin/test/card_test.rs index d332416..aee758e 100644 --- a/src/plugin/test/card_test.rs +++ b/src/plugin/test/card_test.rs @@ -62,6 +62,39 @@ mod tests { assert!(hurry_ahead_card.perform(&mut state, vec![], 0).is_ok()); let current_player = state.clone_current_player(); assert_eq!(current_player.position, 8); + + // test hurry ahead in goal with too many carrots (salads are ok but that was never the problem) + let mut state = create_test_game_state(); + state.turn = 1; + + let mut current_player = state.clone_current_player(); + current_player.salads = 0; + state.update_player(current_player); + let mut other_player = state.clone_other_player(); + other_player.position = 10; + state.update_player(other_player); + + let hurry_ahead_card: Card = Card::HurryAhead; + assert!(hurry_ahead_card.perform(&mut state, vec![], 0).is_err()); + let current_player = state.clone_current_player(); + assert_eq!(current_player.position, 3); + + // test hurry ahead in goal with low enough many carrots + let mut state = create_test_game_state(); + state.turn = 1; + + let mut current_player = state.clone_current_player(); + current_player.carrots = 3; + current_player.salads = 0; + state.update_player(current_player); + let mut other_player = state.clone_other_player(); + other_player.position = 10; + state.update_player(other_player); + + let hurry_ahead_card: Card = Card::HurryAhead; + assert!(hurry_ahead_card.perform(&mut state, vec![], 0).is_ok()); + let current_player = state.clone_current_player(); + assert_eq!(current_player.position, 11); } #[test]