diff --git a/__pycache__/monop_parser.cpython-310.pyc b/__pycache__/monop_parser.cpython-310.pyc index 38d2321..22960d4 100644 Binary files a/__pycache__/monop_parser.cpython-310.pyc and b/__pycache__/monop_parser.cpython-310.pyc differ diff --git a/monop_parser.py b/monop_parser.py index 7fab73e..cabbfde 100644 --- a/monop_parser.py +++ b/monop_parser.py @@ -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:], } diff --git a/plugins/monop/__pycache__/monop_parser.cpython-310.pyc b/plugins/monop/__pycache__/monop_parser.cpython-310.pyc index aa515f0..a59290a 100644 Binary files a/plugins/monop/__pycache__/monop_parser.cpython-310.pyc and b/plugins/monop/__pycache__/monop_parser.cpython-310.pyc differ diff --git a/plugins/monop/monop_parser.py b/plugins/monop/monop_parser.py index 7fab73e..cabbfde 100644 --- a/plugins/monop/monop_parser.py +++ b/plugins/monop/monop_parser.py @@ -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:], }