From 8bbadba7d95b212138c736903cddb9906598c94e Mon Sep 17 00:00:00 2001 From: Jarvis Date: Sat, 21 Feb 2026 10:45:15 +0000 Subject: [PATCH] Fix trade-in-jail bug: jail handler must not roll during active trade Added guard in jail turn handler to skip rolling when in_trade is True. The roll happens later when '-- Command:' arrives after trade completes. Added 5 regression tests for the exact failing sequence. --- __pycache__/monop_players.cpython-310.pyc | Bin 12891 -> 12899 bytes monop_players.py | 5 +- test_players.py | 76 ++++++++++++++++++++++ 3 files changed, 79 insertions(+), 2 deletions(-) diff --git a/__pycache__/monop_players.cpython-310.pyc b/__pycache__/monop_players.cpython-310.pyc index 2a8f06a25a59ce18baba2962dd7162522dd8953c..c403b2eb79996297c663e256087e6a8f092f1353 100644 GIT binary patch delta 1224 zcmZ8gT}TvB6rM9Px$d98JG(ox3#+ZGS^i+QVrtTlFC`+i2@!-57fFq>tMm|S8)&6w zB;3j*i5&<;sYvD}!mJg;uP)aK>PIp24_^KYk z_&ph&=sY#+Hu9m)c#~fS_W2>vSf8BSM!a#*#eOj^(m(8~5`f=qQVEwj*V$u0?L^+tBK~d> zV?hhm*mot=Sz=WR=zw^X=cvSinOX`P61r53PxFewbVZC@i;Kw&V$&fR(1m^}E+yT1 zik+3Ryw(7E8gcOd)2zoEfh+8u_YmA?pS_{Pqd4GvVP18bPo|ptD7qw|{tK(q=lJI% z>ZXlFxTd)?et{KZmP;+&GSN-ZzH14$wcV516J)giy5ID6bFaakMyG zb-FFQ4tqGW%VM52nP`z2-NdZn%GpE*g_ADsdMnM`4Lpu^deeG;$e!1H$hw4hal4~D+|js#h( zrnp_s5FKEQ7thFW+&H)^10ZA^F4IN0ZTzTk0?Zh{DvtpS8b_)eVC&4RaDe>x9}S9W AnE(I) delta 1096 zcmY*YT}V_x6uxKf&D?bt%l)}~cQ!F`iim^|daz!4C@6Zev*UEt`!aXV`M&R*@660sxfyMG-2;0V1YWa zO8>UCOg=z;Ca{k(_kj=i4EW3Uhl7Ou;SYSV>f*Ij4pKbCKxm9tj>SdiRIl}xh zj(>bgH^9Z`^p3uS@QcGL%oS0)M3veNRZ>`mutw3FL-tlDVy$vlFLvFwMi*9eAg7BT z59qBY?f3~9#}%D4Y^c#F6h>|2Rf){-XMy;Y2|IpUFl*MCIJ><#JHlpc%(TGl+k|Nr zm?wpP#VNJer$pFWn>jC-(RC(mE%cRQ--UUf4P24!EeV1>ER4ExWN?LNjY!G=ai}}^kH_9I`LBb<#Bb2I?FQmg#)|oL z=uk~m?#gl1X_RU-ag*5ZtOxlTtCyG*4~JuL7GKkj()5rw?==jO?b) zG&Y7jS56xo;|t-4`z~JpN_q@EXzZT#GfY)TTSudv1QxA_J111Ur7czc#7V6CRgX33 ovD$YR0hC)u5+;FL)~{paKIb)>FkvCupnx05xJno&W#< diff --git a/monop_players.py b/monop_players.py index a400ad8..825b488 100644 --- a/monop_players.py +++ b/monop_players.py @@ -250,8 +250,9 @@ class PlayerBot: self.jail_turns = 2 else: self.jail_turns = 1 - # Try rolling doubles - self.say_delayed("roll") + # Try rolling doubles — but not if we're in a trade + if not self.in_trade: + self.say_delayed("roll") return if msg == "Double roll gets you out.": diff --git a/test_players.py b/test_players.py index 112f21e..63fde51 100644 --- a/test_players.py +++ b/test_players.py @@ -306,6 +306,82 @@ class TestTrading(unittest.TestCase): self.assertIn("roll", msgs) +class TestTradeInJail(unittest.TestCase): + """Regression tests for trade-while-in-jail bug. + + Sequence that caused the hang: + 1. Checkpoint "charlie (3) (cash $985) on JAIL" → trade initiated + 2. "(This is your 2nd turn in JAIL)" → roll queued (ignoring in_trade) + 3. ".trade" sent, then ".roll" sent into trade prompt → hang + """ + + def _make_bot(self, nick="charlie"): + bot = FakePlayerBot(nick, ["alice", "bob", "charlie"], 2) + bot.setup_phase = False + bot.game_started = True + bot.turns_played = 10 # past trade threshold + return bot + + def test_jail_handler_no_roll_during_trade(self): + """Jail turn handler must not queue a roll when in_trade is True.""" + bot = self._make_bot() + bot.current_player = "charlie" + bot.in_trade = True + bot.in_jail = True + msgs = bot.feed("(This is your 2nd turn in JAIL)") + self.assertNotIn("roll", msgs, + "Should not roll while in_trade is True") + + def test_trade_then_jail_turn_sequence(self): + """Simulate the exact sequence: checkpoint initiates trade, then jail prompt arrives.""" + bot = self._make_bot() + # Force trade to trigger (patch random) + with patch("random.random", return_value=0.01): # < 0.10 → triggers trade + msgs = bot.feed("charlie (3) (cash $985) on JAIL") + self.assertTrue(bot.in_trade, "Trade should be initiated") + self.assertIn("trade", msgs, "Should send .trade") + self.assertNotIn("roll", msgs, "Should not also roll") + + # Now jail turn prompt arrives + msgs = bot.feed("(This is your 2nd turn in JAIL)") + self.assertNotIn("roll", msgs, + "Jail handler must not roll during active trade") + self.assertTrue(bot.in_trade, "Trade should still be active") + + def test_trade_which_player_prompt(self): + """Bot should pick a trade partner when asked.""" + bot = self._make_bot() + bot.current_player = "charlie" + bot.in_trade = True + msgs = bot.feed("Which player do you wish to trade with?") + self.assertTrue(len(msgs) > 0, "Should respond to trade partner prompt") + self.assertNotIn("roll", msgs) + # Should pick one of the other players + self.assertTrue( + msgs[0] in ["alice", "bob"], + f"Expected a player name, got: {msgs[0]}" + ) + + def test_no_trade_in_jail_still_rolls(self): + """When NOT in a trade, jail handler should still queue a roll.""" + bot = self._make_bot() + bot.current_player = "charlie" + bot.in_trade = False + bot.in_jail = True + msgs = bot.feed("(This is your 2nd turn in JAIL)") + self.assertIn("roll", msgs, "Should roll when not trading") + + def test_command_prompt_after_trade_allows_roll(self): + """After trade ends (-- Command:), bot should roll normally.""" + bot = self._make_bot() + bot.current_player = "charlie" + bot.in_trade = True + bot.rolled_this_turn = False + msgs = bot.feed("-- Command:") + self.assertFalse(bot.in_trade) + self.assertIn("roll", msgs) + + class TestValidInputs(unittest.TestCase): def test_picks_first_option(self): bot = FakePlayerBot("alice", ["alice", "bob"], 0)