Deliverable фазы 1. Пара к levels.json и validate_levels.py. Контент — английский, обоснование — русское.
10 уровней прототипа сэмплируют полную 100-уровневую кривую в точках
1, 12, 23, 34, 45, 56, 67, 78, 89, 100 (равномерный шаг ≈ 11). Балансовая таблица фазы 5
(brief-balance-table) обязана давать те же значения P1–P8 и difficulty.score в этих десяти
точках — единая модель, два артефакта. Значения ниже — контракт для фазы 5.
1. Сложность как система из 8 параметров (не «слова длиннее»)
| # | Параметр | L1 (v1) | L10 (v100) | Как масштабируем |
|---|---|---|---|---|
| P1 | Число слов | 1 | 5 | грубый регулятор длины уровня |
| P2 | Длина слов (макс) | 4 | 6–7 | потолок читаемости на 6" |
| P3 | Частотность (Zipf-банд) | топ (5.0+) | редкая (2.5–3.5) | банды словаря wordfreq |
| P4 | Скрэмбл-скор (0–1) | 0.33 | 0.70 | формула §3 |
| P5 | Анаграммы-ловушки | нет | да | правдоподобная ложная анаграмма в пуле |
| P6 | Букв / слов в ответе | 3 / 1 | 10 / 2 | длина и «многословность» финала |
| P7 | Тип каламбура | direct | double-layer | прямое → омофон → двухслойное |
| P8 | Прозрачность карикатуры (5=почти показывает, 1=косвенный намёк) | 5 | 1 | детализация сцены в промпте |
Почему шкала P8 инвертирована (5 = легко, 1 = сложно): чтобы «рост сложности» = «падение P8»,
удобно читать спайки. В levels.json хранится как есть; в difficulty-скоре вклад берётся как (6−P8).
2. Таблица уровней: P1–P8, целевое время, роль в кривой
| Прото | Virt | Слова | Ответ | P1 | P2 | P3 | P4 | P5 | P6 | P7 | P8 | score | Цель, сек | Роль |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 1 | NAPS | NAP | 1 | 4 | 1 | 0.33 | 0 | 3 | direct | 5 | 1.0 | <30 | старт/туториал |
| 2 | 12 | PANE·TREE | PANE | 2 | 4 | 2 | 0.33 | 0 | 4 | direct | 4 | 1.6 | 30–45 | подъём |
| 3 | 23 | MEAT·TREE | MEET | 2 | 4 | 3 | 0.45 | 0 | 4 | homophone | 4 | 2.3 | 40–60 | 1-й омофон |
| 4 | 34 | HEIR·FORCE | HEIR FORCE | 2 | 5 | 4 | 0.60 | 1 | 9 | homophone | 3 | 3.4 | 70–100 | СПАЙК |
| 5 | 45 | HOLD·BOAT·SHOW | OLD HAT | 3 | 4 | 3 | 0.33 | 0 | 6 | idiom | 4 | 3.0 | 60–80 | передышка |
| 6 | 56 | PIER·PRESSURE·GUIDED | PIER PRESSURE | 3 | 6 | 4 | 0.52 | 1 | 12 | homophone | 3 | 4.2 | 90–120 | подъём |
| 7 | 67 | SIR·PRIZE·MODEST·AZURE | SIR PRIZE | 4 | 5 | 5 | 0.50 | 1 | 8 | double-layer | 2 | 5.0 | 110–140 | СПАЙК |
| 8 | 78 | GAME·SHOW·GUIDED·SHEEP | GAME SHOW | 4 | 5 | 4 | 0.40 | 1 | 8 | double-layer | 3 | 4.6 | 100–130 | передышка |
| 9 | 89 | SCREEN·TEST·GRASSY·HUMBLE | SCREEN TEST | 4 | 6 | 5 | 0.60 | 1 | 10 | double-layer | 2 | 5.6 | 130–160 | подъём |
| 10 | 100 | PANE·RELIEF·POROUS·SLEIGH·TETHER | PANE RELIEF | 5 | 6 | 6 | 0.70 | 1 | 10 | double-layer | 1 | 6.8 | 180+ | ТОП-СПАЙК |
(В скоре учитываются только circle-несущие слова + число слов как P1; filler-слова на поздних
уровнях — регулятор P1/времени, но их буквы не идут в ответ, как и часть некружковых клеток в оригинале.)
3. Формула скрэмбл-скора P4 (0–1)
Реализована в validate_levels.py и генераторе (sscore). Для answer a, scramble s длины n:
kept = число исходных биграмм (a[i],a[i+1]), сохранившихся в s как соседство
в любом порядке (прямом или обратном)
broken_frac = 1 − kept / (n−1) # 1.0 = все биграммы разрушены (макс. дезориентация)
first_pen = −0.15 если s[0] == a[0] # первая буква на месте → легче
last_pen = −0.05 если s[−1] == a[−1] # последняя на месте → чуть легче
P4 = clamp(0..1, broken_frac + first_pen + last_pen)
- Почему биграммы: сохранённая пара букв (напр.
TRвTREE) — «зацепка», по которой мозг
достраивает слово; их разрушение — главный рычаг «залипания» скрэмбла (jumble-analysis п.2). - Почему штраф за первую букву: в оригинале скрэмблы с первой буквой на месте решаются заметно
быстрее; это стандарт градации scramble-режимов (research §4 п.5: Easy = первая/последняя на месте). - Почему обратное соседство считается «сохранённым»: чистый разворот слова (
WEARY→YRAEW)
читается задом наперёд почти мгновенно — формула честно даёт ему P4≈0. Поэтому на поздних уровнях
развороты не используются (см.gen_levels.py). - Псевдослова («залипают», jumble-analysis п.2) косвенно поощряются: чтобы разрушить биграммы,
скрэмбл обычно получает произносимый, но не-словарный вид (ESRPRSUE,MEHBUL) — и это же
проверяется гейтом (b): scramble с zipf ≥ 3 отвергается как настоящее слово.
4. Кривая: восходящая пила (спайки 4/7/10, передышки 5/8)
score
7.0 ┤ ●10 ← топ-спайк (=v100)
6.0 ┤ ●9
5.0 ┤ ●7 7,9 подъёмы
4.5 ┤ ●8 (дип)
4.0 ┤ ●6
3.5 ┤ ●4 (спайк)
3.0 ┤ ●5 (дип)
2.0 ┤ ●3
1.5 ┤ ●2
1.0 ┤●1
└─┬──┬──┬──┬──┬──┬──┬──┬──┬──┬─
1 2 3 4 5 6 7 8 9 10
Чем создан каждый спайк / передышка (конкретно):
- Спайк L4 (v34) — «первый настоящий вызов»: впервые появляются одновременно (а) двухсловный
ответ-омофонHEIR FORCE(P6 9 букв / 2 слова), (б) первая ловушка-анаграмма P5, (в) скрэмбл
CROFEдляFORCEс P4 0.60. Скачок score 2.3→3.4. Почему тут: research §5 — первая реальная
сложность на 5–10 уровне; в сэмпле это как раз v34. - Передышка L5 (v45): число слов растёт до 3 (ощущение прогресса), но лексика возвращается в
топ-банд (HOLD/BOAT/SHOW, Zipf 5+), скрэмблы лёгкие (P4 0.33), каламбур — знакомая идиома
OLD HAT, ловушек нет. score падает 3.4→3.0. Почему: «дыхание» после пика — стандарт casual
level-design (research §4 п.6). - Спайк L7 (v67) — «когнитивный скачок»: впервые двухслойный каламбур
SIR PRIZE
(омофон surprise + смысловой слой «сэр»), 4 слова, P8 падает до 2 (карикатура намекает косвенно).
score 4.2→5.0. - Передышка L8 (v78): те же 4 слова, но дружелюбнее — лексика проще (P3 4), скрэмблы мягче
(P4 0.40), карикатура прозрачнее (P8 3), ответ короче (8 букв). score 5.0→4.6. - Топ-спайк L10 (v100) — «экзамен»: максимум по совокупности: 5 слов (P1 5), редкая лексика
POROUS/SLEIGH/TETHER(P3 6), самые злые скрэмблы (P4 0.70), двухслойныйPANE RELIEF
(pane=pain), и самая косвенная карикатура (P8 1: доктор просто вздыхает от облегчения — связь
«окно → боль → облегчение» надо додумать). Цель — 3+ минуты. score 5.6→6.8.
Почему пила, а не прямая: монотонный рост выматывает; локальные передышки после пиков дают
дофаминовую «награду лёгкостью» и удерживают retention (research §4 п.6). Гейт (e) валидатора
явно допускает дипы только на id 5 и 8.
5. Маппинг на полную кривую 1→100 (контракт для фазы 5)
- Каждый прото-уровень k — якорь в точке
virtualLevel[k]. Между якорями балансовая таблица
интерполирует P1–P8 монотонно (с той же пилой в миниатюре), но в самих якорных точках значения
обязаны совпасть с таблицей выше. - Опорные пары, которые фаза 5 не имеет права нарушить:
- v1 → score 1.0, 1 слово, P8=5 (туториал);
- v34 → первый спайк (первый 2-словный омофон + первая ловушка);
- v45, v78 → локальные дипы (передышки);
- v100 → score 6.8, 5 слов, двухслойный каламбур, P8=1.
- Диапазон difficulty.score прототипа 1.0–6.8 = сжатый диапазон полной игры; в полной таблице
100-й уровень = потолок кривой, 1-й = пол. Формула score в фазе 5 = та же взвешенная сумма
нормированных P1–P8, что даёт эти 10 чисел на входных P-значениях таблицы выше.
Ориентировочная формула score (для сшивки с балансом):
score = 0.9·P1 + 0.25·P2 + 0.35·P3 + 1.2·P4 + 0.6·P5 + 0.12·P6
+ w(P7) + 0.30·(6−P8)
где w(direct)=0, w(idiom)=0.3, w(homophone)=0.6, w(double-layer)=1.0
Веса подобраны так, чтобы дать таблицу §2 (пила со спайками 4/7/10). Точная калибровка весов —
задача balance-economist в фазе 5; здесь формула зафиксирована как каркас, значения score — как якоря.
6. Проверка критериев готовности
- L1 решается <30 сек без подсказок: 1 слово, 4 буквы, топ-лексика, лёгкий скрэмбл, прямой ответ. ✓
- L10 требует 3+ мин: 5 слов, редкая лексика, злые скрэмблы, двухслойный каламбур, косвенная
карикатура (P8=1). ✓ - Монотонность с передышками: гейт (e)
validate_levels.py= PASS. ✓ - Якоря 1→100 зафиксированы для фазы 5 (§5). ✓
Дополнение (гейт-решение пользователя): кружки «как в оригинале»
По решению на гейте фазы 1 кружки перераспределены под аутентичное правило Jumble:
каждое слово уровня отдаёт ≥1 букву в ответ-сюрприз, а финал — настоящая анаграмма из
букв, разбросанных по всем словам (а не «убрать пробел из двух уже разгаданных слов»).
- Реализовано скриптом
redistribute_circles.py(задача о назначениях: покрыть мультисет букв
ответа позициями слов так, чтобы каждое слово было задействовано; приоритет наименее загруженного
слова — для баланса распределения). - Валидатор получил правило (g): слово с нулём кружков — это FAIL. Все 10 уровней проходят.
- Пример L10
PANE RELIEF: PANE→A,N; RELIEF→E,L,F; POROUS→P,R; SLEIGH→E,I; TETHER→E — 10 букв
из 5 слов собираются в ответ. Финал топ-уровня стал настоящей кросс-словной анаграммой, что
усиливает кульминацию (соответствие задаче «10-й уровень ощущается как 100-й»). - Следствие для баланса (фаза 5) и генератора (фаза 4): every-word-contributes — жёсткий инвариант,
генератор обязан его соблюдать (уже отражено в brief-level-generator: подбор слов под кружки — CSP).