#!/usr/bin/env python3 """Prototype Bloom (Center Circle) generator — prints real sample wheels so we can feel the quality before building any UI. The validated logic here becomes goodnews/bloom.py.""" import hashlib import json import random from pathlib import Path _DATA = Path(__file__).resolve().parents[1] / "goodnews" / "data" d = json.loads((_DATA / "bloom_words.json").read_text()) ACCEPT = d["accept"] COMMON = set(d["common"]) ACCEPT_LS = [(w, frozenset(w)) for w in ACCEPT] # Off-brand words we never CELEBRATE as the day's pangram (accept-list unaffected). AVOID = set(json.loads((_DATA / "bloom_avoid.json").read_text())) # Candidate wheels = letter-sets of COMMON 7-distinct-letter words (so the day's # pangram is always a recognizable word). No 'S' already guaranteed by the list. PANGRAM_SETS: dict[frozenset, list[str]] = {} for w in COMMON: s = frozenset(w) if len(s) == 7: PANGRAM_SETS.setdefault(s, []).append(w) MIN_WORDS, MAX_WORDS, MIN_COMMON, TOP_TIER = 24, 60, 14, 0.70 def score(w: str) -> int: return 1 if len(w) == 4 else len(w) def build(letters: frozenset, center: str): words = [w for w, s in ACCEPT_LS if center in w and s <= letters] pangrams = [w for w in words if frozenset(w) == letters] commons = [w for w in words if w in COMMON] max_score = sum(score(w) for w in words) + 7 * len(pangrams) common_score = sum(score(w) for w in commons) + 7 * len([w for w in pangrams if w in COMMON]) return words, pangrams, commons, max_score, common_score def valid(letters, center): words, pangrams, commons, max_score, common_score = build(letters, center) if not (MIN_WORDS <= len(words) <= MAX_WORDS): return None # The DISPLAY pangram must be calm + recognizable: common, not on the avoid # list. (Off-brand pangrams like LUCIFER/VOMITING are still accepted if typed, # just never the day's celebrated word.) display = [p for p in pangrams if p in COMMON and p not in AVOID] if not display or len(commons) < MIN_COMMON: return None if common_score < TOP_TIER * max_score: # top tier reachable from common vocab return None return words, sorted(display, key=len), commons, max_score, common_score def generate(date: str): rng = random.Random(int(hashlib.sha256(f"bloom:{date}".encode()).hexdigest(), 16)) sets = list(PANGRAM_SETS) rng.shuffle(sets) for s in sets: centers = sorted(s) rng.shuffle(centers) for c in centers: res = valid(s, c) if res: return s, c, res return None print(f"loaded accept={len(ACCEPT)} common={len(COMMON)} | candidate wheels={len(PANGRAM_SETS)}\n") for date in ("2026-06-15", "2026-06-16", "2026-06-17", "2026-06-18", "2026-06-19"): s, c, (words, pangrams, commons, max_score, common_score) = generate(date) outer = sorted(s - {c}) tiers = {"Sprouting": 0, "Budding": int(0.08 * max_score), "Blooming": int(0.30 * max_score), "Flourishing": int(0.70 * max_score)} longest = sorted(words, key=len, reverse=True)[:3] sample = sorted(random.Random(1).sample(words, min(16, len(words)))) print(f"── {date} ── center [{c.upper()}] outer {[x.upper() for x in outer]}") print(f" words={len(words)} (common={len(commons)}) pangram(s)={[p.upper() for p in pangrams]}") print(f" max_score={max_score} tiers={tiers}") print(f" longest={longest} sample={sample}\n")