Fix setup registration: names appear as each player joins
Parser: detect name registration from user input during setup (sender == name constraint prevents false matches on gameplay messages). Bridge: extract _process_buffer() to handle leftover lines after _wait_for_players returns, preventing messages from being stuck. Player bots: proactively send name on join (not just first player sending count) so they respond even if they missed the 'say me' prompt. All tests pass (1551/1553 parser checkpoints, 38 player unit tests).
This commit is contained in:
parent
6d055c68a2
commit
9c47bac33a
7 changed files with 367 additions and 59 deletions
Binary file not shown.
Binary file not shown.
|
|
@ -108,34 +108,38 @@ class IRCBridge:
|
|||
print(f"[bridge] {m.group(1)} joined — starting game")
|
||||
return
|
||||
|
||||
def _process_buffer(self):
|
||||
"""Process any complete lines in the buffer."""
|
||||
while "\r\n" in self.buffer:
|
||||
line, self.buffer = self.buffer.split("\r\n", 1)
|
||||
if line.startswith("PING"):
|
||||
self.irc_send("PONG" + line[4:])
|
||||
continue
|
||||
m = re.match(r":(\S+?)!(\S+?) PRIVMSG (\S+) :(.+)", line)
|
||||
if m:
|
||||
sender_nick = m.group(1)
|
||||
target = m.group(3)
|
||||
msg = m.group(4)
|
||||
if target == CHANNEL and sender_nick != NICK:
|
||||
if msg.startswith(PREFIX):
|
||||
cmd = msg[len(PREFIX):]
|
||||
self._feed_monop(sender_nick, cmd)
|
||||
|
||||
def run(self):
|
||||
self.connect_irc()
|
||||
self._wait_for_players()
|
||||
self.start_monop()
|
||||
|
||||
# Process any lines buffered during _wait_for_players
|
||||
self._process_buffer()
|
||||
|
||||
while True:
|
||||
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)
|
||||
if line.startswith("PING"):
|
||||
self.irc_send("PONG" + line[4:])
|
||||
continue
|
||||
# Parse PRIVMSG
|
||||
m = re.match(
|
||||
r":(\S+?)!(\S+?) PRIVMSG (\S+) :(.+)", line
|
||||
)
|
||||
if m:
|
||||
sender_nick = m.group(1)
|
||||
target = m.group(3)
|
||||
msg = m.group(4)
|
||||
if target == CHANNEL and sender_nick != NICK:
|
||||
if msg.startswith(PREFIX):
|
||||
cmd = msg[len(PREFIX):]
|
||||
self._feed_monop(sender_nick, cmd)
|
||||
self._process_buffer()
|
||||
except Exception as e:
|
||||
print(f"[bridge] error: {e}")
|
||||
break
|
||||
|
|
|
|||
|
|
@ -257,6 +257,17 @@ class MonopParser:
|
|||
g.add_log(f"Game for {count} players", timestamp=timestamp)
|
||||
return
|
||||
|
||||
# Name registration — user sends their IRC nick as their name.
|
||||
# In monop-irc, the bridge sends "nick name" to monop stdin,
|
||||
# so the player name matches the IRC sender.
|
||||
name = msg.strip()
|
||||
if name and not name.isdigit() and name == sender:
|
||||
for p in g.players:
|
||||
if p.name.startswith("Player "):
|
||||
p.name = name
|
||||
g.add_log(f"{name} joined!", player=name, timestamp=timestamp)
|
||||
return
|
||||
|
||||
def parse_line(self, line):
|
||||
"""Parse a single IRC log line. Returns any events generated."""
|
||||
self.line_num += 1
|
||||
|
|
|
|||
|
|
@ -117,6 +117,11 @@ class PlayerBot:
|
|||
time.sleep(1.0)
|
||||
self.log("Sending player count (in case we missed the prompt)")
|
||||
self.say(str(self.num_players))
|
||||
else:
|
||||
# Send our name proactively in case we missed the "say me" prompt
|
||||
time.sleep(1.0)
|
||||
self.log(f"Sending name (in case we missed the prompt)")
|
||||
self.say(self.nick)
|
||||
|
||||
def _send(self, line):
|
||||
with self.lock:
|
||||
|
|
|
|||
|
|
@ -257,6 +257,17 @@ class MonopParser:
|
|||
g.add_log(f"Game for {count} players", timestamp=timestamp)
|
||||
return
|
||||
|
||||
# Name registration — user sends their IRC nick as their name.
|
||||
# In monop-irc, the bridge sends "nick name" to monop stdin,
|
||||
# so the player name matches the IRC sender.
|
||||
name = msg.strip()
|
||||
if name and not name.isdigit() and name == sender:
|
||||
for p in g.players:
|
||||
if p.name.startswith("Player "):
|
||||
p.name = name
|
||||
g.add_log(f"{name} joined!", player=name, timestamp=timestamp)
|
||||
return
|
||||
|
||||
def parse_line(self, line):
|
||||
"""Parse a single IRC log line. Returns any events generated."""
|
||||
self.line_num += 1
|
||||
|
|
|
|||
|
|
@ -1,27 +1,37 @@
|
|||
{
|
||||
"players": [
|
||||
{
|
||||
"name": "Player 1",
|
||||
"name": "alice",
|
||||
"number": 1,
|
||||
"money": 1500,
|
||||
"location": 0,
|
||||
"money": 1190,
|
||||
"location": 40,
|
||||
"inJail": true,
|
||||
"jailTurns": 1,
|
||||
"doublesCount": 0,
|
||||
"getOutOfJailFreeCards": 0
|
||||
},
|
||||
{
|
||||
"name": "bob",
|
||||
"number": 2,
|
||||
"money": 1300,
|
||||
"location": 11,
|
||||
"inJail": false,
|
||||
"jailTurns": 0,
|
||||
"doublesCount": 0,
|
||||
"getOutOfJailFreeCards": 0
|
||||
},
|
||||
{
|
||||
"name": "Player 2",
|
||||
"number": 2,
|
||||
"money": 1500,
|
||||
"location": 0,
|
||||
"name": "charlie",
|
||||
"number": 3,
|
||||
"money": 1400,
|
||||
"location": 8,
|
||||
"inJail": false,
|
||||
"jailTurns": 0,
|
||||
"doublesCount": 0,
|
||||
"getOutOfJailFreeCards": 0
|
||||
}
|
||||
],
|
||||
"currentPlayer": null,
|
||||
"currentPlayer": 3,
|
||||
"squares": [
|
||||
{
|
||||
"id": 0,
|
||||
|
|
@ -31,7 +41,12 @@
|
|||
{
|
||||
"id": 1,
|
||||
"name": "Mediterranean ave. (P)",
|
||||
"type": "property"
|
||||
"type": "property",
|
||||
"owner": null,
|
||||
"mortgaged": false,
|
||||
"group": "purple",
|
||||
"cost": 60,
|
||||
"houses": 0
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
|
|
@ -41,7 +56,12 @@
|
|||
{
|
||||
"id": 3,
|
||||
"name": "Baltic ave. (P)",
|
||||
"type": "property"
|
||||
"type": "property",
|
||||
"owner": 2,
|
||||
"mortgaged": false,
|
||||
"group": "purple",
|
||||
"cost": 60,
|
||||
"houses": 0
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
|
|
@ -51,12 +71,21 @@
|
|||
{
|
||||
"id": 5,
|
||||
"name": "Reading RR",
|
||||
"type": "railroad"
|
||||
"type": "railroad",
|
||||
"owner": null,
|
||||
"mortgaged": false,
|
||||
"group": "railroad",
|
||||
"cost": 200
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"name": "Oriental ave. (L)",
|
||||
"type": "property"
|
||||
"type": "property",
|
||||
"owner": null,
|
||||
"mortgaged": false,
|
||||
"group": "lightblue",
|
||||
"cost": 100,
|
||||
"houses": 0
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
|
|
@ -66,12 +95,22 @@
|
|||
{
|
||||
"id": 8,
|
||||
"name": "Vermont ave. (L)",
|
||||
"type": "property"
|
||||
"type": "property",
|
||||
"owner": 3,
|
||||
"mortgaged": false,
|
||||
"group": "lightblue",
|
||||
"cost": 100,
|
||||
"houses": 0
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"name": "Connecticut ave. (L)",
|
||||
"type": "property"
|
||||
"type": "property",
|
||||
"owner": null,
|
||||
"mortgaged": false,
|
||||
"group": "lightblue",
|
||||
"cost": 120,
|
||||
"houses": 0
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
|
|
@ -81,32 +120,60 @@
|
|||
{
|
||||
"id": 11,
|
||||
"name": "St. Charles pl. (V)",
|
||||
"type": "property"
|
||||
"type": "property",
|
||||
"owner": 2,
|
||||
"mortgaged": false,
|
||||
"group": "violet",
|
||||
"cost": 140,
|
||||
"houses": 0
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"name": "Electric Co.",
|
||||
"type": "utility"
|
||||
"type": "utility",
|
||||
"owner": null,
|
||||
"mortgaged": false,
|
||||
"group": "utility",
|
||||
"cost": 150
|
||||
},
|
||||
{
|
||||
"id": 13,
|
||||
"name": "States ave. (V)",
|
||||
"type": "property"
|
||||
"type": "property",
|
||||
"owner": null,
|
||||
"mortgaged": false,
|
||||
"group": "violet",
|
||||
"cost": 140,
|
||||
"houses": 0
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"name": "Virginia ave. (V)",
|
||||
"type": "property"
|
||||
"type": "property",
|
||||
"owner": 1,
|
||||
"mortgaged": false,
|
||||
"group": "violet",
|
||||
"cost": 160,
|
||||
"houses": 0
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
"name": "Pennsylvania RR",
|
||||
"type": "railroad"
|
||||
"type": "railroad",
|
||||
"owner": null,
|
||||
"mortgaged": false,
|
||||
"group": "railroad",
|
||||
"cost": 200
|
||||
},
|
||||
{
|
||||
"id": 16,
|
||||
"name": "St. James pl. (O)",
|
||||
"type": "property"
|
||||
"type": "property",
|
||||
"owner": null,
|
||||
"mortgaged": false,
|
||||
"group": "orange",
|
||||
"cost": 180,
|
||||
"houses": 0
|
||||
},
|
||||
{
|
||||
"id": 17,
|
||||
|
|
@ -116,12 +183,22 @@
|
|||
{
|
||||
"id": 18,
|
||||
"name": "Tennessee ave. (O)",
|
||||
"type": "property"
|
||||
"type": "property",
|
||||
"owner": null,
|
||||
"mortgaged": false,
|
||||
"group": "orange",
|
||||
"cost": 180,
|
||||
"houses": 0
|
||||
},
|
||||
{
|
||||
"id": 19,
|
||||
"name": "New York ave. (O)",
|
||||
"type": "property"
|
||||
"type": "property",
|
||||
"owner": null,
|
||||
"mortgaged": false,
|
||||
"group": "orange",
|
||||
"cost": 200,
|
||||
"houses": 0
|
||||
},
|
||||
{
|
||||
"id": 20,
|
||||
|
|
@ -131,7 +208,12 @@
|
|||
{
|
||||
"id": 21,
|
||||
"name": "Kentucky ave. (R)",
|
||||
"type": "property"
|
||||
"type": "property",
|
||||
"owner": null,
|
||||
"mortgaged": false,
|
||||
"group": "red",
|
||||
"cost": 220,
|
||||
"houses": 0
|
||||
},
|
||||
{
|
||||
"id": 22,
|
||||
|
|
@ -141,37 +223,70 @@
|
|||
{
|
||||
"id": 23,
|
||||
"name": "Indiana ave. (R)",
|
||||
"type": "property"
|
||||
"type": "property",
|
||||
"owner": null,
|
||||
"mortgaged": false,
|
||||
"group": "red",
|
||||
"cost": 220,
|
||||
"houses": 0
|
||||
},
|
||||
{
|
||||
"id": 24,
|
||||
"name": "Illinois ave. (R)",
|
||||
"type": "property"
|
||||
"type": "property",
|
||||
"owner": null,
|
||||
"mortgaged": false,
|
||||
"group": "red",
|
||||
"cost": 240,
|
||||
"houses": 0
|
||||
},
|
||||
{
|
||||
"id": 25,
|
||||
"name": "B&O RR",
|
||||
"type": "railroad"
|
||||
"type": "railroad",
|
||||
"owner": null,
|
||||
"mortgaged": false,
|
||||
"group": "railroad",
|
||||
"cost": 200
|
||||
},
|
||||
{
|
||||
"id": 26,
|
||||
"name": "Atlantic ave. (Y)",
|
||||
"type": "property"
|
||||
"type": "property",
|
||||
"owner": null,
|
||||
"mortgaged": false,
|
||||
"group": "yellow",
|
||||
"cost": 260,
|
||||
"houses": 0
|
||||
},
|
||||
{
|
||||
"id": 27,
|
||||
"name": "Ventnor ave. (Y)",
|
||||
"type": "property"
|
||||
"type": "property",
|
||||
"owner": null,
|
||||
"mortgaged": false,
|
||||
"group": "yellow",
|
||||
"cost": 260,
|
||||
"houses": 0
|
||||
},
|
||||
{
|
||||
"id": 28,
|
||||
"name": "Water Works",
|
||||
"type": "utility"
|
||||
"type": "utility",
|
||||
"owner": null,
|
||||
"mortgaged": false,
|
||||
"group": "utility",
|
||||
"cost": 150
|
||||
},
|
||||
{
|
||||
"id": 29,
|
||||
"name": "Marvin Gardens (Y)",
|
||||
"type": "property"
|
||||
"type": "property",
|
||||
"owner": null,
|
||||
"mortgaged": false,
|
||||
"group": "yellow",
|
||||
"cost": 280,
|
||||
"houses": 0
|
||||
},
|
||||
{
|
||||
"id": 30,
|
||||
|
|
@ -181,12 +296,22 @@
|
|||
{
|
||||
"id": 31,
|
||||
"name": "Pacific ave. (G)",
|
||||
"type": "property"
|
||||
"type": "property",
|
||||
"owner": null,
|
||||
"mortgaged": false,
|
||||
"group": "green",
|
||||
"cost": 300,
|
||||
"houses": 0
|
||||
},
|
||||
{
|
||||
"id": 32,
|
||||
"name": "N. Carolina ave. (G)",
|
||||
"type": "property"
|
||||
"type": "property",
|
||||
"owner": null,
|
||||
"mortgaged": false,
|
||||
"group": "green",
|
||||
"cost": 300,
|
||||
"houses": 0
|
||||
},
|
||||
{
|
||||
"id": 33,
|
||||
|
|
@ -196,12 +321,21 @@
|
|||
{
|
||||
"id": 34,
|
||||
"name": "Pennsylvania ave. (G)",
|
||||
"type": "property"
|
||||
"type": "property",
|
||||
"owner": null,
|
||||
"mortgaged": false,
|
||||
"group": "green",
|
||||
"cost": 320,
|
||||
"houses": 0
|
||||
},
|
||||
{
|
||||
"id": 35,
|
||||
"name": "Short Line RR",
|
||||
"type": "railroad"
|
||||
"type": "railroad",
|
||||
"owner": null,
|
||||
"mortgaged": false,
|
||||
"group": "railroad",
|
||||
"cost": 200
|
||||
},
|
||||
{
|
||||
"id": 36,
|
||||
|
|
@ -211,7 +345,12 @@
|
|||
{
|
||||
"id": 37,
|
||||
"name": "Park place (D)",
|
||||
"type": "property"
|
||||
"type": "property",
|
||||
"owner": null,
|
||||
"mortgaged": false,
|
||||
"group": "darkblue",
|
||||
"cost": 350,
|
||||
"houses": 0
|
||||
},
|
||||
{
|
||||
"id": 38,
|
||||
|
|
@ -221,27 +360,165 @@
|
|||
{
|
||||
"id": 39,
|
||||
"name": "Boardwalk (D)",
|
||||
"type": "property"
|
||||
"type": "property",
|
||||
"owner": null,
|
||||
"mortgaged": false,
|
||||
"group": "darkblue",
|
||||
"cost": 400,
|
||||
"houses": 0
|
||||
}
|
||||
],
|
||||
"log": [
|
||||
{
|
||||
"text": "Game for 3 players",
|
||||
"player": null,
|
||||
"timestamp": "2026-02-21 11:33:22"
|
||||
"timestamp": "2026-02-21 11:53:09"
|
||||
},
|
||||
{
|
||||
"text": "Waiting for Player 1 to register...",
|
||||
"player": null,
|
||||
"timestamp": "2026-02-21 11:33:22"
|
||||
"timestamp": "2026-02-21 11:53:09"
|
||||
},
|
||||
{
|
||||
"text": "alice joined!",
|
||||
"player": "alice",
|
||||
"timestamp": "2026-02-21 11:53:10"
|
||||
},
|
||||
{
|
||||
"text": "Waiting for Player 2 to register...",
|
||||
"player": null,
|
||||
"timestamp": "2026-02-21 11:33:23"
|
||||
"timestamp": "2026-02-21 11:53:10"
|
||||
},
|
||||
{
|
||||
"text": "bob joined!",
|
||||
"player": "bob",
|
||||
"timestamp": "2026-02-21 11:53:12"
|
||||
},
|
||||
{
|
||||
"text": "Waiting for Player 3 to register...",
|
||||
"player": null,
|
||||
"timestamp": "2026-02-21 11:53:12"
|
||||
},
|
||||
{
|
||||
"text": "charlie joined!",
|
||||
"player": "charlie",
|
||||
"timestamp": "2026-02-21 11:53:15"
|
||||
},
|
||||
{
|
||||
"text": "Game started! alice goes first",
|
||||
"player": null,
|
||||
"timestamp": "2026-02-21 11:53:15"
|
||||
},
|
||||
{
|
||||
"text": "alice's turn \u2014 $1500 on === GO ===",
|
||||
"player": "alice",
|
||||
"timestamp": "2026-02-21 11:53:15"
|
||||
},
|
||||
{
|
||||
"text": "roll is 2, 2",
|
||||
"player": "alice",
|
||||
"timestamp": "2026-02-21 11:53:16"
|
||||
},
|
||||
{
|
||||
"text": "Landed on Income Tax",
|
||||
"player": "alice",
|
||||
"timestamp": "2026-02-21 11:53:17"
|
||||
},
|
||||
{
|
||||
"text": "alice's turn \u2014 $1350 on Income Tax",
|
||||
"player": "alice",
|
||||
"timestamp": "2026-02-21 11:53:19"
|
||||
},
|
||||
{
|
||||
"text": "roll is 5, 5",
|
||||
"player": "alice",
|
||||
"timestamp": "2026-02-21 11:53:20"
|
||||
},
|
||||
{
|
||||
"text": "Landed on Virginia ave. (V)",
|
||||
"player": "alice",
|
||||
"timestamp": "2026-02-21 11:53:21"
|
||||
},
|
||||
{
|
||||
"text": "alice's turn \u2014 $1190 on Virginia ave. (V)",
|
||||
"player": "alice",
|
||||
"timestamp": "2026-02-21 11:53:23"
|
||||
},
|
||||
{
|
||||
"text": "roll is 3, 3",
|
||||
"player": "alice",
|
||||
"timestamp": "2026-02-21 11:53:24"
|
||||
},
|
||||
{
|
||||
"text": "3 doubles \u2014 go to jail!",
|
||||
"player": "alice",
|
||||
"timestamp": "2026-02-21 11:53:25"
|
||||
},
|
||||
{
|
||||
"text": "bob's turn \u2014 $1500 on === GO ===",
|
||||
"player": "bob",
|
||||
"timestamp": "2026-02-21 11:53:25"
|
||||
},
|
||||
{
|
||||
"text": "roll is 1, 2",
|
||||
"player": "bob",
|
||||
"timestamp": "2026-02-21 11:53:27"
|
||||
},
|
||||
{
|
||||
"text": "Landed on Baltic ave. (P)",
|
||||
"player": "bob",
|
||||
"timestamp": "2026-02-21 11:53:27"
|
||||
},
|
||||
{
|
||||
"text": "charlie's turn \u2014 $1500 on === GO ===",
|
||||
"player": "charlie",
|
||||
"timestamp": "2026-02-21 11:53:29"
|
||||
},
|
||||
{
|
||||
"text": "roll is 2, 6",
|
||||
"player": "charlie",
|
||||
"timestamp": "2026-02-21 11:53:31"
|
||||
},
|
||||
{
|
||||
"text": "Landed on Vermont ave. (L)",
|
||||
"player": "charlie",
|
||||
"timestamp": "2026-02-21 11:53:31"
|
||||
},
|
||||
{
|
||||
"text": "alice's turn \u2014 $1190 on JAIL",
|
||||
"player": "alice",
|
||||
"timestamp": "2026-02-21 11:53:33"
|
||||
},
|
||||
{
|
||||
"text": "roll is 2, 6",
|
||||
"player": "alice",
|
||||
"timestamp": "2026-02-21 11:53:34"
|
||||
},
|
||||
{
|
||||
"text": "bob's turn \u2014 $1440 on Baltic ave. (P)",
|
||||
"player": "bob",
|
||||
"timestamp": "2026-02-21 11:53:35"
|
||||
},
|
||||
{
|
||||
"text": "roll is 3, 5",
|
||||
"player": "bob",
|
||||
"timestamp": "2026-02-21 11:53:37"
|
||||
},
|
||||
{
|
||||
"text": "Landed on St. Charles pl. (V)",
|
||||
"player": "bob",
|
||||
"timestamp": "2026-02-21 11:53:37"
|
||||
},
|
||||
{
|
||||
"text": "charlie's turn \u2014 $1400 on Vermont ave. (L)",
|
||||
"player": "charlie",
|
||||
"timestamp": "2026-02-21 11:53:39"
|
||||
},
|
||||
{
|
||||
"text": "roll is 3, 2",
|
||||
"player": "charlie",
|
||||
"timestamp": "2026-02-21 11:53:40"
|
||||
}
|
||||
],
|
||||
"phase": "setup",
|
||||
"numPlayersExpected": 3,
|
||||
"lastUpdated": "2026-02-21T11:33:23.055895+00:00"
|
||||
"lastUpdated": "2026-02-21T11:53:40.970003+00:00"
|
||||
}
|
||||
Loading…
Reference in a new issue