# Filtering Seven-Letter Word Puzzles with JavaScript Bitsets

A seven-letter word puzzle can be described as a small set of constraints. A candidate must contain at least four letters, include a required center letter, and use only the supplied letters. Repeated characters are allowed. A pangram is a candidate that contains all seven different letters. These constraints are useful to separate from any particular dictionary: the rules decide which entries fit, while the dictionary decides which entries are available to search.

This tutorial uses A, C, E, L, N, P, and T, with A in the center. It is a practice set, not an official daily puzzle. The implementation below follows the same mask and subset approach used in the project I maintain, with a tiny sample list replacing the stored dictionary index.

## Represent distinct letters, not quantities

Assign each lowercase letter a bit position: A uses position zero, B position one, and so on. Combining the bits produces one integer describing the different letters present in a word. English A–Z needs 26 positions, which fit within JavaScript’s 32-bit bitwise operations.

The important detail is that setting an already-set bit changes nothing. PALACE contains A twice, but its mask contains the A bit once. That matches the puzzle rule: repeated letters are permitted rather than deducted from a limited inventory. Word length remains a separate value, so the mask does not accidentally turn a six-letter word into a five-letter word for scoring.

For a candidate to fit, its set of bits must be a subset of the puzzle’s bits and contain the center bit. A direct filter could check those two conditions for every dictionary entry. Here, we first group entries by their distinct-letter mask, then retrieve eligible groups.

## A small runnable example

Run this JavaScript in a modern browser console or Node.js. It has no dependencies or network requests. The supplied puzzle is hardcoded and valid; input validation for an interactive form is discussed afterward.

```js
const words = ["pace", "plane", "palace", "placenta", "peel", "cat", "panic"];
const letters = "acelnpt";
const center = "a";

function maskFor(text) {
  let mask = 0;
  for (const c of text) mask |= 1 << (c.charCodeAt(0) - 97);
  return mask;
}

const index = new Map();
for (const word of words) {
  if (!/^[a-z]{4,}$/.test(word) || new Set(word).size > 7) continue;
  const mask = maskFor(word);
  if (!index.has(mask)) index.set(mask, []);
  index.get(mask).push(word);
}

const allowed = maskFor(letters);
const centerBit = maskFor(center);
const found = [];
for (let subset = allowed; subset; subset = (subset - 1) & allowed) {
  if (subset & centerBit) found.push(...(index.get(subset) ?? []));
}

const results = [...new Set(found)].sort().map(word => {
  const pangram = maskFor(word) === allowed;
  return { word, pangram, points: (word.length === 4 ? 1 : word.length)
    + (pangram ? 7 : 0) };
});
console.log(JSON.stringify(results, null, 2));
```

The resulting words are PACE, PALACE, PLACENTA, and PLANE. PEEL disappears because it lacks A. CAT is removed while building the index because it is too short. PANIC contains I, so its mask cannot be reached by enumerating subsets of the supplied letters. PLACENTA is the sole pangram in this sample: its eight letters cover all seven different letters, with A reused.

The displayed point values are 1, 6, 15, and 5 respectively. Four-letter words receive one point; longer words receive their length; a pangram adds seven. These are rule-based calculations for illustrative candidates, not confirmation that a publisher accepts them.

## Keep the index separate from the interface

The subset expression visits 127 nonempty subsets for a seven-letter mask. The center check removes groups without A. Reading and returning the matched words still depends on group sizes; this is not a benchmark or a promise about loading speed. In the project, the dictionary index is stored ahead of time, so a request does not rebuild it from the full word list.

The result should preserve actual words, not just masks. Multiple words can share a letter set, and repeats can produce different lengths. For hints, count the words by length, first letter, or first two letters. Do not use the number of matching mask groups as the word count. Likewise, a presentation filter that hides four-letter words should clearly distinguish its visible subset from the full puzzle totals.

## Validate inputs and label the source

An interactive solver needs checks before mask conversion: exactly seven distinct English letters and an unambiguous required center. Our input convention uses one uppercase character, as in Acelnpt. This is a form convention; the puzzle rule itself is that A must appear in every candidate.

I maintain [Spelling Bee Solver](https://spellingbeesolver.co/), an independent dictionary-based tool. Its hints keep word names folded until the user reveals them. Custom searches use SCOWL American English, while daily pages separately display verified published lists when available. Neither bitsets nor a dictionary entry can establish NYT acceptance: the game uses an edited answer list. Keeping that boundary explicit makes both the code and its results easier to understand.

*The sample was checked against the project code and executed locally.*
