Spaces:
Sleeping
Sleeping
| import re | |
| COMMAND_VERBS = { | |
| "go", "head", "walk", "run", "visit", "check", "rest", "help", | |
| "tell", "make", "ask", "send", "move", "bring", "look", | |
| } | |
| BASE_LOCATION_ALIASES = { | |
| "clinic": "nia_clinic", | |
| "hospital": "nia_clinic", | |
| "nurse": "nia_clinic", | |
| "cafe": "cafe", | |
| "café": "cafe", | |
| "coffee": "cafe", | |
| "school": "school", | |
| "class": "school", | |
| "park": "park", | |
| "fountain": "fountain", | |
| "square": "square", | |
| "plaza": "square", | |
| "office": "priya_office", | |
| "city hall": "priya_office", | |
| "shop": "shop", | |
| "store": "shop", | |
| } | |
| def classify(text, world): | |
| raw = (text or "").strip() | |
| if not raw: | |
| return {"type": "noop", "text": raw, "addressees": [], "instruction": "", "goto": None} | |
| if not re.search(r"[A-Za-z0-9]", raw): | |
| return {"type": "ambient", "text": raw, "addressees": [], "instruction": raw, "goto": None} | |
| cast = world.get("cast", []) | |
| names = _matched_names(raw, cast) | |
| lowered = raw.lower() | |
| looks_command = bool(names) and ( | |
| _starts_with_name_command(lowered, names) | |
| or lowered.startswith("tell ") | |
| or lowered.startswith("make ") | |
| or lowered.startswith("ask ") | |
| or any(re.search(rf"\b{verb}\b", lowered) for verb in COMMAND_VERBS) | |
| ) | |
| if looks_command: | |
| instruction = _strip_command_prefix(raw, names) | |
| goto = resolve_location(instruction, world, cast, names) | |
| return { | |
| "type": "directed_command", | |
| "text": raw, | |
| "addressees": names, | |
| "instruction": instruction, | |
| "goto": goto, | |
| } | |
| return {"type": "world_event", "text": raw, "addressees": [], "instruction": raw, "goto": None} | |
| def resolve_location(text, world, cast=None, addressees=None): | |
| lowered = (text or "").lower() | |
| hotspots = ((world.get("board", {}) or {}).get("hotspots_tile") or {}) | |
| if "home" in lowered and cast and addressees and len(addressees) == 1: | |
| char = next((c for c in cast if c["name"] == addressees[0]), None) | |
| home = char.get("home") if char else None | |
| if home in hotspots: | |
| return home | |
| aliases = dict(BASE_LOCATION_ALIASES) | |
| if "nurse_office" in hotspots: | |
| aliases["clinic"] = "nurse_office" | |
| aliases["nurse"] = "nurse_office" | |
| aliases["nurse office"] = "nurse_office" | |
| if "quad" in hotspots: | |
| aliases["quad"] = "quad" | |
| aliases["campus"] = "quad" | |
| if "riverside_walk" in hotspots: | |
| aliases["river"] = "riverside_walk" | |
| aliases["riverside"] = "riverside_walk" | |
| for key in hotspots: | |
| aliases[key] = key | |
| aliases[key.replace("_", " ")] = key | |
| aliases[key.replace("_", "")] = key | |
| for phrase, hotspot in sorted(aliases.items(), key=lambda item: len(item[0]), reverse=True): | |
| if phrase in lowered and hotspot in hotspots: | |
| return hotspot | |
| return None | |
| def _matched_names(text, cast): | |
| lowered = text.lower() | |
| matches = [] | |
| for char in cast: | |
| first = char["name"].split()[0].lower() | |
| if re.search(rf"\b{re.escape(first)}\b", lowered): | |
| matches.append(char["name"]) | |
| return matches | |
| def _starts_with_name_command(lowered, names): | |
| first_names = [name.split()[0].lower() for name in names] | |
| for first in first_names: | |
| if re.match(rf"^\s*{re.escape(first)}\s*[,:\-]\s*", lowered): | |
| return True | |
| return False | |
| def _strip_command_prefix(text, names): | |
| instruction = text.strip() | |
| first_names = "|".join(re.escape(name.split()[0]) for name in names) | |
| patterns = [ | |
| rf"^\s*(?:tell|ask|make|send)\s+(?:{first_names})\s+(?:to\s+)?", | |
| rf"^\s*(?:{first_names})\s*[,:\-]\s*", | |
| ] | |
| for pattern in patterns: | |
| instruction = re.sub(pattern, "", instruction, flags=re.IGNORECASE).strip() | |
| return instruction or text.strip() | |