mirror of
https://github.com/kennethreitz/rhymepad.org.git
synced 2026-06-11 17:08:33 +00:00
Mosaics: first-vowel anchoring and honest empties
Anchoring at the first vowel instead of the stressed rime lets tonight rebuild as "a night"; single-letter left words return with a rank penalty; indexes warm at boot; the no-results message stops blaming syllable count. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -32,6 +32,7 @@ async def lifespan(app: FastAPI):
|
||||
except Exception:
|
||||
pass
|
||||
get_slant_index()
|
||||
get_mosaic_indexes()
|
||||
yield
|
||||
|
||||
|
||||
@@ -1133,7 +1134,12 @@ def mosaics_for(w: str, limit: int) -> list[dict]:
|
||||
phones = phones_for(w)
|
||||
if not phones:
|
||||
return []
|
||||
rime = DIGITS.sub("", pronouncing.rhyming_part(phones)).split()
|
||||
# anchor at the FIRST vowel: tonight (AH N AY T) splits into a + night
|
||||
pl = DIGITS.sub("", phones).split()
|
||||
vi = next((i for i, p in enumerate(pl) if p in ARPA_VOWELS), None)
|
||||
if vi is None:
|
||||
return []
|
||||
rime = pl[vi:]
|
||||
pidx, ridx = get_mosaic_indexes()
|
||||
pairs: dict[str, float] = {}
|
||||
for i in range(1, len(rime)):
|
||||
@@ -1147,15 +1153,14 @@ def mosaics_for(w: str, limit: int) -> list[dict]:
|
||||
# (placement -> place + meant, the EH collapsing to a schwa)
|
||||
tails = {right: 2.0, _squeeze(right): 0.0}
|
||||
for a in ridx.get(left, []):
|
||||
if len(a) < 2:
|
||||
continue
|
||||
for tail_key, bonus in tails.items():
|
||||
for b in pidx.get(tail_key, []):
|
||||
if a == w or b == w:
|
||||
continue
|
||||
phrase = f"{a} {b}"
|
||||
score = (zipf_frequency(a, "en")
|
||||
+ zipf_frequency(b, "en") + bonus)
|
||||
+ zipf_frequency(b, "en") + bonus
|
||||
- (1.5 if len(a) < 2 else 0))
|
||||
if score > pairs.get(phrase, 0):
|
||||
pairs[phrase] = score
|
||||
ranked = sorted(pairs.items(), key=lambda kv: (-kv[1], kv[0]))
|
||||
|
||||
+1
-1
@@ -655,7 +655,7 @@ async function doLookup(){
|
||||
const data = await r.json();
|
||||
if(mode === 'mosaic'){
|
||||
if(!data.known){
|
||||
resultsBox.innerHTML = `<p class="muted">No two-word mosaics for “${esc(word)}” — try a word with two or more syllables.</p>`;
|
||||
resultsBox.innerHTML = `<p class="muted">No clean two-word split for “${esc(word)}” — mosaics need a sound the dictionary can rebuild from two words (try placement, creation, tonight).</p>`;
|
||||
return;
|
||||
}
|
||||
renderMosaics(word, data.words);
|
||||
|
||||
@@ -525,6 +525,8 @@ def test_mosaic_generator():
|
||||
assert "place meant" in words
|
||||
creation = {m["word"] for m in mosaics_for("creation", 20)}
|
||||
assert "way shun" in creation
|
||||
tonight = {m["word"] for m in mosaics_for("tonight", 20)}
|
||||
assert "a night" in tonight
|
||||
|
||||
|
||||
def test_word_info():
|
||||
|
||||
Reference in New Issue
Block a user