From 58122404aaf87f3ad2d7d3db6e89f611a41fb7cc Mon Sep 17 00:00:00 2001 From: Geoffrey Allott Date: Wed, 31 May 2023 22:53:27 +0100 Subject: [PATCH] fix some issues with cribbage scoring --- src/game/cribbage/mod.rs | 63 ++++++++++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 18 deletions(-) diff --git a/src/game/cribbage/mod.rs b/src/game/cribbage/mod.rs index 1373ac3..f070bd4 100644 --- a/src/game/cribbage/mod.rs +++ b/src/game/cribbage/mod.rs @@ -120,7 +120,7 @@ impl Cribbage { None => self.used_pegging_cards.last().map(|(username, _)| (*username, PeggingScore::one_for_a_go())), Some((username, _)) => { let cards: Vec<_> = self.pegging_cards.iter().map(|(_, card)| *card).collect(); - let score = score_pegging(&cards, false); + let score = score_pegging(&cards, self.all_hands_are_empty()); if score.points() > 0 { Some((*username, score)) } else { @@ -131,7 +131,7 @@ impl Cribbage { } fn unrevealed_card(&self, username: Username) -> Option { - self.hands.get(&username).and_then(|hand| hand.iter().filter(|card| !self.revealed.contains(*card)).next().copied()) + self.hands.get(&username).and_then(|hand| hand.iter().filter(|card| !self.revealed.contains(*card)).min().copied()) } fn four_card_hand(&self, username: Username) -> Option<[Card; 4]> { @@ -290,6 +290,10 @@ impl Game for Cribbage { Ok(()) } (State::Pegging, Action::PlayCard { card }) => { + if Some(username) != self.active { + error!("Username taking action must be active"); + return Err(ActionError::OutOfTurn); + } if let Some(hand) = self.hands.get_mut(&username) { hand.remove(&card); if hand.is_empty() { @@ -299,8 +303,9 @@ impl Game for Cribbage { self.pegging_cards.push((username, card)); if self.last_pegging_score().is_some() { self.state = State::ScoringPegging; + } else { + self.active = self.next_player_still_in(); } - self.active = self.next_player_still_in(); Ok(()) } (State::Pegging, Action::Pass) => { @@ -313,24 +318,13 @@ impl Game for Cribbage { }, active => self.active = active, } - if self.all_hands_are_empty() { - for (username, card) in self.used_pegging_cards.drain(..) { - self.hands.entry(username).or_default().insert(card); - } - self.active = self.dealer.and_then(|dealer| self.seats.player_after(dealer)); - self.state = State::Scoring; - } Ok(()) } (State::ScoringPegging, Action::Score { points, .. }) => { *self.points.entry(username).or_default() += points as u32; - if self.next_player_still_in().is_none() { + if self.next_player_still_in().is_none() || self.pegging_total() == 31 { self.used_pegging_cards.extend(self.pegging_cards.drain(..)); self.players_still_in = self.hands.iter().filter(|(_, cards)| !cards.is_empty()).map(|(&username, _)| username).collect(); - self.active = Some(username); - if !self.players_still_in.contains(&username) { - self.active = self.next_player_still_in(); - } } if self.player_has_won(username) { self.state = State::ScoringPegging; @@ -341,6 +335,7 @@ impl Game for Cribbage { self.active = self.dealer.and_then(|dealer| self.seats.player_after(dealer)); self.state = State::Scoring; } else { + self.active = self.next_player_still_in(); self.state = State::Pegging; } Ok(()) @@ -474,7 +469,7 @@ mod tests { let mut game = Cribbage::new(0, settings, seed); for action in actions { match action.action { - Action::Join { .. } | Action::PutInBox { .. } | Action::PlayCard { .. } => { + Action::Join { .. } | Action::PutInBox { .. } | Action::PlayCard { .. } | Action::Pass => { let validated = game.validate_action(action.clone()).unwrap(); assert_eq!(ValidatedUserAction(action), validated); game.take_action(validated).unwrap(); @@ -519,12 +514,44 @@ mod tests { {"timestamp":1685308376248,"username":"Aga","action":{"action":"PlayCard","card":{"rank":"King","suit":"Diamonds"}}}, {"timestamp":1685308376248,"username":"Geoff","action":{"action":"PlayCard","card":{"rank":"Two","suit":"Spades"}}}, {"timestamp":1685308376248,"username":"Aga","action":{"action":"PlayCard","card":{"rank":"Three","suit":"Hearts"}}}, - {"timestamp":1685309372399,"username":"Aga","action":{"action":"Score","points":2,"reason":"Fifteen for two"}} + {"timestamp":1685309372399,"username":"Aga","action":{"action":"Score","points":2,"reason":"Fifteen for two"}}, + {"timestamp":1685308376248,"username":"Geoff","action":{"action":"PlayCard","card":{"rank":"Nine","suit":"Clubs"}}}, + {"timestamp":1685308376248,"username":"Aga","action":{"action":"PlayCard","card":{"rank":"Four","suit":"Clubs"}}}, + {"timestamp":1685308376248,"username":"Geoff","action":{"action":"PlayCard","card":{"rank":"Three","suit":"Clubs"}}}, + {"timestamp":1685567770541,"username":"Geoff","action":{"action":"Score","points":2,"reason":"Thirty-one for two"}}, + {"timestamp":1685308376248,"username":"Aga","action":{"action":"PlayCard","card":{"rank":"King","suit":"Hearts"}}}, + {"timestamp":1685308376248,"username":"Geoff","action":{"action":"PlayCard","card":{"rank":"Seven","suit":"Clubs"}}}, + {"timestamp":1685568658530,"username":"Geoff","action":{"action":"Score","points":1,"reason":"One for a go"}}, + {"timestamp":1685568658532,"username":"Aga","action":{"action":"RevealCard","card":{"rank":"Three","suit":"Hearts"}}}, + {"timestamp":1685568658533,"username":"Aga","action":{"action":"RevealCard","card":{"rank":"Four","suit":"Clubs"}}}, + {"timestamp":1685568658531,"username":"Aga","action":{"action":"RevealCard","card":{"rank":"King","suit":"Diamonds"}}}, + {"timestamp":1685568658532,"username":"Aga","action":{"action":"RevealCard","card":{"rank":"King","suit":"Hearts"}}}, + {"timestamp":1685568658563,"username":"Aga","action":{"action":"Score","points":2,"reason":"Two"}}, + {"timestamp":1685568658567,"username":"Geoff","action":{"action":"RevealCard","card":{"rank":"Two","suit":"Spades"}}}, + {"timestamp":1685568658565,"username":"Geoff","action":{"action":"RevealCard","card":{"rank":"Three","suit":"Clubs"}}}, + {"timestamp":1685568658568,"username":"Geoff","action":{"action":"RevealCard","card":{"rank":"Seven","suit":"Clubs"}}}, + {"timestamp":1685568658564,"username":"Geoff","action":{"action":"RevealCard","card":{"rank":"Nine","suit":"Clubs"}}}, + {"timestamp":1685568658569,"username":"Geoff","action":{"action":"Score","points":4,"reason":"Fifteen two, fifteen four, look all day, see no more"}}, + {"timestamp":1685568658570,"username":"Geoff","action":{"action":"Score","points":4,"reason":"Fifteen two, fifteen four, look all day, see no more"}}, + {"timestamp":1685568658571,"username":"Aga","action":{"action":"NextToDeal"}}, + {"timestamp":1685568658572,"username":"Geoff","action":{"action":"ReceiveCard","card":{"rank":"Eight","suit":"Clubs"}}}, + {"timestamp":1685568658572,"username":"Aga","action":{"action":"ReceiveCard","card":{"rank":"Five","suit":"Diamonds"}}}, + {"timestamp":1685568658573,"username":"Geoff","action":{"action":"ReceiveCard","card":{"rank":"Ten","suit":"Diamonds"}}}, + {"timestamp":1685568658574,"username":"Aga","action":{"action":"ReceiveCard","card":{"rank":"Three","suit":"Diamonds"}}}, + {"timestamp":1685568658575,"username":"Geoff","action":{"action":"ReceiveCard","card":{"rank":"King","suit":"Spades"}}}, + {"timestamp":1685568658576,"username":"Aga","action":{"action":"ReceiveCard","card":{"rank":"Ace","suit":"Hearts"}}}, + {"timestamp":1685568658576,"username":"Geoff","action":{"action":"ReceiveCard","card":{"rank":"Jack","suit":"Diamonds"}}}, + {"timestamp":1685568658577,"username":"Aga","action":{"action":"ReceiveCard","card":{"rank":"Seven","suit":"Spades"}}}, + {"timestamp":1685568658578,"username":"Geoff","action":{"action":"ReceiveCard","card":{"rank":"Three","suit":"Hearts"}}}, + {"timestamp":1685568658579,"username":"Aga","action":{"action":"ReceiveCard","card":{"rank":"Seven","suit":"Diamonds"}}}, + {"timestamp":1685568658580,"username":"Geoff","action":{"action":"ReceiveCard","card":{"rank":"Eight","suit":"Spades"}}}, + {"timestamp":1685568658581,"username":"Aga","action":{"action":"ReceiveCard","card":{"rank":"Four","suit":"Diamonds"}}}, + {"timestamp":1685568658582,"username":"Aga","action":{"action":"EndDeal"}} ]"#; let actions = serde_json::from_str(actions).unwrap(); - let settings = r#"{"format":"Cribbage","title":"Cribbage Testing","max_players":2,"target_score":181,"start_time":null}"#; + let settings = r#"{"format":"Cribbage","title":"Cribbage Testing","max_players":2,"target_score":121,"start_time":null}"#; let settings = serde_json::from_str(settings).unwrap(); let seed = r#"{"rng":"ChaCha20","seed":"3789582e9d1a9229bd22d2a61b156bcf907e4cb44d613f97c08b16ec73ff0d90"}"#; -- 2.34.1