Skip to content

Fix(env): a response window shows nothing until it closes - #110

Open
Haor wants to merge 1 commit into
nissymori:mainfrom
Haor:fix/private-response-window
Open

Haor wants to merge 1 commit into
nissymori:mainfrom
Haor:fix/private-response-window

Conversation

@Haor

@Haor Haor commented Sep 24, 2026

Copy link
Copy Markdown
Contributor

Two things let a player learn, from what the env offered them, what the other players could do or had done with the same discard.

1. A double ron was settled one winner at a time

In red_mahjong the first RON was paid, took the riichi sticks and went into action_history before the next player who could ron the same tile had answered. That player decided knowing whether the first had won, and for how much, ura dora included. On a 三家和 the third player saw two wins booked and could void both with a RON. This is the known limitation in the v0.1.6 release notes.

_ron now keeps each RON in pending_rewards and pending_winners, and _settle_ron pays them all, clears the sticks and records the RONs, in the order they were asked, on the step that closes the window: the last candidate's RON or PASS. A 三家和 has paid nothing, so there is nothing to roll back, and pending_kyotaku is removed. A window closed by a PASS keeps target and leaves the turn with the last winner, as one closed by a RON does, so the closed round does not show that someone declined.

2. The offered calls depended on the other players' hands

In both envs, _claims_open_to offered a player who could ron and pon (or chi) a discard the pon only when nobody else could ron (or pon) it, so the prompt told them about another player's hand. In a double ron it also told the second candidate what the first did: after a RON only the RON column was kept, after a PASS the whole row.

A player who can ron now always answers RON or PASS first; a declined ron leaves the pon or chi, offered once the higher claims are answered, as #87 already did when claims competed. A RON also leaves the rows in players.legal_action_mask exactly as a PASS does, so a candidate's own stored row does not show it either.

Behavior changes

  • A player who can both ron and call a discard answers RON/PASS first, then the call.
  • RON enters action_history, and ron payments enter score and rewards, only on the step that closes the window.
  • In red_mahjong's EnvState, pending_kyotaku is removed and pending_winners is added. no_red_mahjong has no multiple ron and no such fields.
  • A state serialized before this change does not load as is, and a caller that booked each RON's payment on its own step now gets the whole window's payment on the step that closes it.

Tests

  • New tests/red_mahjong/test_ron_window.py, starting from real discards and an added kan:
    • a later candidate's observation, offered mask and stored rows are identical after every way the earlier candidates can answer (double ron, triple ron, robbing kan);
    • every RON/PASS combination pays exactly once, when the window closes;
    • three RONs abandon the round without paying anything;
    • the next round follows the closed window under auto and dummy_share;
    • a ron on a riichi declaration leaves the riichi unmade, and a declaration everyone passes stands at the next draw;
    • a declined ron still leaves the pon.
  • no_red: a ron candidate is offered the same calls whoever else can claim the tile.
  • Updated because they pinned the old behavior: the lower-call tests of both envs, and red's _claims_open_to, double-ron and triple-ron tests.
  • 22 of the 24 new tests fail on main; the 2 that pass guard unchanged riichi behavior. Full suite: 334 passed, 2 skipped.

Tested on CPU with Python 3.12 and JAX 0.11.1, using the CI command (visualization tests excluded).

A double ron in red_mahjong was settled one winner at a time. The first
RON was paid, took the riichi sticks and went into the action history
before the next player who could ron the same tile had answered, so that
player decided knowing whether the first had won, and for how much. On a
三家和 the third player saw two wins booked and could void both with a
RON. _ron now keeps each RON in pending_rewards and pending_winners, and
_settle_ron pays them all, clears the sticks and records the RONs, in
the order they were asked, on the step that closes the window: the last
candidate's RON or PASS. A 三家和 has paid nothing, so there is nothing
to roll back, and pending_kyotaku is removed. A window closed by a PASS
now keeps target and leaves the turn with the last winner, as one closed
by a RON did, so it no longer shows that someone declined.

In both envs a player who could ron and pon (or chi) a discard was
offered the pon only when nobody else could ron (or pon) it, so the
prompt told them about another player's hand. In a double ron it also
told the second candidate what the first did: after a RON only the RON
column is kept, after a PASS the whole row. A player who can ron now
always answers RON or PASS first; a declined ron leaves the pon or chi,
offered once the higher claims are answered. A RON also leaves the rows
in players.legal_action_mask exactly as a PASS does, so a candidate's
own stored row does not show it either.

New tests in tests/red_mahjong/test_ron_window.py and no_red's test_env
start from real discards and an added kan, and compare a later
candidate's observation, offered mask and stored rows after each way the
earlier ones can answer. The lower-call tests of both
envs and red's _claims_open_to, double-ron and triple-ron tests pinned
the old behavior and are updated.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant