Add game log to parser output for web viewer
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.
This commit is contained in:
parent
cceec64a7c
commit
d2bd66ba78
4 changed files with 74 additions and 4 deletions
Binary file not shown.
|
|
@ -97,6 +97,8 @@ class GameState:
|
|||
self.property_owner = {} # square_id -> player_number (1-based)
|
||||
self.property_mortgaged = {} # square_id -> bool
|
||||
self.property_houses = {} # square_id -> int (5 = hotel)
|
||||
# Game log for the web viewer
|
||||
self.log = [] # list of {"timestamp": str, "text": str, "player": str|None}
|
||||
self.last_roll = (0, 0)
|
||||
self.last_roll_total = 0
|
||||
self.pending_buy_cost = None # cost of property being offered
|
||||
|
|
@ -111,6 +113,15 @@ class GameState:
|
|||
# Track current player's location before card movement
|
||||
self.pending_rent_owner = None # name of rent owner
|
||||
|
||||
def add_log(self, text, player=None, timestamp=None):
|
||||
"""Append an entry to the game log (kept to last 100 entries)."""
|
||||
entry = {"text": text, "player": player}
|
||||
if timestamp:
|
||||
entry["timestamp"] = timestamp
|
||||
self.log.append(entry)
|
||||
if len(self.log) > 100:
|
||||
self.log = self.log[-100:]
|
||||
|
||||
def get_player(self, name=None, number=None):
|
||||
"""Find player by name or number (1-based)."""
|
||||
for p in self.players:
|
||||
|
|
@ -422,6 +433,7 @@ class MonopParser:
|
|||
break
|
||||
g.phase = "playing"
|
||||
g.game_active = True
|
||||
g.add_log(f"Game started! {name} goes first", timestamp=timestamp)
|
||||
return
|
||||
|
||||
# "Player N, say 'me' please" - just note it
|
||||
|
|
@ -454,6 +466,8 @@ class MonopParser:
|
|||
)
|
||||
return
|
||||
|
||||
g.add_log(f"{name}'s turn — ${money} on {sq_name}", player=name, timestamp=timestamp)
|
||||
|
||||
player = g.get_player(name=name, number=num)
|
||||
if player is None:
|
||||
# New player we haven't seen (mid-game join)
|
||||
|
|
@ -509,6 +523,7 @@ class MonopParser:
|
|||
d1, d2 = int(m.group(1)), int(m.group(2))
|
||||
g.last_roll = (d1, d2)
|
||||
g.last_roll_total = d1 + d2
|
||||
g.add_log(f"roll is {d1}, {d2}", player=cp.name if cp else None, timestamp=timestamp)
|
||||
return
|
||||
|
||||
# ===== MOVEMENT =====
|
||||
|
|
@ -523,12 +538,16 @@ class MonopParser:
|
|||
cp.location = 40 # JAIL
|
||||
cp.in_jail = True
|
||||
cp.jail_turns = 0
|
||||
g.add_log(f"Landed on GO TO JAIL!", player=cp.name, timestamp=timestamp)
|
||||
else:
|
||||
g.add_log(f"Landed on {sq_name}", player=cp.name, timestamp=timestamp)
|
||||
return
|
||||
|
||||
# ===== PASS GO =====
|
||||
if self.PASS_GO_RE.match(msg):
|
||||
if cp:
|
||||
cp.money += 200
|
||||
g.add_log("Passed GO — collected $200", player=cp.name, timestamp=timestamp)
|
||||
return
|
||||
|
||||
# ===== SAFE PLACE =====
|
||||
|
|
@ -602,6 +621,7 @@ class MonopParser:
|
|||
cp.in_jail = True
|
||||
cp.jail_turns = 0
|
||||
cp.doubles_count = 0
|
||||
g.add_log("3 doubles — go to jail!", player=cp.name, timestamp=timestamp)
|
||||
return
|
||||
|
||||
# ===== GO TO JAIL (landing on square) =====
|
||||
|
|
@ -774,6 +794,8 @@ class MonopParser:
|
|||
sq_id = cp.location
|
||||
if 0 <= sq_id < 40:
|
||||
g.property_owner[sq_id] = num
|
||||
sq_name = g.location_name(sq_id)
|
||||
g.add_log(f"Won auction for {sq_name} at ${price}", player=name, timestamp=timestamp)
|
||||
return
|
||||
|
||||
if self.NOBODY_RE.match(msg):
|
||||
|
|
@ -834,11 +856,14 @@ class MonopParser:
|
|||
if self.RESIGN_TO_BANK_RE.match(msg):
|
||||
# Player resigns to bank - remove them
|
||||
if cp:
|
||||
g.add_log(f"{cp.name} resigned to the bank", player=cp.name, timestamp=timestamp)
|
||||
self._remove_player(cp)
|
||||
return
|
||||
|
||||
m = self.WINS_RE.match(msg)
|
||||
if m:
|
||||
winner = m.group(1)
|
||||
g.add_log(f"{winner} WINS!", player=winner, timestamp=timestamp)
|
||||
g.phase = "over"
|
||||
g.game_active = False
|
||||
return
|
||||
|
|
@ -1006,10 +1031,14 @@ class MonopParser:
|
|||
return
|
||||
cp.money -= amount
|
||||
# Pay to owner
|
||||
if g.pending_rent_owner:
|
||||
owner = g.get_player(name=g.pending_rent_owner)
|
||||
owner_name = g.pending_rent_owner
|
||||
if owner_name:
|
||||
owner = g.get_player(name=owner_name)
|
||||
if owner:
|
||||
owner.money += amount
|
||||
g.add_log(f"Paid ${amount} rent to {owner_name}", player=cp.name)
|
||||
else:
|
||||
g.add_log(f"Paid ${amount} rent", player=cp.name)
|
||||
g.pending_rent_owner = None
|
||||
|
||||
def _process_card(self, lines):
|
||||
|
|
@ -1022,6 +1051,9 @@ class MonopParser:
|
|||
return
|
||||
|
||||
text = "\n".join(lines)
|
||||
# Log the card draw (use first non-empty line as summary)
|
||||
card_summary = next((l.strip() for l in lines if l.strip()), "Drew a card")
|
||||
g.add_log(card_summary, player=cp.name)
|
||||
|
||||
# GET OUT OF JAIL FREE
|
||||
if "GET OUT OF JAIL FREE" in text:
|
||||
|
|
@ -1124,6 +1156,7 @@ class MonopParser:
|
|||
p2.get_out_of_jail_free_cards += self._trade_gojf1
|
||||
p2.get_out_of_jail_free_cards -= self._trade_gojf2
|
||||
p1.get_out_of_jail_free_cards += self._trade_gojf2
|
||||
g.add_log(f"Trade completed between {p1.name} and {p2.name}")
|
||||
self._trade_state = None
|
||||
self._trade_player1 = None
|
||||
self._trade_player2 = None
|
||||
|
|
@ -1146,6 +1179,7 @@ class MonopParser:
|
|||
target.money += cp.money
|
||||
if target:
|
||||
target.get_out_of_jail_free_cards += cp.get_out_of_jail_free_cards
|
||||
g.add_log(f"{cp.name} resigned to {target.name if target else 'bank'}", player=cp.name)
|
||||
self._remove_player(cp)
|
||||
|
||||
def _remove_player(self, player):
|
||||
|
|
@ -1193,6 +1227,7 @@ class MonopParser:
|
|||
"players": [p.to_dict() for p in g.players],
|
||||
"currentPlayer": g.current_player.number if g.current_player else None,
|
||||
"squares": squares,
|
||||
"log": g.log[-30:],
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -97,6 +97,8 @@ class GameState:
|
|||
self.property_owner = {} # square_id -> player_number (1-based)
|
||||
self.property_mortgaged = {} # square_id -> bool
|
||||
self.property_houses = {} # square_id -> int (5 = hotel)
|
||||
# Game log for the web viewer
|
||||
self.log = [] # list of {"timestamp": str, "text": str, "player": str|None}
|
||||
self.last_roll = (0, 0)
|
||||
self.last_roll_total = 0
|
||||
self.pending_buy_cost = None # cost of property being offered
|
||||
|
|
@ -111,6 +113,15 @@ class GameState:
|
|||
# Track current player's location before card movement
|
||||
self.pending_rent_owner = None # name of rent owner
|
||||
|
||||
def add_log(self, text, player=None, timestamp=None):
|
||||
"""Append an entry to the game log (kept to last 100 entries)."""
|
||||
entry = {"text": text, "player": player}
|
||||
if timestamp:
|
||||
entry["timestamp"] = timestamp
|
||||
self.log.append(entry)
|
||||
if len(self.log) > 100:
|
||||
self.log = self.log[-100:]
|
||||
|
||||
def get_player(self, name=None, number=None):
|
||||
"""Find player by name or number (1-based)."""
|
||||
for p in self.players:
|
||||
|
|
@ -422,6 +433,7 @@ class MonopParser:
|
|||
break
|
||||
g.phase = "playing"
|
||||
g.game_active = True
|
||||
g.add_log(f"Game started! {name} goes first", timestamp=timestamp)
|
||||
return
|
||||
|
||||
# "Player N, say 'me' please" - just note it
|
||||
|
|
@ -454,6 +466,8 @@ class MonopParser:
|
|||
)
|
||||
return
|
||||
|
||||
g.add_log(f"{name}'s turn — ${money} on {sq_name}", player=name, timestamp=timestamp)
|
||||
|
||||
player = g.get_player(name=name, number=num)
|
||||
if player is None:
|
||||
# New player we haven't seen (mid-game join)
|
||||
|
|
@ -509,6 +523,7 @@ class MonopParser:
|
|||
d1, d2 = int(m.group(1)), int(m.group(2))
|
||||
g.last_roll = (d1, d2)
|
||||
g.last_roll_total = d1 + d2
|
||||
g.add_log(f"roll is {d1}, {d2}", player=cp.name if cp else None, timestamp=timestamp)
|
||||
return
|
||||
|
||||
# ===== MOVEMENT =====
|
||||
|
|
@ -523,12 +538,16 @@ class MonopParser:
|
|||
cp.location = 40 # JAIL
|
||||
cp.in_jail = True
|
||||
cp.jail_turns = 0
|
||||
g.add_log(f"Landed on GO TO JAIL!", player=cp.name, timestamp=timestamp)
|
||||
else:
|
||||
g.add_log(f"Landed on {sq_name}", player=cp.name, timestamp=timestamp)
|
||||
return
|
||||
|
||||
# ===== PASS GO =====
|
||||
if self.PASS_GO_RE.match(msg):
|
||||
if cp:
|
||||
cp.money += 200
|
||||
g.add_log("Passed GO — collected $200", player=cp.name, timestamp=timestamp)
|
||||
return
|
||||
|
||||
# ===== SAFE PLACE =====
|
||||
|
|
@ -602,6 +621,7 @@ class MonopParser:
|
|||
cp.in_jail = True
|
||||
cp.jail_turns = 0
|
||||
cp.doubles_count = 0
|
||||
g.add_log("3 doubles — go to jail!", player=cp.name, timestamp=timestamp)
|
||||
return
|
||||
|
||||
# ===== GO TO JAIL (landing on square) =====
|
||||
|
|
@ -774,6 +794,8 @@ class MonopParser:
|
|||
sq_id = cp.location
|
||||
if 0 <= sq_id < 40:
|
||||
g.property_owner[sq_id] = num
|
||||
sq_name = g.location_name(sq_id)
|
||||
g.add_log(f"Won auction for {sq_name} at ${price}", player=name, timestamp=timestamp)
|
||||
return
|
||||
|
||||
if self.NOBODY_RE.match(msg):
|
||||
|
|
@ -834,11 +856,14 @@ class MonopParser:
|
|||
if self.RESIGN_TO_BANK_RE.match(msg):
|
||||
# Player resigns to bank - remove them
|
||||
if cp:
|
||||
g.add_log(f"{cp.name} resigned to the bank", player=cp.name, timestamp=timestamp)
|
||||
self._remove_player(cp)
|
||||
return
|
||||
|
||||
m = self.WINS_RE.match(msg)
|
||||
if m:
|
||||
winner = m.group(1)
|
||||
g.add_log(f"{winner} WINS!", player=winner, timestamp=timestamp)
|
||||
g.phase = "over"
|
||||
g.game_active = False
|
||||
return
|
||||
|
|
@ -1006,10 +1031,14 @@ class MonopParser:
|
|||
return
|
||||
cp.money -= amount
|
||||
# Pay to owner
|
||||
if g.pending_rent_owner:
|
||||
owner = g.get_player(name=g.pending_rent_owner)
|
||||
owner_name = g.pending_rent_owner
|
||||
if owner_name:
|
||||
owner = g.get_player(name=owner_name)
|
||||
if owner:
|
||||
owner.money += amount
|
||||
g.add_log(f"Paid ${amount} rent to {owner_name}", player=cp.name)
|
||||
else:
|
||||
g.add_log(f"Paid ${amount} rent", player=cp.name)
|
||||
g.pending_rent_owner = None
|
||||
|
||||
def _process_card(self, lines):
|
||||
|
|
@ -1022,6 +1051,9 @@ class MonopParser:
|
|||
return
|
||||
|
||||
text = "\n".join(lines)
|
||||
# Log the card draw (use first non-empty line as summary)
|
||||
card_summary = next((l.strip() for l in lines if l.strip()), "Drew a card")
|
||||
g.add_log(card_summary, player=cp.name)
|
||||
|
||||
# GET OUT OF JAIL FREE
|
||||
if "GET OUT OF JAIL FREE" in text:
|
||||
|
|
@ -1124,6 +1156,7 @@ class MonopParser:
|
|||
p2.get_out_of_jail_free_cards += self._trade_gojf1
|
||||
p2.get_out_of_jail_free_cards -= self._trade_gojf2
|
||||
p1.get_out_of_jail_free_cards += self._trade_gojf2
|
||||
g.add_log(f"Trade completed between {p1.name} and {p2.name}")
|
||||
self._trade_state = None
|
||||
self._trade_player1 = None
|
||||
self._trade_player2 = None
|
||||
|
|
@ -1146,6 +1179,7 @@ class MonopParser:
|
|||
target.money += cp.money
|
||||
if target:
|
||||
target.get_out_of_jail_free_cards += cp.get_out_of_jail_free_cards
|
||||
g.add_log(f"{cp.name} resigned to {target.name if target else 'bank'}", player=cp.name)
|
||||
self._remove_player(cp)
|
||||
|
||||
def _remove_player(self, player):
|
||||
|
|
@ -1193,6 +1227,7 @@ class MonopParser:
|
|||
"players": [p.to_dict() for p in g.players],
|
||||
"currentPlayer": g.current_player.number if g.current_player else None,
|
||||
"squares": squares,
|
||||
"log": g.log[-30:],
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue