#!/usr/bin/env python3 """Run a simulated game: player bots + parser writing game-state.json to site/.""" import sys import os import json import socket import threading import time sys.path.insert(0, os.path.dirname(__file__)) from monop_parser import MonopParser from monop_players import PlayerBot STATE_PATH = os.path.join(os.path.dirname(__file__), "site", "game-state.json") HOST = "127.0.0.1" PORT = 6667 CHANNEL = "#monop" BOT_NICK = "monop" class ParserClient: """IRC client that watches the game and writes game-state.json.""" def __init__(self): self.parser = MonopParser() self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.buffer = "" self.running = True def connect(self): self.sock.connect((HOST, PORT)) self.sock.sendall(b"NICK observer\r\nUSER observer 0 * :observer\r\n") time.sleep(1) self.sock.sendall(f"JOIN {CHANNEL}\r\n".encode()) def run(self): self.connect() while self.running: try: data = self.sock.recv(4096) if not data: break self.buffer += data.decode("utf-8", errors="replace") while "\r\n" in self.buffer: line, self.buffer = self.buffer.split("\r\n", 1) self._handle(line) except Exception as e: print(f"[parser] Error: {e}") break def _handle(self, line): if line.startswith("PING"): self.sock.sendall(f"PONG {line[5:]}\r\n".encode()) return # Parse PRIVMSG from bot if "PRIVMSG" in line and CHANNEL in line: # :nick!user@host PRIVMSG #chan :message parts = line.split(f"PRIVMSG {CHANNEL} :", 1) if len(parts) == 2: prefix = parts[0].strip() sender = prefix.split("!")[0].lstrip(":") message = parts[1] ts = time.strftime("%Y-%m-%d %H:%M:%S") log_line = f"{ts}\t{sender}\t{message}" self.parser.parse_line(log_line) state = self.parser.get_state() if state: from datetime import datetime, timezone state["lastUpdated"] = datetime.now(timezone.utc).isoformat() try: with open(STATE_PATH, "w") as f: json.dump(state, f, indent=2) except Exception as e: print(f"[parser] Write error: {e}") def stop(self): self.running = False try: self.sock.close() except: pass def main(): player_names = ["alice", "bob", "charlie"] # Start parser client pc = ParserClient() parser_thread = threading.Thread(target=pc.run, daemon=True) parser_thread.start() print(f"[game] Parser writing to {STATE_PATH}") time.sleep(2) # Start player bots bots = [] for i, name in enumerate(player_names): bot = PlayerBot(name, CHANNEL, HOST, PORT, player_names, i) t = threading.Thread(target=bot.run, daemon=True) t.start() bots.append(bot) time.sleep(0.5) print(f"[game] {len(bots)} player bots started") # Run until killed try: while True: time.sleep(1) except KeyboardInterrupt: print("[game] Shutting down...") pc.stop() if __name__ == "__main__": main()