How we measured

  • We analysed the grid shapes and clue sentences of the 32 grids left in the game record DB.
  • We modified a copy of the source to record only which path each grid came from, and made 30 grids per difficulty.
  • We ran the new-grid process 3 times per difficulty with gemma4 on the same Mac as the live service. For grid design, we lifted the time limit and collected 6 more responses.

The grid-first path never succeeded once

All 90 grids came from the fallback path. It is the word-joining placer from post 1. It places one word in the middle and attaches words one shared syllable at a time, so grids are not symmetric and one side tends to be empty.

  • Grids with 180° symmetry: 0 of 90
  • 18 of the 60 medium and hard grids contained answers of 6 or more syllables. On the grid-first path, answers cannot exceed 5 syllables.
  • Average word count: easy 10.7, medium 14.9, hard 19.9
  • Median generation time: easy 0.6 s, medium 1.9 s, hard 6.6 s. For hard, one grid in ten took more than 22.9 s.

The grids actually saved are the same. Of the 8 grids saved after switching to grid-first, 0 were symmetric, and 5 had answers of 6–8 syllables.

There is a reason no one noticed. The check that decides whether a grid meets the criteria looks only at the white-square ratio, the number of across and down clues, the number of middle crossings and connectivity. Fallback grids also meet these criteria, so 89 of the 90 grids "passed".

We tried filling words into each of the 24 pre-drawn grids 10 times. All 240 attempts failed, and even the best attempt filled only 28% of the word slots. The average was 14%. Running each grid 20 more times with different seeds never succeeded once either. Random grids were 0 of 120 as well. Of the 24 pre-drawn grids, two pairs were the same grid (medium 4 and 7, hard 5 and 8).

Why they will not fill: Korean syllables

English crosswords cross on the 26 letters of the alphabet. In a Korean crossword one syllable fills one square, so there are far more kinds of characters. The syllables appearing in the word bank alone number 545 kinds, and 178 of them appear only once.

So once a crossing fixes one square, almost no candidates remain. When one syllable of a 5-syllable slot is fixed, there are on average 0.52 other 5-syllable words with the same syllable in that position, and 70% of the time there are none. For 4 syllables it is 0.85 on average, with 55% at zero.

The pre-drawn grids are 64–75% white, and most white squares belong to both an across and a down word. After a few words are placed, one of the crossing squares inevitably ends up with zero candidates. The grids that actually went out are 36.4% white, with only 30–37% of squares shared by across and down words.

AI grid design: 0/9

Every time a new grid is made, it waits 15 seconds for the grid design. In 8 of 9 runs no answer came within 15 seconds, and once an answer came but contained no usable grid.

We lifted the time limit and asked 6 more times. Answers took 13–52 seconds, and of the 15 candidates received, 0 passed the rules, and 0 even after automatic correction. Most were blobs with an entire column painted black, as in the picture above. Once, the answer ran long and was cut off at 1,200 tokens. In the current structure, the 15 seconds spent on grid design are simply thrown away every time.

AI clues: 2/9, and clue quality

Clue writing has a 25-second limit. In 7 of 9 runs it ran out of time and the word bank's default sentences went out. Getting one new grid took 35–54 seconds, with a median of 44 seconds, most of it spent waiting on the AI twice.

The 8 grids actually saved are similar. The AI rewrote the clues for only 2 of them.

The sentences the AI wrote had problems too.

  • They turn into dictionary definitions rather than quizzes. "The football tournament co-hosted by the US, Mexico and Canada in 2026" became "an international football tournament where national teams from many countries gather to compete". Current-affairs clues added to raise the difficulty turn into ordinary definitions.
  • Wrong quotations: a clue about Confucius cited "the teaching, 'Becoming a crane, learn and practise it from time to time'". The opening line of the Analects is "To learn and to practise it from time to time".
  • Tense: it described the 2026 Winter Olympics, which are already over, as "a winter sports festival scheduled to be held".
  • A book that does not exist: it called d'Alembert "the person who wrote the *Pilsoenon*".
  • It called Daejeon Metropolitan City an "autonomous metropolitan district".

The word bank and current-affairs words

  • Some entries cut words short to fill 5-syllable slots. There are 8 of them, such as '인천국제공' (Incheon Internationa…), '김포국제공' (Gimpo Internationa…), '시외버스터' (intercity bus termi…) and '국회의원선' (general elec…), with clues of the form "the first part of …". The answer for the San Francisco bridge is '금문' (Golden Gate), not '금문교' (Golden Gate Bridge).
  • Some entries have clues that are simply wrong. The clue for '냉장고문' (refrigerator door) is "the door you open and close on a cupboard".
  • Answers taken from random Wikipedia articles: words like '시발리크급구축함' (Shivalik-class frigate), '란도어과', '일리네어레코즈' (Illionaire Records) and '임혁동음이의' (Lim Hyuk disambiguation). '동음이의' (disambiguation) is a word attached to article titles. The clue is the first sentence of the wiki article cut at 70 characters, so the clue for '이채윤가수' (Lee Chae-yoon, singer) nearly gives away the answer, like "Chaeyoon (real name: Lee Seul, …) is a South Korean singer". 4 of the 8 saved grids contained words like these.
  • Recent news is hardly used in practice. The new-grid API calls the feature that has the AI pick words from the news with it switched off. Headlines are used for only one rule: if they contain 'Nepal', 'flood' and 'glacier', add Nepal and the Himalayas. Nepal gets added even for flood stories from other countries. Most current-affairs clues are the 12 added by hand.
  • Vocabulary grades by difficulty (common, ordinary, rare) exist only as a function that computes them; the generator does not use them. Difficulty is currently distinguished only by grid size, revealed squares and answer length.

Resuming works well

Partially saved grids could be reopened from the records and resumed, and empty grids were deleted. After switching to entering whole answers, two finished 20-clue grids took 24 and 27 inputs and 24 and 15 minutes.

One thing needs fixing. The game room saves the elapsed time every 5 seconds, so time accumulates even if a grid with no input is just left open in a tab. One empty grid had more than 13 hours recorded, and since it never went through the lobby, it was never deleted either.

Directions for fixing

  • Remove the AI from grid design or push it back. Right now it spends 15 seconds and gains nothing. The program will build the grids, going with sparser grids that have fewer squares shared by across and down words.
  • Find words to fit the grid. Give the model the syllables already fixed in a slot, like '○스○○', have it propose candidate words, then verify them against a dictionary before placing them. The predefined word bank alone is short on 4- and 5-syllable words.
  • Build grids in advance. Each grid takes about 44 seconds, so build and stock them in the background per difficulty and hand them out immediately. That also allows longer time limits and leaves time for review.
  • Review the clues. Keep current-affairs clues in quiz form, and double-check dates, tense and quotations. Drop wiki "disambiguation" articles and overly long proper nouns, and check whether part of the answer appears in the clue.
  • Clean up the truncated words and wrong clues, and reflect vocabulary grades in the actual difficulty.

These are all 32 saved grids. Word count and grid usage clearly improved. But those grids came from the fallback path, not the planned grid-first path, and what remains is the grid shape and the quality of the clues.

The actual code for this step

This is the order for filling a grid. It tries pre-drawn grid → random grid → word-joining placer. In the measurements the first two steps never succeeded once, so it always fell through to the last line.

lib/gridFill.tstypescript · 15 lines
for (const mask of shuffle(readyBoards(size, cap), rng)) {
  const filled = tryMask(mask);
  if (filled) return filled;
}
for (let attempt = 0; attempt < 8; attempt++) {
  const mask = makePattern(size, 2, cap, rng);
  if (!mask) continue;
  const filled = tryMask(mask);
  if (filled) return filled;
}
for (let attempt = 0; attempt < 5; attempt++) {
  const connected = tryBuildConnected(size, maxLen, bank, prefer, avoid, rng);
  if (connected) return connected;
}
return null;

This is the part that collects current-affairs words. The new-grid API passes useLlm as false, so the news-based AI words are empty and the headlines are used only for the Nepal rule.

lib/dynamicBank.tstypescript · 18 lines
export async function loadDynamicBank(
  difficulty: DiffKey,
  avoid: Iterable<string>,
  options?: { useLlm?: boolean },
): Promise<DynamicBankResult> {
  const avoidList = [...avoid];
  const [headlines, wiki] = await Promise.all([fetchNewsHeadlines(), fetchWikiEntries(16)]);
  const llm = options?.useLlm === false ? [] : await fetchLlmEntries(difficulty, headlines, avoidList);
  const newsHints: WordEntry[] = [];
  if (headlines.some((h) => h.includes("네팔") || h.includes("홍수") || h.includes("빙하"))) {
    const nepal = asHangulEntry("네팔", "이번에 히말라야 빙하가 무너져 큰 홍수가 난 나라");
    const himal = asHangulEntry("히말라야", "올해 빙하가 붕괴해 네팔에 대홍수를 일으킨 산맥");
    if (nepal) newsHints.push(nepal);
    if (himal) newsHints.push(himal);
  }
  const entries = [...llm, ...newsHints, ...wiki];
  const source: DynamicBankResult["source"] = llm.length >= 12 ? "llm" : wiki.length ? "wiki" : headlines.length ? "news" : "bank";
  return { entries, source };

These are the words cut short to fit 5 syllables.

lib/words.tstypescript · 3 lines
{ word: "시외버스터", clue: "시외버스가 모이는 곳의 앞말" },
{ word: "인천국제공", clue: "서해 관문 국제공항의 앞말" },
{ word: "김포국제공", clue: "서울 서쪽 국제공항의 앞말" },

The next post is "4. The full source code and the tools used".