New file: house_parser.py — self-contained state machine for the interactive
house buy/sell dialog. Parses:
- Property prompts ('PropName (N): ') to capture current house count
- Player responses (how many to buy/sell)
- Auto-skipped prompts (hotel during buy, 0 houses during sell)
- Error retries ('spread too wide', 'too many')
- Confirmation ('Is that ok?' → y/n)
Returns a result dict with {action, changes: {sq_id: new_count}, cost}
when the dialog completes.
Integration: monop_parser feeds every line (bot + player) to HouseParser.
On result, applies house count changes to property_houses.
Tests:
- test_house_parser.py (18 tests): buy/sell basics, hotel, error retry,
real log replay (lines 59, 354, 4386)
- test_parser_commands.py: 4 new integration tests (buy, sell, reject,
real log verification)
This closes the last tracking gap — house counts are now accurate at
purchase time, not just when rent is later charged.
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.