diff --git a/README.md b/README.md new file mode 100644 index 0000000..0962622 --- /dev/null +++ b/README.md @@ -0,0 +1,40 @@ +# monop-board + +A Monopoly board viewer for IRC games played with [monop-irc](https://github.com/thlc/monop-irc). + +## Components + +### IRC Bot (`bot/`) +Python bot that joins an IRC channel, watches for monop-irc game output, parses it, and writes the game state to `game-state.json`. + +### Board Viewer (`site/`) +Single-file HTML/CSS/JS static site that reads `game-state.json` and renders a visual Monopoly board with player positions, property ownership, houses/hotels, and a game log. Auto-refreshes every 2 seconds. + +## Quick Start + +```bash +# 1. Configure the bot +cp bot/config.json bot/config.local.json +# Edit config with your IRC server/channel details + +# 2. Install bot dependencies +pip install -r bot/requirements.txt + +# 3. Start the bot +cd bot && python3 monopbot.py + +# 4. Serve the board viewer +cd site && python3 -m http.server 8080 +# Open http://localhost:8080 +``` + +## How It Works + +1. monop-irc runs in an IRC channel, outputting game text +2. The bot parses messages (rolls, movement, purchases, rent, jail, etc.) +3. Game state is written to `game-state.json` after each change +4. The board viewer polls the JSON file and renders the board + +## License + +BSD (matching the original bsdgames monop license) diff --git a/bot/__pycache__/board_data.cpython-310.pyc b/bot/__pycache__/board_data.cpython-310.pyc new file mode 100644 index 0000000..6c77569 Binary files /dev/null and b/bot/__pycache__/board_data.cpython-310.pyc differ diff --git a/bot/__pycache__/parser.cpython-310.pyc b/bot/__pycache__/parser.cpython-310.pyc new file mode 100644 index 0000000..c109a46 Binary files /dev/null and b/bot/__pycache__/parser.cpython-310.pyc differ diff --git a/bot/config.json b/bot/config.json index f216aff..e15a6af 100644 --- a/bot/config.json +++ b/bot/config.json @@ -3,7 +3,7 @@ "port": 6697, "tls": true, "nick": "monopbot", - "channel": "#monop-test", + "channel": "#monop-dev", "game_nick": "monop", "state_file": "../game-state.json" } diff --git a/bot/monopbot.py b/bot/monopbot.py index c04cf06..ff912e3 100644 --- a/bot/monopbot.py +++ b/bot/monopbot.py @@ -107,7 +107,10 @@ class MonopBot: try: connect_params = {} if config.get("tls", False): - ssl_factory = irc.connection.Factory(wrapper=ssl.wrap_socket) + ssl_ctx = ssl.create_default_context() + hostname = config["server"] + ssl_factory = irc.connection.Factory( + wrapper=lambda s: ssl_ctx.wrap_socket(s, server_hostname=hostname)) connect_params["connect_factory"] = ssl_factory server = reactor.server() diff --git a/bot/parser.py b/bot/parser.py index 5d6ae86..1dfc20b 100644 --- a/bot/parser.py +++ b/bot/parser.py @@ -289,8 +289,8 @@ class MonopParser: # Holdings cash line if self._parsing_holdings and self._holdings_player is not None: - m = re.match(r"\s+\$(\d+)", line) - if m: + m = re.match(r"\s+\$?(\d+)", line) + if m and line.strip()[0] in "$0123456789": self.players[self._holdings_player]["money"] = int(m.group(1)) # Check for GOJF cards gojf_m = re.search(r"(\d+) get-out-of-jail-free card", line) diff --git a/game-state.json b/game-state.json new file mode 100644 index 0000000..7211d4b --- /dev/null +++ b/game-state.json @@ -0,0 +1,678 @@ +{ + "players": [], + "currentPlayer": 0, + "squares": [ + { + "id": 0, + "name": "Go", + "type": "safe", + "owner": -1, + "cost": 0, + "mortgaged": false, + "houses": 0, + "monopoly": false, + "group": null, + "rent": [ + 0 + ] + }, + { + "id": 1, + "name": "Mediterranean Ave.", + "type": "property", + "owner": -1, + "cost": 60, + "mortgaged": false, + "houses": 0, + "monopoly": false, + "group": "purple", + "rent": [ + 2, + 10, + 30, + 90, + 160, + 250 + ] + }, + { + "id": 2, + "name": "Community Chest", + "type": "cc", + "owner": -1, + "cost": 0, + "mortgaged": false, + "houses": 0, + "monopoly": false, + "group": null, + "rent": [ + 0 + ] + }, + { + "id": 3, + "name": "Baltic Ave.", + "type": "property", + "owner": -1, + "cost": 60, + "mortgaged": false, + "houses": 0, + "monopoly": false, + "group": "purple", + "rent": [ + 4, + 20, + 60, + 180, + 320, + 450 + ] + }, + { + "id": 4, + "name": "Income Tax", + "type": "tax", + "owner": -1, + "cost": 0, + "mortgaged": false, + "houses": 0, + "monopoly": false, + "group": null, + "rent": [ + 0 + ] + }, + { + "id": 5, + "name": "Reading Railroad", + "type": "railroad", + "owner": -1, + "cost": 200, + "mortgaged": false, + "houses": 0, + "monopoly": false, + "group": "railroad", + "rent": [ + 0 + ] + }, + { + "id": 6, + "name": "Oriental Ave.", + "type": "property", + "owner": -1, + "cost": 100, + "mortgaged": false, + "houses": 0, + "monopoly": false, + "group": "lightblue", + "rent": [ + 6, + 30, + 90, + 270, + 400, + 550 + ] + }, + { + "id": 7, + "name": "Chance", + "type": "chance", + "owner": -1, + "cost": 0, + "mortgaged": false, + "houses": 0, + "monopoly": false, + "group": null, + "rent": [ + 0 + ] + }, + { + "id": 8, + "name": "Vermont Ave.", + "type": "property", + "owner": -1, + "cost": 100, + "mortgaged": false, + "houses": 0, + "monopoly": false, + "group": "lightblue", + "rent": [ + 6, + 30, + 90, + 270, + 400, + 550 + ] + }, + { + "id": 9, + "name": "Connecticut Ave.", + "type": "property", + "owner": -1, + "cost": 120, + "mortgaged": false, + "houses": 0, + "monopoly": false, + "group": "lightblue", + "rent": [ + 8, + 40, + 100, + 300, + 450, + 600 + ] + }, + { + "id": 10, + "name": "Just Visiting", + "type": "jail", + "owner": -1, + "cost": 0, + "mortgaged": false, + "houses": 0, + "monopoly": false, + "group": null, + "rent": [ + 0 + ] + }, + { + "id": 11, + "name": "St. Charles Place", + "type": "property", + "owner": -1, + "cost": 140, + "mortgaged": false, + "houses": 0, + "monopoly": false, + "group": "pink", + "rent": [ + 10, + 50, + 150, + 450, + 625, + 750 + ] + }, + { + "id": 12, + "name": "Electric Company", + "type": "utility", + "owner": -1, + "cost": 150, + "mortgaged": false, + "houses": 0, + "monopoly": false, + "group": "utility", + "rent": [ + 0 + ] + }, + { + "id": 13, + "name": "States Ave.", + "type": "property", + "owner": -1, + "cost": 140, + "mortgaged": false, + "houses": 0, + "monopoly": false, + "group": "pink", + "rent": [ + 10, + 50, + 150, + 450, + 625, + 750 + ] + }, + { + "id": 14, + "name": "Virginia Ave.", + "type": "property", + "owner": -1, + "cost": 160, + "mortgaged": false, + "houses": 0, + "monopoly": false, + "group": "pink", + "rent": [ + 12, + 60, + 180, + 500, + 700, + 900 + ] + }, + { + "id": 15, + "name": "Pennsylvania Railroad", + "type": "railroad", + "owner": -1, + "cost": 200, + "mortgaged": false, + "houses": 0, + "monopoly": false, + "group": "railroad", + "rent": [ + 0 + ] + }, + { + "id": 16, + "name": "St. James Place", + "type": "property", + "owner": -1, + "cost": 180, + "mortgaged": false, + "houses": 0, + "monopoly": false, + "group": "orange", + "rent": [ + 14, + 70, + 200, + 550, + 750, + 950 + ] + }, + { + "id": 17, + "name": "Community Chest", + "type": "cc", + "owner": -1, + "cost": 0, + "mortgaged": false, + "houses": 0, + "monopoly": false, + "group": null, + "rent": [ + 0 + ] + }, + { + "id": 18, + "name": "Tennessee Ave.", + "type": "property", + "owner": -1, + "cost": 180, + "mortgaged": false, + "houses": 0, + "monopoly": false, + "group": "orange", + "rent": [ + 14, + 70, + 200, + 550, + 750, + 950 + ] + }, + { + "id": 19, + "name": "New York Ave.", + "type": "property", + "owner": -1, + "cost": 200, + "mortgaged": false, + "houses": 0, + "monopoly": false, + "group": "orange", + "rent": [ + 16, + 80, + 220, + 600, + 800, + 1000 + ] + }, + { + "id": 20, + "name": "Free Parking", + "type": "safe", + "owner": -1, + "cost": 0, + "mortgaged": false, + "houses": 0, + "monopoly": false, + "group": null, + "rent": [ + 0 + ] + }, + { + "id": 21, + "name": "Kentucky Ave.", + "type": "property", + "owner": -1, + "cost": 220, + "mortgaged": false, + "houses": 0, + "monopoly": false, + "group": "red", + "rent": [ + 18, + 90, + 250, + 700, + 875, + 1050 + ] + }, + { + "id": 22, + "name": "Chance", + "type": "chance", + "owner": -1, + "cost": 0, + "mortgaged": false, + "houses": 0, + "monopoly": false, + "group": null, + "rent": [ + 0 + ] + }, + { + "id": 23, + "name": "Indiana Ave.", + "type": "property", + "owner": -1, + "cost": 220, + "mortgaged": false, + "houses": 0, + "monopoly": false, + "group": "red", + "rent": [ + 18, + 90, + 250, + 700, + 875, + 1050 + ] + }, + { + "id": 24, + "name": "Illinois Ave.", + "type": "property", + "owner": -1, + "cost": 240, + "mortgaged": false, + "houses": 0, + "monopoly": false, + "group": "red", + "rent": [ + 20, + 100, + 300, + 750, + 925, + 1100 + ] + }, + { + "id": 25, + "name": "B&O Railroad", + "type": "railroad", + "owner": -1, + "cost": 200, + "mortgaged": false, + "houses": 0, + "monopoly": false, + "group": "railroad", + "rent": [ + 0 + ] + }, + { + "id": 26, + "name": "Atlantic Ave.", + "type": "property", + "owner": -1, + "cost": 260, + "mortgaged": false, + "houses": 0, + "monopoly": false, + "group": "yellow", + "rent": [ + 22, + 110, + 330, + 800, + 975, + 1150 + ] + }, + { + "id": 27, + "name": "Ventnor Ave.", + "type": "property", + "owner": -1, + "cost": 260, + "mortgaged": false, + "houses": 0, + "monopoly": false, + "group": "yellow", + "rent": [ + 22, + 110, + 330, + 800, + 975, + 1150 + ] + }, + { + "id": 28, + "name": "Water Works", + "type": "utility", + "owner": -1, + "cost": 150, + "mortgaged": false, + "houses": 0, + "monopoly": false, + "group": "utility", + "rent": [ + 0 + ] + }, + { + "id": 29, + "name": "Marvin Gardens", + "type": "property", + "owner": -1, + "cost": 280, + "mortgaged": false, + "houses": 0, + "monopoly": false, + "group": "yellow", + "rent": [ + 24, + 120, + 360, + 850, + 1025, + 1200 + ] + }, + { + "id": 30, + "name": "Go to Jail", + "type": "gotojail", + "owner": -1, + "cost": 0, + "mortgaged": false, + "houses": 0, + "monopoly": false, + "group": null, + "rent": [ + 0 + ] + }, + { + "id": 31, + "name": "Pacific Ave.", + "type": "property", + "owner": -1, + "cost": 300, + "mortgaged": false, + "houses": 0, + "monopoly": false, + "group": "green", + "rent": [ + 26, + 130, + 390, + 900, + 1100, + 1275 + ] + }, + { + "id": 32, + "name": "North Carolina Ave.", + "type": "property", + "owner": -1, + "cost": 300, + "mortgaged": false, + "houses": 0, + "monopoly": false, + "group": "green", + "rent": [ + 26, + 130, + 390, + 900, + 1100, + 1275 + ] + }, + { + "id": 33, + "name": "Community Chest", + "type": "cc", + "owner": -1, + "cost": 0, + "mortgaged": false, + "houses": 0, + "monopoly": false, + "group": null, + "rent": [ + 0 + ] + }, + { + "id": 34, + "name": "Pennsylvania Ave.", + "type": "property", + "owner": -1, + "cost": 320, + "mortgaged": false, + "houses": 0, + "monopoly": false, + "group": "green", + "rent": [ + 28, + 150, + 450, + 1000, + 1200, + 1400 + ] + }, + { + "id": 35, + "name": "Short Line Railroad", + "type": "railroad", + "owner": -1, + "cost": 200, + "mortgaged": false, + "houses": 0, + "monopoly": false, + "group": "railroad", + "rent": [ + 0 + ] + }, + { + "id": 36, + "name": "Chance", + "type": "chance", + "owner": -1, + "cost": 0, + "mortgaged": false, + "houses": 0, + "monopoly": false, + "group": null, + "rent": [ + 0 + ] + }, + { + "id": 37, + "name": "Park Place", + "type": "property", + "owner": -1, + "cost": 350, + "mortgaged": false, + "houses": 0, + "monopoly": false, + "group": "darkblue", + "rent": [ + 35, + 175, + 500, + 1100, + 1300, + 1500 + ] + }, + { + "id": 38, + "name": "Luxury Tax", + "type": "tax", + "owner": -1, + "cost": 0, + "mortgaged": false, + "houses": 0, + "monopoly": false, + "group": null, + "rent": [ + 0 + ] + }, + { + "id": 39, + "name": "Boardwalk", + "type": "property", + "owner": -1, + "cost": 400, + "mortgaged": false, + "houses": 0, + "monopoly": false, + "group": "darkblue", + "rent": [ + 50, + 200, + 600, + 1400, + 1700, + 2000 + ] + } + ], + "log": [], + "lastUpdated": "2026-02-20T21:05:15.895061+00:00" +} \ No newline at end of file