Skip to content

Commit a4bd343

Browse files
maxblanYoEnte
andcommitted
fix: advance logic and move generation
Co-authored-by: YoEnte <[email protected]>
1 parent 12bf9eb commit a4bd343

File tree

7 files changed

+154
-39
lines changed

7 files changed

+154
-39
lines changed

src/plugin/action/advance.rs

+43-18
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use pyo3::{pyclass, pymethods, PyErr};
22

3-
use crate::plugin::{errors::HUIError, field::Field, game_state::GameState};
3+
use crate::plugin::{errors::HUIError, field::Field, game_state::GameState, hare::Hare};
44

55
use super::card::Card;
66

@@ -28,21 +28,44 @@ impl Advance {
2828

2929
let current_field = state.board.get_field(player.position).unwrap();
3030
if self.cards.is_empty() {
31-
match current_field {
32-
Field::Market | Field::Hare => {
33-
return Err(HUIError::new_err("Cannot enter field without any cards"));
34-
}
35-
_ => {
36-
state.update_player(player);
37-
return Ok(());
38-
}
31+
return self.handle_empty_cards(current_field, state, player);
32+
}
33+
34+
self.handle_cards(current_field, state, player)
35+
}
36+
37+
fn handle_empty_cards(
38+
&self,
39+
current_field: Field,
40+
state: &mut GameState,
41+
player: Hare,
42+
) -> Result<(), PyErr> {
43+
match current_field {
44+
Field::Market | Field::Hare => {
45+
Err(HUIError::new_err("Cannot enter field without any cards"))
46+
}
47+
_ => {
48+
state.update_player(player);
49+
Ok(())
3950
}
4051
}
52+
}
4153

54+
fn handle_cards(
55+
&self,
56+
mut current_field: Field,
57+
state: &mut GameState,
58+
mut player: Hare,
59+
) -> Result<(), PyErr> {
4260
let mut last_card: Option<&Card> = None;
4361
let mut card_bought = false;
4462

45-
for card in &self.cards {
63+
for (index, card) in self.cards.iter().enumerate() {
64+
let remaining_cards = self
65+
.cards
66+
.get(index + 1..)
67+
.map(|slice| slice.to_vec())
68+
.unwrap_or(Vec::new());
4669
match current_field {
4770
Field::Market if card_bought => {
4871
return Err(HUIError::new_err("Only one card allowed to buy"));
@@ -60,18 +83,20 @@ impl Advance {
6083
}
6184

6285
last_card = Some(card);
63-
let mut remaining_cards = self.cards.clone();
64-
65-
if let Some(position) = remaining_cards.iter().position(|c| c == card) {
66-
remaining_cards.remove(position);
67-
} else {
68-
return Err(HUIError::new_err("Card not in list of cards"))?;
69-
}
7086

71-
card.perform(state, remaining_cards)?;
87+
card.perform(state, remaining_cards.clone())?;
88+
player = state.clone_current_player();
7289
}
7390
_ => Err(HUIError::new_err("Card cannot be played on this field"))?,
7491
}
92+
93+
current_field = state.board.get_field(player.position).unwrap();
94+
if current_field == Field::Hare && remaining_cards.is_empty() && last_card.is_none() {
95+
return Err(HUIError::new_err("Cannot enter field without any cards"));
96+
}
97+
if current_field == Field::Market && remaining_cards.is_empty() && !card_bought {
98+
return Err(HUIError::new_err("Cannot enter field without any cards"));
99+
}
75100
}
76101

77102
state.update_player(player);

src/plugin/board.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ impl Board {
2525

2626
/// Finds the index of the specified field within the given range.
2727
pub fn find_field(&self, field: Field, start: usize, end: usize) -> Option<usize> {
28-
(start..end).find(|&i| self.track.get(i) == Some(&field))
28+
(start..=end).find(|&i| self.track.get(i) == Some(&field))
2929
}
3030

3131
/// Finds the previous occurrence of the specified field before the given index.

src/plugin/constants.rs

+9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use pyo3::*;
22

3+
use super::action::card::Card;
4+
35
#[pyclass]
46
pub struct PluginConstants;
57

@@ -13,4 +15,11 @@ impl PluginConstants {
1315
pub const ROUND_LIMIT: usize = 30;
1416

1517
pub const LAST_LETTUCE_POSITION: usize = 57;
18+
19+
pub const MARKET_SELECTION: [Card; 4] = [
20+
Card::FallBack,
21+
Card::HurryAhead,
22+
Card::EatSalad,
23+
Card::SwapCarrots,
24+
];
1625
}

src/plugin/game_state.rs

+18-19
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ use itertools::Itertools;
22
use pyo3::*;
33

44
use super::action::advance::Advance;
5-
use super::action::card::Card;
65
use super::action::eat_salad::EatSalad;
76
use super::action::exchange_carrots::ExchangeCarrots;
87
use super::action::fall_back::FallBack;
@@ -149,29 +148,28 @@ impl GameState {
149148
let mut moves = Vec::new();
150149

151150
for distance in 1..=max_distance {
152-
if let Some(Field::Hare) = self.board.get_field(current_player.position + distance) {
153-
for k in 0..current_player.cards.len() {
154-
for combination in current_player.cards.iter().combinations(k) {
155-
moves.push(Move::new(Action::Advance(Advance::new(
156-
distance,
157-
combination.iter().map(|&c| *c).collect(),
158-
))));
159-
}
160-
}
151+
for card in PluginConstants::MARKET_SELECTION {
152+
moves.push(Move::new(Action::Advance(Advance::new(
153+
distance,
154+
vec![card],
155+
))));
161156
}
162157

163-
if self.board.get_field(current_player.position + distance) == Some(Field::Market) {
164-
let cards = vec![
165-
Card::FallBack,
166-
Card::HurryAhead,
167-
Card::EatSalad,
168-
Card::SwapCarrots,
169-
];
170-
for card in cards {
158+
for k in 0..=current_player.cards.len() {
159+
for permutation in current_player.cards.iter().permutations(k).unique() {
171160
moves.push(Move::new(Action::Advance(Advance::new(
172161
distance,
173-
vec![card],
162+
permutation.iter().map(|&c| *c).collect(),
174163
))));
164+
165+
for card in PluginConstants::MARKET_SELECTION {
166+
let mut extended_permutaion = permutation.clone();
167+
extended_permutaion.push(&card);
168+
moves.push(Move::new(Action::Advance(Advance::new(
169+
distance,
170+
extended_permutaion.iter().map(|&c| *c).collect(),
171+
))));
172+
}
175173
}
176174
}
177175

@@ -180,6 +178,7 @@ impl GameState {
180178

181179
moves
182180
.into_iter()
181+
.unique()
183182
.filter(|m| m.perform(&mut self.clone()).is_ok())
184183
.collect()
185184
}

src/plugin/test.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ pub mod advance_test;
22
pub mod board_test;
33
pub mod card_test;
44
pub mod rules_test;
5+
pub mod state_test;

src/plugin/test/advance_test.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ mod tests {
6060
assert!(result.is_ok());
6161

6262
let current_player = state.clone_current_player();
63-
assert_eq!(current_player.position, 6);
63+
assert_eq!(current_player.position, 2);
6464
}
6565

6666
#[test]

src/plugin/test/state_test.rs

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
#[cfg(test)]
2+
mod tests {
3+
use std::vec;
4+
5+
use crate::plugin::{
6+
action::{advance::Advance, card::Card, Action},
7+
board::Board,
8+
field::Field,
9+
game_state::GameState,
10+
hare::{Hare, TeamEnum},
11+
r#move::Move,
12+
};
13+
14+
fn create_player(
15+
team: TeamEnum,
16+
position: usize,
17+
cards: Vec<Card>,
18+
carrots: i32,
19+
salads: i32,
20+
) -> Hare {
21+
Hare::new(
22+
team,
23+
Some(cards),
24+
Some(carrots),
25+
Some(salads),
26+
None,
27+
Some(position),
28+
)
29+
}
30+
31+
fn create_board() -> Board {
32+
Board::new(vec![
33+
Field::Start,
34+
Field::Salad,
35+
Field::Position2,
36+
Field::Hare,
37+
Field::Carrots,
38+
Field::Market,
39+
Field::Position1,
40+
Field::Hare,
41+
Field::Goal,
42+
])
43+
}
44+
45+
#[test]
46+
fn test_possible_advance_moves_with_one_card() {
47+
let state = GameState::new(
48+
create_board(),
49+
20,
50+
create_player(TeamEnum::One, 2, vec![Card::EatSalad], 37, 1),
51+
create_player(TeamEnum::Two, 6, vec![], 11, 1),
52+
);
53+
let moves = state.possible_moves();
54+
assert!(moves.contains(&Move::new(Action::Advance(Advance::new(
55+
1,
56+
vec![Card::EatSalad]
57+
)))));
58+
}
59+
60+
#[test]
61+
fn test_possible_advance_moves_with_hurry_ahead_back_and_market() {
62+
let state = GameState::new(
63+
create_board(),
64+
20,
65+
create_player(
66+
TeamEnum::One,
67+
2,
68+
vec![Card::HurryAhead, Card::FallBack],
69+
37,
70+
0,
71+
),
72+
create_player(TeamEnum::Two, 6, vec![], 11, 0),
73+
);
74+
let moves = state.possible_moves();
75+
76+
assert!(moves.contains(&Move::new(Action::Advance(Advance::new(
77+
1,
78+
vec![Card::HurryAhead, Card::FallBack, Card::EatSalad]
79+
)))));
80+
}
81+
}

0 commit comments

Comments
 (0)