LivePublic submissionsChallenge repo ↗

Smallest Complete Crossword

Pack every word of the Moby single-word list into the smallest possible square crossword.

Current best
3,250
steventhornton · 4.1% below baseline
Score
side
lower is better
filled_cells (tie-break)
2,970,647
lower is better
SubmitChecking local sign-in…
Leaderboard
#Solversidefilled_cellsdensitySubmission
1steventhorntonsteventhornton3,2502,970,6470.281Download
Record progression
3.4k3.32k3.23kbaseline 3,388
record (side, lower is better)baseline
Objective

Pack every word of a fixed public word list into a single square grid so the whole thing reads as one connected crossword, and make that grid as small as you can. Smaller side wins; among grids of the same side, fewer filled cells wins.

The word list is the Moby Project single-word list, filtered to words of length at least 2, uppercased, de-duplicated, and sorted: 351,049 words (3,321,695 letters, longest 31, shortest 2), pinned by hash inside the verifier.

A valid grid is a real crossword over that whole list: every across-run and down-run of two or more letters is a word of the list, every word appears exactly once, and all filled cells form one connected block.

What you submit

The submission is a fixed-size little-endian binary file:

bytes 0..3    magic: the 3 ASCII bytes "XWD"
bytes 3..5    n: u16, the side of the coordinate space (the grid is n x n)
then exactly |D| = 351,049 records, each a u32:
    bit 31        orientation: 0 = across (horizontal), 1 = down (vertical)
    bits 0..=30   p: the anchor index, with p = row + n * col

The file is exactly 5 + 4 * 351049 = 1,404,201 bytes; any other length is invalid.

Records are in the canonical dictionary order defined in Constraints, so record i is word D[i] and the word itself is never stored. Decoding a record: row = p % n, col = p // n. The word's first letter (leftmost for across, topmost for down) goes at (row, col). An across word extends right (increasing column); a down word extends down (increasing row).

Constraints

The word list (defines validity)

  • The Moby Project single-word list, the exact file data/moby-single.txt, pinned by SHA-256 2056d03ea1189904b98a13843dd258277f394470229c1e212460eac5074066c5. The verifier uses this file to reconstruct and check the crossword; the submitted artifact contains placements only, not the words themselves.
  • Canonical form, which defines the target set D: uppercase every line and sort byte-lexicographically. (The verifier also drops non-letter and length-1 tokens and de-duplicates; this file has none, so those steps are no-ops.) D is 351,049 words, 3,321,695 letters, longest 31, shortest 2.
  • Record i in the submitted file is word D[i], in exactly this order, so the word itself is never stored.

Validity rules (any failure means invalid)

The verifier reconstructs the grid from (n, records) by placing each word's letters from D, then requires all of:

  1. Well-formed and in range: correct magic, exact file length, 2 <= n <= 3388 (a grid wider than the baseline side could not beat the baseline), every record's p < n*n, and every word fits wholly inside the n x n grid (an across word needs col + len <= n, a down word needs row + len <= n).
  2. Consistent overlaps: wherever two words cross they agree on the shared letter. A cell two words would fill differently is invalid.
  3. No lone cells: every filled cell belongs to at least one entry. An isolated single letter is invalid.
  4. Exact lexicon match: an entry is a maximal run of 2 or more consecutive filled cells in one row (across) or column (down), bounded by a blank or the grid edge ("maximal" means CATS is one entry, not also CAT). The multiset of all entries, across and down, must equal D exactly: every word once, no non-word, no duplicate, none missing. This rule alone decides coverage; the records only build the grid.
  5. Connected: all filled cells form a single 4-connected component (up, down, left, right, not diagonal).

Reading is across (left to right) and down (top to bottom) only: no diagonals, no reversed words.

Rules 1 and 2 alone fix the score, so the verifier compares it to the current best first (or to the baseline on an empty board). A grid that cannot beat the best can never take the record, so it returns skipped without running rules 3 to 5. That is a cost optimization only: a grid that could take the record is always checked in full.

Score

Two metrics, ranked lexicographically. The first decides; the second only breaks an exact tie on the first.

  1. side (minimize): max(bbox_width, bbox_height), the side of the smallest square enclosing all filled cells, so blank borders never help or hurt.
  2. filled_cells (minimize): the count of cells that hold a letter. Fewer at a given side means more letters shared between crossing words.

A new record must be strictly better on the first metric where it differs from the current best. A submission that ties on both metrics does not displace the incumbent, so exact ties resolve to the earliest submission. There is no minimum-improvement margin.

Both metrics are exact integers read off the reconstructed grid, so the same submission always scores the same.

Also reported next to each entry, for context only and never ranked: density (filled_cells / side^2), crossings (the number of shared cells, total_letters - filled_cells, where total_letters is 3,321,695), bbox_width, and bbox_height.

Verifier

Places each word's letters from the pinned word list into a grid at the placement you gave it, checks every rule in Constraints, then scores side and filled_cells. It enforces those rules and nothing more: the verifier is the spec.

There are two implementations. verifier/checker.py is the readable reference and the authoritative one; the Rust verifier in verifier/rust/ is the fast production one, mirroring it constant for constant. A differential test (tests/differential.py) runs both on the same inputs and requires them to agree; they are kept in lockstep, not formally proven equivalent.

Determinism and duplicates

  • No randomness: one fixed instance, the whole list, checked in full. No seed or sample is involved, and the same submission always scores the same.
  • Fingerprint (the dedup key): canonical up to the eight symmetries of the square, so a grid, its rotations and its mirror images are all one key. Each of the eight images of the reconstructed grid is shifted so its filled bounding box starts at the origin, sorted by (row, column), and serialized as 9 bytes per cell (row as u32 little-endian, column as u32 little-endian, one ASCII letter); the lexicographically smallest of those eight byte strings is hashed once with SHA-256. Two files describing the same crossword up to translation, rotation or reflection therefore collide, whatever their byte encoding. A malformed file falls back to the SHA-256 of its raw bytes, so dedup still works. None of this affects the score.
  • Dedup scope is the whole challenge, so a grid any solver has already submitted is rejected as a duplicate.

The verifier is the canonical spec. View verifier source →