From: Geoffrey Allott Date: Thu, 1 Jun 2023 21:26:01 +0000 (+0100) Subject: fix revealing of box cards X-Git-Url: https://git.pointlesshacks.com/?a=commitdiff_plain;h=838133901900aacdf1b9c6fff9e562aaf555b75d;p=pokerwave.git fix revealing of box cards --- diff --git a/src/game/cribbage/mod.rs b/src/game/cribbage/mod.rs index f070bd4..ebf1724 100644 --- a/src/game/cribbage/mod.rs +++ b/src/game/cribbage/mod.rs @@ -112,7 +112,8 @@ impl Cribbage { } fn next_player_still_in(&self) -> Option { - self.active.and_then(|player| self.seats.player_after_where(player, |player| self.players_still_in.contains(&player))) + self.active.and_then(|player| self.seats.player_after_where(player, |player| self.players_still_in.contains(&player)) + .or_else(|| self.players_still_in.get(&player).copied())) } fn last_pegging_score(&self) -> Option<(Username, PeggingScore)> { @@ -145,6 +146,19 @@ impl Cribbage { }) } + fn unrevealed_box_card(&self) -> Option { + self.box_cards.iter().filter(|card| !self.revealed.contains(*card)).min().copied() + } + + fn four_card_box(&self) -> Option<[Card; 4]> { + let hand: Vec<_> = self.box_cards.iter().collect(); + if let [a, b, c, d] = hand[..] { + Some([*a, *b, *c, *d]) + } else { + None + } + } + fn player_has_won(&self, username: Username) -> bool { self.points.get(&username).map_or(false, |points| *points >= self.settings.target_score) } @@ -309,6 +323,10 @@ impl Game for Cribbage { Ok(()) } (State::Pegging, Action::Pass) => { + if Some(username) != self.active { + error!("Username taking action must be active"); + return Err(ActionError::OutOfTurn); + } self.players_still_in.remove(&username); match self.next_player_still_in() { None => { @@ -436,14 +454,14 @@ impl Game for Cribbage { DealerAction::Leave } } - State::Scoring | State::ScoringBox => { + State::Scoring => { if let Some(username) = self.winner() { DealerAction::TakeAction(ValidatedUserAction(UserAction { timestamp, username, action: Action::WinGame })) } else if let Some(username) = self.active { if let Some(card) = self.unrevealed_card(username) { DealerAction::TakeAction(ValidatedUserAction(UserAction { timestamp, username, action: Action::RevealCard { card } })) } else if let (Some(hand), Some(turn_up)) = (self.four_card_hand(username), self.turn_up) { - let score = score_4_card_cribbage_hand(hand, turn_up, self.state == State::ScoringBox); + let score = score_4_card_cribbage_hand(hand, turn_up, false); DealerAction::TakeAction(ValidatedUserAction(UserAction { timestamp, username, action: Action::Score { points: score.points() as u64, reason: format!("{}", score) } })) } else { error!("Found no 4-card hand for scoring user"); @@ -456,6 +474,26 @@ impl Game for Cribbage { DealerAction::Leave } } + State::ScoringBox => { + if let Some(username) = self.winner() { + DealerAction::TakeAction(ValidatedUserAction(UserAction { timestamp, username, action: Action::WinGame })) + } else if let Some(username) = self.active { + if let Some(card) = self.unrevealed_box_card() { + DealerAction::TakeAction(ValidatedUserAction(UserAction { timestamp, username, action: Action::RevealCard { card } })) + } else if let (Some(hand), Some(turn_up)) = (self.four_card_box(), self.turn_up) { + let score = score_4_card_cribbage_hand(hand, turn_up, true); + DealerAction::TakeAction(ValidatedUserAction(UserAction { timestamp, username, action: Action::Score { points: score.points() as u64, reason: format!("{}", score) } })) + } else { + error!("Found no 4-card box for scoring user"); + DealerAction::Leave + } + } else if let Some(username) = self.dealer.and_then(|dealer| self.seats.player_after(dealer)) { + DealerAction::TakeAction(ValidatedUserAction(UserAction { timestamp, username, action: Action::NextToDeal })) + } else { + error!("Could not find next dealer"); + DealerAction::Leave + } + } State::Completed => DealerAction::Leave, } } @@ -532,26 +570,63 @@ mod tests { {"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":1685568658569,"username":"Geoff","action":{"action":"RevealCard","card":{"rank":"Five","suit":"Clubs"}}}, + {"timestamp":1685568658569,"username":"Geoff","action":{"action":"RevealCard","card":{"rank":"Ten","suit":"Clubs"}}}, + {"timestamp":1685568658569,"username":"Geoff","action":{"action":"RevealCard","card":{"rank":"Jack","suit":"Diamonds"}}}, + {"timestamp":1685568658569,"username":"Geoff","action":{"action":"RevealCard","card":{"rank":"King","suit":"Clubs"}}}, + {"timestamp":1685568658570,"username":"Geoff","action":{"action":"Score","points":7,"reason":"Fifteen two, fifteen four, fifteen six and one for his nob is 7"}}, {"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"}} + {"timestamp":1685653386239,"username":"Geoff","action":{"action":"ReceiveCard","card":{"rank":"Queen","suit":"Spades"}}}, + {"timestamp":1685653386240,"username":"Aga","action":{"action":"ReceiveCard","card":{"rank":"King","suit":"Hearts"}}}, + {"timestamp":1685653386240,"username":"Geoff","action":{"action":"ReceiveCard","card":{"rank":"Ten","suit":"Diamonds"}}}, + {"timestamp":1685653386241,"username":"Aga","action":{"action":"ReceiveCard","card":{"rank":"Seven","suit":"Diamonds"}}}, + {"timestamp":1685653386241,"username":"Geoff","action":{"action":"ReceiveCard","card":{"rank":"Three","suit":"Diamonds"}}}, + {"timestamp":1685653386242,"username":"Aga","action":{"action":"ReceiveCard","card":{"rank":"Six","suit":"Spades"}}}, + {"timestamp":1685653386242,"username":"Geoff","action":{"action":"ReceiveCard","card":{"rank":"Eight","suit":"Clubs"}}}, + {"timestamp":1685653386243,"username":"Aga","action":{"action":"ReceiveCard","card":{"rank":"Four","suit":"Clubs"}}}, + {"timestamp":1685653386243,"username":"Geoff","action":{"action":"ReceiveCard","card":{"rank":"Five","suit":"Diamonds"}}}, + {"timestamp":1685653386244,"username":"Aga","action":{"action":"ReceiveCard","card":{"rank":"Three","suit":"Hearts"}}}, + {"timestamp":1685653386244,"username":"Geoff","action":{"action":"ReceiveCard","card":{"rank":"Six","suit":"Hearts"}}}, + {"timestamp":1685653386244,"username":"Aga","action":{"action":"ReceiveCard","card":{"rank":"Jack","suit":"Diamonds"}}}, + {"timestamp":1685653386245,"username":"Aga","action":{"action":"EndDeal"}}, + {"timestamp":1685653386245,"username":"Geoff","action":{"action":"PutInBox","card":{"rank":"Five","suit":"Diamonds"}}}, + {"timestamp":1685653386245,"username":"Aga","action":{"action":"PutInBox","card":{"rank":"Jack","suit":"Diamonds"}}}, + {"timestamp":1685653386245,"username":"Aga","action":{"action":"PutInBox","card":{"rank":"King","suit":"Hearts"}}}, + {"timestamp":1685653386245,"username":"Geoff","action":{"action":"PutInBox","card":{"rank":"Queen","suit":"Spades"}}}, + {"timestamp":1685653386245,"username":"Aga","action":{"action":"CommunityCard","card":{"rank":"Eight","suit":"Hearts"}}}, + {"timestamp":1685653387245,"username":"Geoff","action":{"action":"PlayCard","card":{"rank":"Eight","suit":"Clubs"}}}, + {"timestamp":1685653387245,"username":"Aga","action":{"action":"PlayCard","card":{"rank":"Seven","suit":"Diamonds"}}}, + {"timestamp":1685653387245,"username":"Aga","action":{"action":"Score","points":2,"reason":"Fifteen for two"}}, + {"timestamp":1685653387245,"username":"Geoff","action":{"action":"PlayCard","card":{"rank":"Six","suit":"Hearts"}}}, + {"timestamp":1685653387245,"username":"Geoff","action":{"action":"Score","points":3,"reason":"Three"}}, + {"timestamp":1685653387245,"username":"Aga","action":{"action":"PlayCard","card":{"rank":"Four","suit":"Clubs"}}}, + {"timestamp":1685653387245,"username":"Geoff","action":{"action":"PlayCard","card":{"rank":"Three","suit":"Diamonds"}}}, + {"timestamp":1685653387245,"username":"Aga","action":{"action":"PlayCard","card":{"rank":"Three","suit":"Hearts"}}}, + {"timestamp":1685653387245,"username":"Aga","action":{"action":"Score","points":4,"reason":"Thirty-one for two and two is four"}}, + {"timestamp":1685653387245,"username":"Geoff","action":{"action":"PlayCard","card":{"rank":"Ten","suit":"Diamonds"}}}, + {"timestamp":1685653387245,"username":"Aga","action":{"action":"PlayCard","card":{"rank":"Six","suit":"Spades"}}}, + {"timestamp":1685653387245,"username":"Aga","action":{"action":"Score","points":1,"reason":"One for a go"}}, + {"timestamp":1685653387245,"username":"Geoff","action":{"action":"RevealCard","card":{"rank":"Three","suit":"Diamonds"}}}, + {"timestamp":1685653387245,"username":"Geoff","action":{"action":"RevealCard","card":{"rank":"Six","suit":"Hearts"}}}, + {"timestamp":1685653387245,"username":"Geoff","action":{"action":"RevealCard","card":{"rank":"Eight","suit":"Clubs"}}}, + {"timestamp":1685653387245,"username":"Geoff","action":{"action":"RevealCard","card":{"rank":"Ten","suit":"Diamonds"}}}, + {"timestamp":1685653387245,"username":"Geoff","action":{"action":"Score","points":2,"reason":"Two"}}, + {"timestamp":1685653387245,"username":"Aga","action":{"action":"RevealCard","card":{"rank":"Three","suit":"Hearts"}}}, + {"timestamp":1685653387245,"username":"Aga","action":{"action":"RevealCard","card":{"rank":"Four","suit":"Clubs"}}}, + {"timestamp":1685653387245,"username":"Aga","action":{"action":"RevealCard","card":{"rank":"Six","suit":"Spades"}}}, + {"timestamp":1685653387245,"username":"Aga","action":{"action":"RevealCard","card":{"rank":"Seven","suit":"Diamonds"}}}, + {"timestamp":1685653387245,"username":"Aga","action":{"action":"Score","points":7,"reason":"Fifteen two, fifteen four and three is 7"}}, + {"timestamp":1685653387245,"username":"Aga","action":{"action":"RevealCard","card":{"rank":"Five","suit":"Diamonds"}}}, + {"timestamp":1685653387245,"username":"Aga","action":{"action":"RevealCard","card":{"rank":"Jack","suit":"Diamonds"}}}, + {"timestamp":1685653387245,"username":"Aga","action":{"action":"RevealCard","card":{"rank":"Queen","suit":"Spades"}}}, + {"timestamp":1685653387245,"username":"Aga","action":{"action":"RevealCard","card":{"rank":"King","suit":"Hearts"}}}, + {"timestamp":1685653387245,"username":"Aga","action":{"action":"Score","points":9,"reason":"Fifteen two, fifteen four, fifteen six and three is 9"}}, + {"timestamp":1685653387245,"username":"Aga","action":{"action":"WinGame"}} ]"#; let actions = serde_json::from_str(actions).unwrap(); - let settings = r#"{"format":"Cribbage","title":"Cribbage Testing","max_players":2,"target_score":121,"start_time":null}"#; + let settings = r#"{"format":"Cribbage","title":"Cribbage Testing","max_players":2,"target_score":21,"start_time":null}"#; let settings = serde_json::from_str(settings).unwrap(); let seed = r#"{"rng":"ChaCha20","seed":"3789582e9d1a9229bd22d2a61b156bcf907e4cb44d613f97c08b16ec73ff0d90"}"#; diff --git a/src/game/cribbage/score.rs b/src/game/cribbage/score.rs index 5ab2a6c..ec7379c 100644 --- a/src/game/cribbage/score.rs +++ b/src/game/cribbage/score.rs @@ -223,7 +223,7 @@ pub fn score_pegging(cards: &[Card], go: bool) -> PeggingScore { if cards.len() < 2 { break; } - if cards.iter().all_equal() { + if cards.iter().map(|card| card.rank).all_equal() { score.pair += (cards.len() * (cards.len() - 1)) as u8; break; } @@ -380,4 +380,11 @@ mod test { assert_eq!(2, score.points()); assert_eq!("Thirty-one for two", format!("{}", score)); } + + #[test] + fn pegging_pair_with_a_go() { + let score = score_pegging(&[TEN_OF_DIAMONDS, THREE_OF_DIAMONDS, THREE_OF_HEARTS], true); + assert_eq!(3, score.points()); + assert_eq!("Two and a go is three", format!("{}", score)); + } }