Skip to content

Fix hurry ahead bug + Performance Possible Moves #91

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
May 31, 2025
Merged
11 changes: 11 additions & 0 deletions python/socha/_socha.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,8 @@ class Move:
...
def __repr__(self) -> str: ...

def __eq__(self) -> bool: ...

class GameState:
"""
Repräsentiert den aktuellen Zustand des Spiels.
Expand Down Expand Up @@ -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.
Expand Down
5 changes: 5 additions & 0 deletions src/plugin/action/card.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ impl Card {
cards: Vec<Card>,
) -> 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,
Expand Down
18 changes: 9 additions & 9 deletions src/plugin/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
}
}
}
88 changes: 87 additions & 1 deletion src/plugin/game_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<Move> {
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<Move> {
let mut moves = Vec::new();

Expand Down Expand Up @@ -146,7 +158,8 @@ impl GameState {
.collect()
}

fn possible_advance_moves(&self) -> Vec<Move> {
fn possible_advance_moves_old(&self) -> Vec<Move> {

let current_player = self.clone_current_player();
let max_distance =
(((-1.0 + (1 + 8 * current_player.carrots) as f64).sqrt()) / 2.0) as usize;
Expand Down Expand Up @@ -189,6 +202,79 @@ impl GameState {
.collect()
}

fn possible_advance_moves(&self) -> Vec<Move> {

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<Card> = 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<Move> = 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)
}
Expand Down
4 changes: 4 additions & 0 deletions src/plugin/move.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ impl Move {
fn __repr__(&self) -> PyResult<String> {
Ok(format!("Move(action={:?})", self.action))
}

fn __eq__(&self, other: &Move) -> PyResult<bool> {
Ok(self == other)
}
}

impl std::fmt::Display for Move {
Expand Down
33 changes: 33 additions & 0 deletions src/plugin/test/card_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down