Player command tracking:
- .card: detect GOJF card usage in jail (previously invisible)
- .mortgage/.mor: set property_mortgaged flag using cost+name disambiguation
- .unmortgage/.unm: clear property_mortgaged flag using cost+name disambiguation
- Command context (_command_context) disambiguates 'That cost you $X' between
jail pay and unmortgage
Holdings display parsing:
- Parse 'NAME's (N) holdings' header, then printsq-format property lines
- Full resync of property_owner, property_mortgaged, property_houses
- Reuses resolve_trade_property() for name matching
- Holdings end on any non-property line (checkpoint, command prompt, etc.)
13 new tests in test_parser_commands.py:
- GOJF card (4): exit jail, no card, not in jail, log entry
- Unmortgage (2): unique property, disambiguation with user input
- Mortgage (1): flag set correctly
- Holdings (5): ownership, mortgage, houses, stale clearing, real log replay
- Real log (1): unmortgage at line 36-41
All 1551 checkpoints + 80 unit tests passing.
When 'with N houses, rent is X' or 'with a hotel, rent is X' is seen,
update property_houses for the current player's location. This partially
fixes stale house data in the UI — houses are synced when rent is charged,
though not at the moment of purchase.
Parser: detect name registration from user input during setup
(sender == name constraint prevents false matches on gameplay messages).
Bridge: extract _process_buffer() to handle leftover lines after
_wait_for_players returns, preventing messages from being stuck.
Player bots: proactively send name on join (not just first player
sending count) so they respond even if they missed the 'say me' prompt.
All tests pass (1551/1553 parser checkpoints, 38 player unit tests).
Bridge changes:
- Wait for at least one user to JOIN before starting monop
- Ensures observer is in channel to see all setup messages
Parser changes:
- Handle 'Player N, say me' even without prior 'How many players?'
- Infer num_players_expected from highest player number seen
- Emit state during setup phase
run_game.py changes:
- 3s stagger between bot joins so setup is visible in web UI
- Observer connects before bots to catch all registration messages
Parser changes:
- Track num_players_expected from user input after 'How many players?'
- Create placeholder players ('Player N') on 'say me please' prompts
- Replace placeholder names when real names appear in roll lines
- Emit state during setup phase (was returning None)
- Include phase and numPlayersExpected in state JSON
UI changes:
- Empty slots shown as dashed/dimmed panels with '?' token
- Placeholder players shown as 'Registering...'
- Registered players shown with checkmark and ,500
- Status bar shows 'Setting up · 2/3 players registered'
The C code picks a starting player via dice roll but doesn't reorder
the player array. The parser now tracks _first_player_idx and rotates
the players list in get_state() so the UI shows them in actual turn
order.
Parser now accumulates log entries for key game events:
- Turn starts (checkpoint lines)
- Rolls, movement, passing GO
- Rent payments
- Card draws (with card text)
- Auctions, trades, resignations
- Jail (triple doubles, GO TO JAIL)
- Game start and winner
Log is capped at 100 entries in GameState, last 30 emitted in
get_state() to match what index.html expects.
Also synced plugin's monop_parser.py copy.