This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from __future__ import annotations
import asyncio
import random
import re
from pathlib import Path
from typing import Dict, List
from snekbot.bot import Bot
from ai import AI
WORD_LIST_PATH = Path(__file__).with_suffix(".wordlist")
if WORD_LIST_PATH.exists():
with WORD_LIST_PATH.open() as _f:
DICTIONARY: List[str] = [w.strip().lower() for w in _f if len(w.strip()) == 5]
else:
DICTIONARY: List[str] = []
def squares(secret: str, guess: str) -> str:
"""Return 🟩🟨⬜ pattern for *guess* vs *secret*."""
res = [""] * 5
s_left = list(secret)
g_left = list(guess)
for i, (g, s) in enumerate(zip(g_left, s_left)):
if g == s:
res[i] = "🟩"
s_left[i] = None
g_left[i] = None
for i, g in enumerate(g_left):
if g and g in s_left:
res[i] = "🟨"
s_left[s_left.index(g)] = None
return "".join(res)
def _try_ai(system_prompt: str, **kwargs):
try:
kwargs["use_cache"] = False
return AI(timeout=4).prompt(system_prompt, **kwargs)
except Exception:
return None
def determine_action(msg: str) -> Dict[str, str]:
sys = (
"You are WordleBot's intent detector.\n"
"Reply ONLY with JSON: {'action':'new_game'} | {'action':'guess','guess':'xxxxx'} | {'action':'unknown'}.\n"
"If the user clearly explictely wants a new game choose new_game, else extract the first 5letter word as guess. Chose unknown for anything else.\n"
f"Message: {msg}"
)
out = _try_ai(sys, json=True)
if isinstance(out, dict) and out.get("action") != "unknown":
print(msg, out)
return out
if re.search(r"\b(new game|reset|restart|start)\b", msg.lower()):
return {"action": "new_game"}
m = re.search(r"\b[a-z]{5}\b", msg.lower())
return {"action": "guess", "guess": m.group(0) if m else ""}
def say(ctx: str) -> str:
sys = (
"You are WordleBot, an upbeat, gameshowstyle host! Use emojis and exclamation marks, max 140 chars.\n"
"Respond ONLY with JSON: {'text':'...'}\n"
f"Context: {ctx}"
)
out = _try_ai(sys, json=True)
if isinstance(out, dict) and "text" in out:
return out["text"]
return ctx
def imagine_word() -> str:
with open("wordle.wordlist", "r") as f:
words = [word.strip() for word in f.read().split("\n") if word.strip()]
return random.choice(words)
class WordleBot(Bot):
MAX_TRIES = 6
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.games: Dict[str, Dict] = {}
def _start_game(self, chan: str):
self.games[chan] = {"word": imagine_word(), "guesses": {}, "lost": set()}
def _add_guess(self, chan: str, user: str, word: str):
self.games[chan]["guesses"].setdefault(user, []).append(word)
def _log_state(self, chan: str):
g = self.games.get(chan)
if not g:
print(f"[CHAN {chan}] no active game")
return
parts = (
[f"word={g['word']}"]
+ [f"{u}:{v}" for u, v in g["guesses"].items()]
+ [f"lost:{g['lost']}"]
)
print(f"[CHAN {chan}] " + " | ".join(parts))
async def on_mention(
self, username: str, user_nick: str | None, channel_uid: str, message: str
):
intent = determine_action(message)
if intent["action"] == "new_game":
self._start_game(channel_uid)
self._log_state(channel_uid)
return await self.send_message(
channel_uid,
say(
"🎉 New round! A fresh 5letter mystery awaits fire your first shot!"
),
)
guess = intent.get("guess", "").lower()
if len(guess) != 5 or not guess.isalpha():
self._log_state(channel_uid)
return await self.send_message(
channel_uid, say("⛔ Oops! I need a real 5letter word try again!")
)
if channel_uid not in self.games:
self._start_game(channel_uid)
game = self.games[channel_uid]
self._add_guess(channel_uid, username, guess)
patt = squares(game["word"], guess)
used = len(game["guesses"][username])
left = self.MAX_TRIES - used
if guess == game["word"]:
msg = say(
f"🏆 {guess.upper()} {patt} spotlight on {user_nick or username}! Fancy another round?"
)
del self.games[channel_uid]
self._log_state(channel_uid)
return await self.send_message(channel_uid, msg)
if left == 0:
game["lost"].add(username)
everyone = set(game["guesses"].keys())
if game["lost"] == everyone and everyone:
ans = game["word"].upper()
msg = say(
f"🛑 All challengers down! The word was {ans}. Shall we spin up a new puzzle?"
)
del self.games[channel_uid]
self._log_state(channel_uid)
return await self.send_message(channel_uid, msg)
bust_msg = say(
f"💀 {guess.upper()} {patt}. You're out of turns cheer on the rest!"
)
self._log_state(channel_uid)
return await self.send_message(channel_uid, bust_msg)
feedback = f"{patt} ({left} left)"
self._log_state(channel_uid)
return await self.send_message(channel_uid, feedback)
if __name__ == "__main__":
bot = WordleBot(username="wordlebot", password="thetopsecretpassword")
asyncio.run(bot.run())