Skip to content
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