Skip to content

Commit 1fda697

Browse files
committed
fix: Update hare movement logic to handle negative distances and card usage
1 parent 6d25dbf commit 1fda697

File tree

7 files changed

+90
-54
lines changed

7 files changed

+90
-54
lines changed

logic.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
# not all imports are currently used, but they might be in the future and it shows all available functionalities
2-
import logging
2+
import math
33
import random
4-
from typing import List
4+
import time
5+
from typing import Optional, Tuple
56
from socha import (
7+
Field,
68
GameState,
79
Move
810
)
@@ -16,9 +18,7 @@ class Logic(IClientHandler):
1618
# this method is called every time the server is requesting a new move
1719
# this method should always be implemented otherwise the client will be disqualified
1820
def calculate_move(self) -> Move:
19-
moves: List[Move] = self.game_state.possible_moves()
20-
move: Move = random.choice(moves)
21-
return move
21+
return random.choice(self.game_state.possible_moves())
2222

2323
# this method is called every time the server has sent a new game state update
2424
# this method should be implemented to keep the game state up to date

src/plugin/action/advance.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ impl Advance {
2828
pub fn perform(&self, state: &mut GameState) -> Result<(), PyErr> {
2929
let mut player = state.clone_current_player();
3030

31-
player.advance_by(state, self.distance)?;
31+
player.advance_by(state, self.distance as isize, self.cards.clone())?;
3232

3333
let current_field = state.board.get_field(player.position).unwrap();
3434
if self.cards.is_empty() {
@@ -66,7 +66,15 @@ impl Advance {
6666
}
6767

6868
last_card = Some(card);
69-
card.perform(state)?;
69+
let mut remaining_cards = self.cards.clone();
70+
71+
if let Some(position) = player.cards.iter().position(|c| c == card) {
72+
remaining_cards.remove(position);
73+
} else {
74+
return Err(CannotPlayCardError::new_err("Card not owned"));
75+
}
76+
77+
card.perform(state, remaining_cards)?;
7078
}
7179
_ => {}
7280
}

src/plugin/action/card.rs

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use crate::plugin::{
77
field::Field,
88
game_state::GameState,
99
hare::Hare,
10+
rules_engine::RulesEngine,
1011
};
1112

1213
use super::Action;
@@ -29,11 +30,36 @@ impl Card {
2930
}
3031
}
3132

33+
fn move_to_field(
34+
&self,
35+
player: &mut Hare,
36+
state: &mut GameState,
37+
target_position: usize,
38+
cards: Vec<Card>,
39+
) -> Result<(), PyErr> {
40+
println!("target_position: {}", target_position);
41+
println!("player.position: {}", player.position);
42+
let distance = target_position as isize - player.position as isize;
43+
RulesEngine::can_advance_to(
44+
&state.board,
45+
distance,
46+
player,
47+
&state.clone_other_player(),
48+
cards,
49+
)?;
50+
51+
player.position = (player.position as isize + distance) as usize;
52+
53+
state.update_player(player.clone());
54+
Ok(())
55+
}
56+
3257
fn play(
3358
&self,
3459
state: &mut GameState,
3560
current: &mut Hare,
3661
other: &mut Hare,
62+
remaining_cards: Vec<Card>,
3763
) -> Result<(), PyErr> {
3864
match self {
3965
Card::FallBack => {
@@ -42,15 +68,21 @@ impl Card {
4268
"You can only play this card if you are ahead of the other player",
4369
));
4470
}
45-
current.move_to_field(state, other.position - 1)?;
71+
self.move_to_field(
72+
current,
73+
state,
74+
other.position.saturating_sub(1),
75+
remaining_cards,
76+
)?;
4677
}
4778
Card::HurryAhead => {
4879
if current.position > other.position {
4980
return Err(CannotPlayCardError::new_err(
5081
"You can only play this card if you are behind the other player",
5182
));
5283
}
53-
current.move_to_field(state, other.position + 1)?;
84+
self.move_to_field(current, state, other.position + 1, remaining_cards)?;
85+
// saturating add is here unnecessary because the board is finite and never larger than usize::MAX
5486
}
5587
Card::EatSalad => current.eat_salad(state)?,
5688
Card::SwapCarrots => {
@@ -90,7 +122,7 @@ impl Card {
90122
Ok(())
91123
}
92124

93-
pub fn perform(&self, state: &mut GameState) -> Result<(), PyErr> {
125+
pub fn perform(&self, state: &mut GameState, remaining_cards: Vec<Card>) -> Result<(), PyErr> {
94126
let mut current = state.clone_current_player();
95127
let mut other = state.clone_other_player();
96128

@@ -109,9 +141,9 @@ impl Card {
109141
.cards
110142
.iter()
111143
.position(|card| card == self)
112-
.ok_or_else(|| CardNotOwnedError::new_err(""))?;
144+
.ok_or_else(|| CardNotOwnedError::new_err("Card not owned"))?;
113145

114-
self.play(state, &mut current, &mut other)?;
146+
self.play(state, &mut current, &mut other, remaining_cards)?;
115147

116148
current.cards.remove(index);
117149

src/plugin/hare.rs

Lines changed: 15 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -81,23 +81,33 @@ impl Hare {
8181
self.carrots <= 10 && self.salads == 0
8282
}
8383

84-
pub fn advance_by(&mut self, state: &mut GameState, distance: usize) -> Result<(), PyErr> {
85-
let new_position = self.position + distance;
84+
pub fn advance_by(
85+
&mut self,
86+
state: &mut GameState,
87+
distance: isize,
88+
cards: Vec<Card>,
89+
) -> Result<(), PyErr> {
90+
let needed_carrots = RulesEngine::calculates_carrots(distance.unsigned_abs());
91+
92+
if self.carrots - needed_carrots < 0 {
93+
return Err(MissingCarrotsError::new_err("Not enough carrots"));
94+
}
8695

8796
RulesEngine::can_advance_to(
8897
&state.board,
89-
new_position,
98+
distance,
9099
self,
91100
&state.clone_other_player(),
101+
cards,
92102
)?;
93103

94-
let needed_carrots = RulesEngine::calculates_carrots(distance);
104+
let new_position = (self.position as isize + distance) as usize;
95105

96106
self.carrots -= needed_carrots;
97107
self.position = new_position;
98108

99109
state.update_player(self.clone());
100-
110+
101111
Ok(())
102112
}
103113

@@ -128,23 +138,6 @@ impl Hare {
128138
Ok(())
129139
}
130140

131-
pub fn move_to_field(
132-
&mut self,
133-
state: &mut GameState,
134-
new_position: usize,
135-
) -> Result<(), PyErr> {
136-
RulesEngine::can_advance_to(
137-
&state.board,
138-
new_position,
139-
self,
140-
&state.clone_other_player(),
141-
)?;
142-
self.position = new_position;
143-
144-
state.update_player(self.clone());
145-
Ok(())
146-
}
147-
148141
pub fn get_fall_back(&self, state: &GameState) -> Option<usize> {
149142
match state
150143
.board

src/plugin/rules_engine.rs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::plugin::errors::{
66
};
77

88
use super::{
9-
action::{eat_salad::EatSalad, Action},
9+
action::{card::Card, eat_salad::EatSalad, Action},
1010
board::Board,
1111
errors::{CannotEnterFieldError, NoSaladError},
1212
field::Field,
@@ -95,26 +95,23 @@ impl RulesEngine {
9595
#[staticmethod]
9696
pub fn can_advance_to(
9797
board: &Board,
98-
distance: usize,
98+
distance: isize,
9999
player: &Hare,
100100
other_player: &Hare,
101+
cards: Vec<Card>,
101102
) -> Result<(), PyErr> {
102103
if distance == 0 {
103104
return Err(CannotEnterFieldError::new_err(
104105
"Advance distance cannot be 0",
105106
));
106107
}
107108

108-
let new_position = player.position + distance;
109+
let new_position = (player.position as isize + distance) as usize;
109110

110111
if new_position == 0 {
111112
return Err(CannotEnterFieldError::new_err("Cannot jump to position 0"));
112113
}
113114

114-
if player.carrots - Self::calculates_carrots(distance) < 0 {
115-
return Err(MissingCarrotsError::new_err("Not enough carrots"));
116-
}
117-
118115
Self::has_to_eat_salad(board, player)?;
119116

120117
let field = board
@@ -131,7 +128,7 @@ impl RulesEngine {
131128
)),
132129
Field::Salad if player.salads > 0 => Ok(()),
133130
Field::Salad => Err(FieldOccupiedError::new_err("Field is occupied by opponent")),
134-
Field::Hare if !player.cards.is_empty() => Ok(()),
131+
Field::Hare if !cards.is_empty() => Ok(()),
135132
Field::Hare => Err(CardNotOwnedError::new_err("No card to play")),
136133
Field::Market if player.carrots >= 10 => Ok(()),
137134
Field::Market => Err(MissingCarrotsError::new_err("Not enough carrots")),

src/plugin/test/card_test.rs

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,9 @@ mod tests {
4343
fn test_fallback_card() {
4444
let mut state = create_test_game_state();
4545
let fallback_card = Card::FallBack;
46-
assert!(fallback_card.perform(&mut state).is_ok());
46+
assert!(fallback_card
47+
.perform(&mut state, vec![Card::EatSalad, Card::SwapCarrots])
48+
.is_ok());
4749
let current_player = state.clone_current_player();
4850
assert_eq!(current_player.position, 2);
4951
}
@@ -52,8 +54,8 @@ mod tests {
5254
fn test_hurryahead_card() {
5355
let mut state = create_test_game_state();
5456
state.turn = 1;
55-
let hurry_ahead_card = Card::HurryAhead;
56-
assert!(hurry_ahead_card.perform(&mut state).is_ok());
57+
let hurry_ahead_card: Card = Card::HurryAhead;
58+
assert!(hurry_ahead_card.perform(&mut state, vec![]).is_ok());
5759
let current_player = state.clone_current_player();
5860
assert_eq!(current_player.position, 7);
5961
}
@@ -62,7 +64,9 @@ mod tests {
6264
fn test_eatsalad_card() {
6365
let mut state = create_test_game_state();
6466
let eat_salad_card = Card::EatSalad;
65-
assert!(eat_salad_card.perform(&mut state).is_ok());
67+
assert!(eat_salad_card
68+
.perform(&mut state, vec![Card::FallBack, Card::SwapCarrots])
69+
.is_ok());
6670
let current_player = state.clone_current_player();
6771
assert_eq!(current_player.salads, 2);
6872
}
@@ -71,7 +75,9 @@ mod tests {
7175
fn test_swapcarrots_card() {
7276
let mut state = create_test_game_state();
7377
let swap_carrots_card = Card::SwapCarrots;
74-
assert!(swap_carrots_card.perform(&mut state).is_ok());
78+
assert!(swap_carrots_card
79+
.perform(&mut state, vec![Card::FallBack, Card::EatSalad])
80+
.is_ok());
7581
let current_player = state.clone_current_player();
7682
let other_player = state.clone_other_player();
7783
assert_eq!(current_player.carrots, 60);
@@ -83,7 +89,7 @@ mod tests {
8389
let mut state = create_test_game_state();
8490
state.turn = 1;
8591
let card_not_owned = Card::FallBack;
86-
let result = card_not_owned.perform(&mut state);
92+
let result = card_not_owned.perform(&mut state, vec![Card::HurryAhead]);
8793
assert!(result.is_err());
8894
}
8995

@@ -94,7 +100,7 @@ mod tests {
94100
let mut current_player = state.clone_current_player();
95101
current_player.position = 1;
96102
state.update_player(current_player);
97-
let result = card.perform(&mut state);
103+
let result = card.perform(&mut state, vec![Card::EatSalad, Card::SwapCarrots]);
98104
assert!(result.is_err());
99105
}
100106

@@ -103,7 +109,7 @@ mod tests {
103109
let mut state = create_test_game_state();
104110
let invalid_card = Card::FallBack;
105111
state.board.track.clear();
106-
let result = invalid_card.perform(&mut state);
112+
let result = invalid_card.perform(&mut state, vec![Card::EatSalad, Card::SwapCarrots]);
107113
assert!(result.is_err());
108114
}
109115
}

src/plugin/test/rules_test.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,14 +77,14 @@ mod tests {
7777
let mut player_one = create_player(TeamEnum::One, 0);
7878
let player_two = create_player(TeamEnum::Two, 2);
7979

80-
assert!(RulesEngine::can_advance_to(&board, 3, &player_one, &player_two).is_ok());
80+
assert!(RulesEngine::can_advance_to(&board, 3, &player_one, &player_two, vec![Card::FallBack]).is_ok());
8181

82-
assert!(RulesEngine::can_advance_to(&board, 2, &player_one, &player_two).is_err());
82+
assert!(RulesEngine::can_advance_to(&board, 2, &player_one, &player_two, vec![]).is_err());
8383

8484
player_one.carrots = 1;
85-
assert!(RulesEngine::can_advance_to(&board, 5, &player_one, &player_two).is_err());
85+
assert!(RulesEngine::can_advance_to(&board, 5, &player_one, &player_two, vec![]).is_err());
8686

8787
player_one.cards = vec![];
88-
assert!(RulesEngine::can_advance_to(&board, 6, &player_one, &player_two).is_err());
88+
assert!(RulesEngine::can_advance_to(&board, 6, &player_one, &player_two, vec![]).is_err());
8989
}
9090
}

0 commit comments

Comments
 (0)