Ultra Crossword Helper

Solve any crossword clue with pattern matching · ? = unknown letter · 32 dictionaries

Show Filters ▼
10 free searches — try AWF Ultra before subscribing 10 of 10 remaining Unlock Unlimited →

Search Results:

Enter a crossword pattern above

Type a pattern using ? for unknown letters (e.g. ?O?S?W?RD) to find every fitting word.

Tip: Use the Pattern field for crossword clues. Letters field narrows results to available tiles.

'); w.document.close(); } // ── Insert character at cursor position (mobile helper buttons) ── function insertAtCursor(fieldId, ch) { const el = document.getElementById(fieldId); const start = el.selectionStart || el.value.length; el.value = el.value.slice(0, start) + ch + el.value.slice(start); el.focus(); el.setSelectionRange(start + 1, start + 1); } // ── Freemium gating (10 free searches per day for non-members) ──── const PRO_TIERS = ['ultra','awf-pro','word-pro','pro','premium','all-access','all-access-ai','empire','lifetime']; const isUltraMember = window.__gwnTier && PRO_TIERS.includes(window.__gwnTier); let freeSearches = parseInt(localStorage.getItem('ultra_free_searches') || '10'); const freeResetDate = localStorage.getItem('ultra_free_reset') || ''; const today = new Date().toISOString().slice(0, 10); if (freeResetDate !== today) { freeSearches = 10; localStorage.setItem('ultra_free_reset', today); } if (isUltraMember) document.getElementById('freemium-bar').classList.add('hidden'); else updateFreemiumCount(); function updateFreemiumCount() { const el = document.getElementById('freemium-count'); if (el) el.textContent = freeSearches + ' of 10 remaining'; if (freeSearches <= 0) { const bar = document.getElementById('freemium-bar'); bar.querySelector('.freemium-text').innerHTML = 'Free searches used up for today'; bar.style.borderColor = 'rgba(231,76,60,.3)'; } } // S49 AC: global click handler for clickable result words → details modal with clues + dictionary (function () { const ovId = 'gwn-word-modal'; function ensureOverlay() { if (document.getElementById(ovId)) return; const ov = document.createElement('div'); ov.id = ovId; ov.style.cssText = 'display:none;position:fixed;inset:0;background:rgba(0,0,0,.78);z-index:9999;align-items:center;justify-content:center;padding:1rem'; ov.innerHTML = '
' + '
' + '
' + '' + '
' + '
' + '
'; document.body.appendChild(ov); ov.addEventListener('click', e => { if (e.target === ov) hideOverlay(); }); document.getElementById('gwm-close').addEventListener('click', hideOverlay); document.addEventListener('keydown', e => { if (e.key === 'Escape') hideOverlay(); }); } function hideOverlay() { const o = document.getElementById(ovId); if (o) o.style.display = 'none'; } function showOverlay() { ensureOverlay(); document.getElementById(ovId).style.display = 'flex'; } function escHtml(s) { return (s || '').replace(/&/g, '&').replace(//g, '>'); } async function loadWordDetails(word) { showOverlay(); document.getElementById('gwm-word').textContent = word.toUpperCase(); document.getElementById('gwm-body').innerHTML = '
Loading clues + definitions\u2026
'; let html = ''; // 1) Backend clues try { const r = await fetch('/cgi-bin/wordfinder.fcgi?SearchType=Crossword&Pattern=' + encodeURIComponent(word) + '&MatchType=Exactly&Dict=american-crossword&MinLetters=' + word.length + '&MaxLetters=' + word.length + '&format=json'); const d = await r.json(); const clues = (d.words && d.words[0] && d.words[0].clues) || []; if (clues.length > 0) { html += '

Crossword Clues (' + clues.length + ')

'; html += ''; } } catch (e) {} // 2) Free Dictionary API (definitions) try { const r2 = await fetch('https://api.dictionaryapi.dev/api/v2/entries/en/' + encodeURIComponent(word.toLowerCase())); if (r2.ok) { const dd = await r2.json(); const entry = dd[0]; if (entry) { html += '

Definition

'; if (entry.phonetic || (entry.phonetics && entry.phonetics[0] && entry.phonetics[0].text)) { html += '
' + escHtml(entry.phonetic || entry.phonetics[0].text) + '
'; } (entry.meanings || []).slice(0, 3).forEach(m => { html += '
' + escHtml(m.partOfSpeech || '') + ''; (m.definitions || []).slice(0, 2).forEach((dd2, i) => { html += '
' + (i + 1) + '. ' + escHtml(dd2.definition || '') + '
'; if (dd2.example) html += '
"' + escHtml(dd2.example) + '"
'; }); html += '
'; }); } } } catch (e) {} // 3) External lookup links html += '
' + 'Merriam-Webster \u2192' + 'Wiktionary \u2192' + 'Dictionary.com \u2192' + '
'; if (!html) html = '
No details available for "' + escHtml(word) + '"
'; document.getElementById('gwm-body').innerHTML = html; } document.addEventListener('click', function (e) { let el = e.target; for (let i = 0; i < 5 && el && el !== document; i++) { if (el.classList && (el.classList.contains('bm-word-link') || el.classList.contains('sm-word-clickable'))) { e.preventDefault(); const word = el.getAttribute('data-word') || el.textContent.trim().toLowerCase(); if (word && word.length >= 2) { window.open("https://www.a2zwordfinder.com/words/en/crossword-solver/" + encodeURIComponent(word) + "/", "_blank"); } return; } el = el.parentNode; } }); })(); // S49 AC: xd-clues frequency lookup — crossword-historical ranking signal let __xdFreqCache = null; let __xdFreqLoading = null; let __xdBylenCache = null; let __xdBylenLoading = null; async function loadXdFreq() { if (__xdFreqCache) return __xdFreqCache; if (__xdFreqLoading) return __xdFreqLoading; __xdFreqLoading = (async () => { try { const r = await fetch('/data/clue_freq.json.gz'); if (!r.ok) return {}; const ds = r.body.pipeThrough(new DecompressionStream('gzip')); __xdFreqCache = JSON.parse(await new Response(ds).text()); return __xdFreqCache; } catch (e) { return {}; } })(); return __xdFreqLoading; } // S49 AC: (clue,length) length-aware index — fixes happy|5→GLAD wrong-length bug async function loadXdBylen() { if (__xdBylenCache) return __xdBylenCache; if (__xdBylenLoading) return __xdBylenLoading; __xdBylenLoading = (async () => { try { const r = await fetch('/data/clue_freq_bylen.json.gz'); if (!r.ok) return {}; const ds = r.body.pipeThrough(new DecompressionStream('gzip')); __xdBylenCache = JSON.parse(await new Response(ds).text()); return __xdBylenCache; } catch (e) { return {}; } })(); return __xdBylenLoading; } function xdNormClue(s) { return s.toLowerCase().replace(/\([^)]*\)/g, '').replace(/[^a-z0-9 ]/g, ' ').replace(/\s+/g, ' ').trim(); } async function xdLookup(clue, length) { if (!clue) return null; const norm = xdNormClue(clue); // Try length-specific first (most precise) if (length) { const bylen = await loadXdBylen(); const lkey = norm + '|' + length; if (bylen[lkey]) return bylen[lkey]; } // Fall back to clue-only index const data = await loadXdFreq(); return data[norm] || null; } // S49 AC: Datamuse fan-out + Reciprocal Rank Fusion (hybrid clue matcher) const DM_CACHE_KEY = 'gwn_dm_v1'; function dmCacheGet(key) { try { const c = JSON.parse(localStorage.getItem(DM_CACHE_KEY) || '{}'); const e = c[key]; if (e && e.t > Date.now() - 30*86400000) return e.v; } catch (e) {} return null; } function dmCacheSet(key, val) { try { const c = JSON.parse(localStorage.getItem(DM_CACHE_KEY) || '{}'); c[key] = { t: Date.now(), v: val }; if (Object.keys(c).length > 200) { delete c[Object.keys(c)[0]]; } localStorage.setItem(DM_CACHE_KEY, JSON.stringify(c)); } catch (e) {} } async function fetchDatamuse(clue, lengthOrPattern) { if (!clue) return []; const key = (clue + '|' + (lengthOrPattern || '')).toLowerCase(); const cached = dmCacheGet(key); if (cached) return cached; const sp = (lengthOrPattern || '').toLowerCase(); const wantLen = sp.length; // Two parallel calls: rel_syn (true synonyms — strongest signal) + ml (means-like — broader) const ctl = new AbortController(); const tm = setTimeout(() => ctl.abort(), 2500); // S49 AC v2: was 1200, increased for slow networks const mkUrl = (extraParams) => { const p = new URLSearchParams(Object.assign({ max: '50' }, extraParams)); if (sp) p.set('sp', sp); return 'https://api.datamuse.com/words?' + p.toString(); }; let synRes = [], mlRes = []; try { [synRes, mlRes] = await Promise.all([ fetch(mkUrl({ rel_syn: clue }), { signal: ctl.signal }).then(r => r.ok ? r.json() : []), fetch(mkUrl({ ml: clue }), { signal: ctl.signal }).then(r => r.ok ? r.json() : []) ]); clearTimeout(tm); } catch (e) { clearTimeout(tm); } // Blend: rel_syn weight 1.5, ml score-weighted const blend = {}; const isAlpha = w => /^[A-Z]+$/.test(w); synRes.forEach((x, i) => { const w = (x.word || '').toUpperCase(); if (!w || (wantLen && w.length !== wantLen) || !isAlpha(w)) return; blend[w] = (blend[w] || 0) + 1.5 / (i + 1); }); mlRes.forEach((x, i) => { const w = (x.word || '').toUpperCase(); if (!w || (wantLen && w.length !== wantLen) || !isAlpha(w)) return; const s = x.score || 0; const wt = s >= 1000 ? 1.0 : (s >= 100 ? 0.5 : 0.2); blend[w] = (blend[w] || 0) + wt / (i + 1); }); const ranked = Object.entries(blend).sort((a, b) => b[1] - a[1]) .map(([w, s]) => ({ word: w, score: s })); dmCacheSet(key, ranked); return ranked; } function rrfFuse(ours, theirs, kRRF) { const k = kRRF || 60; const scores = {}; const dmRank = {}; ours.forEach((w, i) => { scores[w.word.toUpperCase()] = (scores[w.word.toUpperCase()] || 0) + 1.0 / (k + i + 1); }); theirs.forEach((w, i) => { const wu = w.word.toUpperCase(); scores[wu] = (scores[wu] || 0) + 0.6 / (k + i + 1); dmRank[wu] = i; // 0-indexed Datamuse position }); const oursMap = {}; ours.forEach(w => { oursMap[w.word.toUpperCase()] = w; }); // Union: every word from BOTH sources, annotated with rank info const allKeys = new Set([...Object.keys(scores)]); const merged = []; allKeys.forEach(k2 => { const baseW = oursMap[k2] || { word: k2.toLowerCase(), score: 0, len: k2.length, clues: [] }; merged.push(Object.assign({}, baseW, { rrfScore: scores[k2] || 0, dmAgrees: dmRank[k2] !== undefined, dmPosition: dmRank[k2] !== undefined ? dmRank[k2] : -1, fromDmOnly: !oursMap[k2] })); }); // Apply length filter — only keep words matching pattern length const patLen = (ours[0] && ours[0].len) || (theirs[0] && theirs[0].word.length) || 0; const filtered = patLen > 0 ? merged.filter(w => w.word.length === patLen) : merged; return filtered.sort((a, b) => b.rrfScore - a.rrfScore); } // S49 AC: wordplays-style clue-relevance scoring + Best Matches panel function scoreWordVsClue(word, clueQuery) { // S49 AC: xd-freq is HIGHEST priority — historical crossword usage data if (word.xdRank !== undefined && word.xdRank >= 0) { if (word.xdRank === 0) return { stars: 5, match: 'Most common crossword answer for "' + clueQuery + '" (used ' + (word.xdCount || 0) + 'x)' }; if (word.xdRank < 3) return { stars: 5, match: 'Common crossword answer for "' + clueQuery + '" (used ' + (word.xdCount || 0) + 'x)' }; if (word.xdRank < 7) return { stars: 4, match: 'Documented crossword answer for "' + clueQuery + '"' }; } // S49 AC: Datamuse semantic-primacy — top Datamuse picks get high stars if (word.dmPosition !== undefined && word.dmPosition >= 0) { if (word.dmPosition < 5) { return { stars: 5, match: word.clues && word.clues[0] ? word.clues[0] : 'Top semantic match for "' + clueQuery + '"' }; } else if (word.dmPosition < 15) { return { stars: 4, match: word.clues && word.clues[0] ? word.clues[0] : 'Strong semantic match' }; } else if (word.dmPosition < 30) { return { stars: 3, match: word.clues && word.clues[0] ? word.clues[0] : 'Related to "' + clueQuery + '"' }; } } // Fall back to literal clue-text match if (!word.clues || word.clues.length === 0) return { stars: 0, match: '' }; const q = clueQuery.toLowerCase().trim(); const qTerms = q.split(/\s+/).filter(t => t.length > 1); let bestStars = 0, bestClue = ''; for (const c of word.clues) { const cl = c.toLowerCase(); let stars = 0; if (q && cl.includes(q)) stars = 5; else if (qTerms.length) { const m = qTerms.filter(t => cl.includes(t)).length; if (m === qTerms.length) stars = 4; else if (m >= Math.ceil(qTerms.length / 2)) stars = 3; else if (m >= 1) stars = 2; } if (stars > bestStars) { bestStars = stars; bestClue = c; } } return { stars: bestStars, match: bestClue }; } function renderStarsHtml(n) { return '' + '★'.repeat(n) + '' + '☆'.repeat(5-n) + ''; } function escClue(s) { return (s || '').replace(/&/g,'&').replace(//g,'>'); } function renderBestMatchesPanel(words, clueQuery) { if (!clueQuery) return ''; const scored = words.map(w => Object.assign({}, w, scoreWordVsClue(w, clueQuery))) .filter(w => w.stars >= 2) .sort((a, b) => b.stars - a.stars || b.score - a.score); if (scored.length === 0) return ''; const top = scored.slice(0, 12); const rows = top.map(w => '
' + renderStarsHtml(w.stars) + '
' + '
' + w.word.toUpperCase() + '' + (((w.xdRank !== undefined && w.xdRank >= 0) ? ' \u2606 XD' : '') + (w.dmAgrees ? ' \u2713 DM' : '')) + '
' + '
' + escClue(w.match) + '
' ).join(''); return '
' + '

Best Matches for clue \u201C' + escClue(clueQuery) + '\u201D

' + '' + scored.length + ' clue-ranked match' + (scored.length===1?'':'es') + '
' + '
' + '
Rank
Answer
Best matching clue
' + rows + '
'; } // S49 AC: smart pattern parser ported from puzdep — accepts pattern, length, or mix function parsePatternInput(input) { if (!input) return ''; let normalized = input.toUpperCase().replace(/_/g, '?').replace(/\s/g, ''); // Pure number (e.g., "8" or "94" → sum of digits as wildcards) if (/^\d+$/.test(normalized)) { const length = normalized.split('').reduce((s, d) => s + parseInt(d), 0); return '?'.repeat(length); } // Mixed pattern with numbers (e.g., "?A4" → "?A????") if (/\d/.test(normalized)) { let result = ''; for (const ch of normalized) { if (/\d/.test(ch)) { const n = parseInt(ch); if (n > 0) result += '?'.repeat(n); } else { result += ch; } } return result; } return normalized; } // FCGI search via AJAX document.getElementById('search-form').addEventListener('submit', async (e) => { e.preventDefault(); // Freemium gate if (!isUltraMember && freeSearches <= 0) { document.getElementById('results-container').innerHTML = '

Daily limit reached

You\'ve used your 10 free searches for today.

' + 'Unlock Unlimited Searches →
'; return; } if (!isUltraMember) { freeSearches--; localStorage.setItem('ultra_free_searches', String(freeSearches)); updateFreemiumCount(); } // Build v3 API params (lowercase names) const form = new FormData(e.target); const pattern = parsePatternInput((form.get('Pattern') || '').trim()); if (!pattern) { document.getElementById('pattern').focus(); return; } // S49 AC: switched to /cgi-bin/wordfinder.fcgi (v2.5) — supports ClueHint + returns clues per word const params = new URLSearchParams(); params.set('SearchType', 'Crossword'); params.set('Pattern', pattern); params.set('MatchType', form.get('MatchType') || 'Exactly'); params.set('Dict', form.get('Dictionary') || 'american-crossword'); params.set('format', 'json'); // Lock pattern length so backend doesn't return shorter words params.set('MinLetters', String(pattern.length)); params.set('MaxLetters', String(pattern.length)); // Pass ClueHint if user supplied one (this is THE narrowing-by-clue feature) const clueHintVal = form.get('ClueHint'); if (clueHintVal && clueHintVal.trim()) params.set('ClueHint', clueHintVal.trim()); if (form.get('Starts')) params.set('Starts', form.get('Starts')); if (form.get('Ends')) params.set('Ends', form.get('Ends')); if (form.get('Contains')) params.set('Contains', form.get('Contains')); if (form.get('Excludes')) params.set('Excludes', form.get('Excludes')); params.set('SortBy', form.get('SortBy') || 'Score'); // Score system index into the s[] array: [wwf, scrabble, literati, wordox, lexulous, wordchums, wordfeud, wordscraper] const SCORE_IDX = { Scrabble:1, WordsWF:0, Literati:2, WordOx:3, Lexulous:4, WordChums:5, WordFeud:6, Wordscapes:7 }; const scoreIdx = SCORE_IDX[form.get('Scoring')] || 1; const container = document.getElementById('results-container'); container.innerHTML = '

Searching...

'; const apiUrl = '/cgi-bin/wordfinder.fcgi?' + params.toString(); try { const res = await fetch(apiUrl); if (!res.ok) { container.innerHTML = '

Search error

API returned HTTP ' + res.status + '

' + apiUrl.substring(0,120) + '

'; return; } const data = await res.json(); // v2.5 shape: {count, words:[{word, length, score, clues:[...]}]}; v3 shape: {results:[{w,l,s:[scores]}]} allWords = (data.words || data.results || []).map(w => ({ word: (w.word || w.w || '').toLowerCase(), score: w.score !== undefined ? w.score : (w.s ? w.s[scoreIdx] : 0), len: w.length || w.l || (w.word || w.w || '').length, clues: w.clues || [] })); // S49 AC: hybrid — fan out to Datamuse + xd freq + re-rank const _ch = (form.get('ClueHint') || '').trim(); if (_ch && allWords.length > 0) { const [dmRes, xdRes] = await Promise.all([ fetchDatamuse(_ch, pattern), xdLookup(_ch, pattern.length) ]); // xd freq gives ranked list of historically-common answers for this exact clue // Filter to length-matching words, build weighted ranked list const xdWords = []; if (xdRes && Array.isArray(xdRes)) { // S49 AC: pattern-regex filter (FIX: was length-only) const __ultraPatRe = new RegExp('^' + pattern.toUpperCase().replace(/_/g, '[A-Z]').replace(/\?/g, '[A-Z]') + '$'); xdRes.forEach(([w, cnt]) => { if (__ultraPatRe.test(w.toUpperCase())) xdWords.push({ word: w, score: cnt }); }); } // Merge: xdWords is HIGHEST priority (historical crossword data) // Then Datamuse (semantic), then backend (literal clue match) if (dmRes.length > 0 || xdWords.length > 0) { allWords = rrfFuse(allWords, dmRes, 60); // Apply xd boost: any word in xdWords gets large rrfScore bump if (xdWords.length > 0) { const xdMap = {}; xdWords.forEach((w, i) => { xdMap[w.word.toUpperCase()] = { rank: i, count: w.score }; }); allWords = allWords.map(w => { const xd = xdMap[w.word.toUpperCase()]; if (xd) { return Object.assign({}, w, { rrfScore: w.rrfScore + (3.0 / (xd.rank + 1)), // big boost xdRank: xd.rank, xdCount: xd.count }); } return w; }); // Add xd-only words not already in list const haveSet = new Set(allWords.map(w => w.word.toUpperCase())); xdWords.forEach((w, i) => { if (!haveSet.has(w.word.toUpperCase())) { allWords.push({ word: w.word.toLowerCase(), score: 0, len: w.word.length, clues: [], rrfScore: 3.0 / (i + 1), xdRank: i, xdCount: w.score, fromXd: true }); } }); allWords.sort((a, b) => b.rrfScore - a.rrfScore); } } } if (allWords.length > 0) { document.getElementById('results-heading').textContent = 'Search Results:'; document.getElementById('results-heading').style.color = 'var(--accent)'; renderResults(); } else { container.innerHTML = '

No words found

Try different letters or a broader pattern.

'; } // Scroll to just below style mode bar so results are immediately visible setTimeout(() => { const target = document.getElementById('style-mode-bar'); if (target) target.scrollIntoView({ behavior: 'smooth', block: 'start' }); }, 50); } catch (err) { container.innerHTML = '

Search error

' + err.message + '

URL: ' + apiUrl.substring(0,120) + '

'; } });