Closed ctm closed 3 years ago
Hmmm... This appears to be that part of mb2 is still thinking the "game" is "PRP" while other parts of mb2 are thinking the "game" is "Hold'em". So PRP itself doesn't have a functioning hand_winnings
method, yet that's what's getting called.
This should be trivial to find and fix.
Sure enough:
self.upcoming_game = Some(game);
self.betting_rounds = betting_rounds;
self.betting_rounds.set_index(betting_round_index);
if self.drawing.is_some() {
self.table_message(TableMessage::ChosenGame(desc, true));
} else {
self.table_message(TableMessage::ChosenGame(desc, false));
// FWIW, this may be unnecessary once we adjust
// game_keeper, because we may wind up sending
// status at the beginning of each betting round
// anyway. I don't rememember.
self.send_status(None);
self.choices = None;
}
Mb2 sets up the betting rounds without actually telling the GameKeeper
that we're playing a new game.
I can't remember why I deferred updating GameKeeper
. I'll poke around a bit and see if anything jars my memory. I should have written a comment, but I'm sure it was obvious at the time (maybe if I get a few more decades of programming under my belt I'll learn my lesson!). Anyway, there are two solutions. The cleanest is to not defer the assignment. The other is to also add an assignment where all-ins short-circuit the path that normally has the deferred assignment. Ugh!
Git to the rescue:
commit e60909f501d900e6347b16feb663818b5f487742
Author: Clifford T. Matthews <ctm@devctm.com>
Date: Sun Jun 27 12:09:08 2021 -0600
[Bug] allows UTG raises in Paradise Road Pick'em Stud. Closes #641.
diff --git a/mb2/src/table.rs b/mb2/src/table.rs
index d110d794..a63028fa 100644
--- a/mb2/src/table.rs
+++ b/mb2/src/table.rs
@@ -394,6 +394,7 @@ pub struct Table {
button: Button,
betting_rounds: BettingRounds,
pub(crate) game: Box<dyn GameGroup + Send + Sync>,
+ upcoming_game: Option<Box<dyn GameGroup + Send + Sync>>,
...
We used to switch the game immediately, but that precluded UTG raises in stud. I'll add a comment to the code and then add the appropriate clean-up assignment to game
when we do the short-circuit.
This bug is sufficiently embarrassing (it crashed yesterday's evening tournament) and the issue is sufficiently tricky that I'm going to wear my underpants on the outside just to show that I've cleaned them up and to encourage me to soil them less frequently.
diff --git a/mb2/src/table.rs b/mb2/src/table.rs
index 8d6610db..87fe3be1 100644
--- a/mb2/src/table.rs
+++ b/mb2/src/table.rs
@@ -420,6 +420,11 @@ pub struct Table {
button: Button,
betting_rounds: BettingRounds,
pub(crate) game: Box<dyn GameGroup + Send + Sync>,
+ // upcoming_game is used by Paradise Road Pick'em to defer
+ // changing the raising semantics until after the cleanup betting
+ // round has been completed because stud during cleanup allows the
+ // first to act to raise (and there's no concept of a bring-in or
+ // completion).
upcoming_game: Option<Box<dyn GameGroup + Send + Sync>>,
pub(crate) on_break: bool,
to_nick_mapper: ToNickMapper,
@@ -891,6 +896,12 @@ impl Table {
self.awaiting_redraw = true;
}
+ pub(crate) fn switch_to_upcoming_game(&mut self) {
+ if let Some(game) = self.upcoming_game.take() {
+ self.game = game;
+ }
+ }
+
#[allow(clippy::too_many_arguments)]
pub(crate) fn new(
id: TableId,
@@ -3196,6 +3207,16 @@ impl Table {
true
}
+ // The switch_to_upcoming_game calls are slightly tricky, in that
+ // currently we could just put a single call to
+ // switch_to_upcoming_game in continue_dealing, because currently
+ // our only use of upcoming_game is in the cleanup portion of
+ // Paradise Road Pick'em and that portion uses combined
+ // card-manipulation and action. However, a previous version of
+ // Paradise Road Pick'em used Mixed Deal where card-manipulation
+ // and action were separate and were we ever to allow such a
+ // thing, we *wouldn't* want to switch to the upcoming game until
+ // after the manipulation was done.
fn proceed_to_next_player_after(&mut self, seat: SeatIndex) {
use NextAction::*;
@@ -3203,8 +3224,10 @@ impl Table {
self.action_timer = None;
self.timer_sequence = self.timer_sequence.wrapping_add(1);
let message = if self.n_not_folded < 2 {
+ self.switch_to_upcoming_game();
AwardUncontested(self.id)
} else if self.n_not_all_in < 1 && self.drawing.is_none() {
+ self.switch_to_upcoming_game();
ContinueDealing(self.id)
} else {
let next_to_act = self.next_actionable_seat(seat);
@@ -3223,9 +3246,7 @@ impl Table {
|| self.choices.is_some())
{
if self.drawing.is_none() {
- if let Some(game) = self.upcoming_game.take() {
- self.game = game;
- }
+ self.switch_to_upcoming_game();
ContinueDealing(self.id)
} else {
self.choices = None;
Deploying now.
The hand involved was probably #202937:
In addition to the crash, AIYAH didn't have to discard. That may actually be a separate issue. If so, I'll split it off...