Files
andrey 3061247ce5 Паттерны и кампания: 8 новых фигур, 3 showcase-уровня, факты в README
- PATTERNS: авианосец, длинная лодка, каноэ, MWSS, HWSS, Loafer, кролики, ружьё Симкина (22 → 30)
- CAMPAIGN_LEVELS: ручные «Флотилия»/«Погоня»/«Кролики» после «Издалека» (100 → 103)
- README: раздел «Интересные факты» и список ресурсов
- tests: счётчики и проверки новых фигур/уровней синхронизированы
2026-10-13 15:06:52 +03:00

121 lines
23 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Life — территориальная борьба
Браузерная hot-seat игра-переосмысление *Conway's Game of Life*: двое игроков по очереди расставляют свои живые клетки, а дальше доска живёт по правилам Конвея — и чей цвет захватит поле, тот и победил. Один HTML-файл, без зависимостей: открой в браузере и играй.
## Интересные факты
Наша игра — **вариант** классического автомата: у Конвея клетки одного цвета, бесконечное поле и «игра для нуля игроков», а у нас — территориальная борьба двух цветов, конечное поле, бюджет клеток и наследование цвета большинством. Вот несколько фактов о самой классике.
- **Джон Хортон Конвей** придумал игру в **1970 году** как упрощение самовоспроизводящейся машины фон Неймана (1940-е). Публика сделала её знаменитой после колонки Мартина Гарднера в **Scientific American (октябрь 1970)**.
- Это **игра для нуля игроков**: вся эволюция полностью задаётся начальной расстановкой. Автомат — **B3/S23**, окрестность **Мура** (8 соседей), состояния одновременны (тик).
- Конвей объявил приз **$50** тому, кто найдёт пример неограниченного роста. В ноябре 1970 приз взяла команда MIT во главе с **Биллом Госпером** — так появилось **ружьё Госпера** (первое ружьё, 36 клеток, период 30).
- Игра **тьюринг-полна**: в 2002 году Пол Чапмен построил в ней универсальную регистровую машину (268 096 клеток). Всё, что вычислимо алгоритмически, вычислимо и в Life.
- Life **омнипериодична**: существуют осцилляторы любого периода. Последним, в **июле 2023**, был найден период **41**.
- Алгоритм **HashLife** (Госпер, 1984) хранит подпаттерны в хеш-таблице и перескакивает тысячи поколений — им посчитано 6-октиллионное поколение машины Тьюринга Ренделла менее чем за 30 секунд.
- **Garden of Eden** — конфигурации без «родителя» — существуют по теореме Мура–Майхилла с самого рождения игры. «Проблема уникального отца» (1972) была решена лишь в **2022** (пример из 306 клеток).
- Конвей подбирал правила так, чтобы **не было простого доказательства** неограниченного роста, но такие растущие конфигурации всё же существовали, а простые «семена» долго и непредсказуемо развивались.
### Ресурсы
- **LifeWiki** — главная энциклопедия игры: `https://conwaylife.com/wiki` (может требовать обхода Cloudflare; страницы доступны через `https://web.archive.org`).
- **Catagolue** — перепись «естественно возникающих» паттернов, краудсорсинг-поиск: `https://catagolue.hatsya.com`.
- **Life Lexicon** (Stephen Silver) — словарь терминов: `https://conwaylife.com/ref/lexicon/lex.htm`.
- **OEIS** — числовые последовательности (стойкие фигуры `A019473`, плотные `A055397` и др.): `https://oeis.org`.
- **MathWorld** — обзор с правилами и ссылками: `https://mathworld.wolfram.com/GameofLife.html`.
- **Golly** — эталонный симулятор с HashLife: `https://sourceforge.net/projects/golly/`.
- **ibiblio** — скан оригинальной статьи Гарднера 1970 года и коллекция паттернов: `https://www.ibiblio.org/lifepatterns/`.
- **Rosetta Code** — реализации Life на десятках языков: `https://rosettacode.org/wiki/Conway%27s_Game_of_Life`.
## Как играть
Игра на одном экране, игроки ходят по очереди, передавая устройство друг другу. На **стартовом экране** — краткое описание игры, выбор темы и две равноправные кнопки режимов: **«Кампания»** и **«Игра на двоих»** (а над ними выделенная **«Продолжить партию»**, если есть отложенная). Нажав «Игра на двоих», вы попадаете на экран с настройками поля (размер, «замкнутая карта», бюджет) и справкой «Как играть»; партия стартует по кнопке **«Начать»**. Сначала **первый игрок** расставляет свои клетки, затем устройство переходит **второму** — он расставляет вслепую, не видя расстановку соперника. Если первый игрок хочет что-то поправить, на экране передачи есть кнопка **«Вернуться к игроку 1»**. После этого включается симуляция: живые клетки выживают при 2–3 живых соседях, а в пустой клетке рождается новая ровно при 3 соседях — и цвет новорождённой наследуется от большинства из своих соседей-родителей.
Каждый игрок может ставить клетки **только в своей половине поля** (первый — в левой, второй — в правой), а неактивная половина затемняется, чтобы было видно зону. На всю партию у каждого игрока есть **бюджет клеток** — счётчик остатка виден в HUD. Бюджет тратится на каждую новую поставленную клетку, **ластик возвращает** потраченное, а симуляция бюджет не расходует.
Когда поле **перестало меняться** — симуляция сама встаёт на паузу (и в HUD появляется метка «поле стабильно»), после чего каждый игрок может **дорисовать** клетки (уже видя всё поле): на паузе есть отдельные кнопки **«Дорисовать красным»** и **«Дорисовать зелёным»** (в любом порядке) и **«Старт симуляции»**. Дорисовка доступна только на «застывшем» поле — это либо неподвижная структура, либо короткий цикл (например, мигалка). Если поле продолжает меняться (глайдеры, рост, хаос), дорисовки нет — такую партию можно только завершить вручную. Партия заканчивается при **вымирании** — как только у игрока не осталось ни одной клетки его цвета на доске (бюджет его уже не спасает), — **тупиком по бюджету** (поле стабильно, а отстающему игроку нечем ходить: его бюджет исчерпан, и ни поставить, ни стереть он не может; при равном счёте — когда бюджет кончился у обоих) либо **ручным завершением** кнопкой «Завершить партию» (с подтверждением, чтобы не завершить случайно). **Победитель — у кого на доске больше живых клеток своего цвета.**
Правила: окрестность Мура (8 соседей), классический автомат **B3/S23**. По умолчанию **границы жёсткие** (без заворота краёв); на экране «Игра на двоих» можно включить опцию **«Замкнутая карта (torus)»** — тогда соседи за краем берутся с противоположной стороны.
## Кампания
В меню есть кнопка **«Кампания»** — это режим-испытание с отдельным экраном выбора уровня (справка «Как играть» — в сворачиваемом блоке на том же экране). На уровне заранее расставлены **стабильные фигуры врага** (зелёные клетки); вы играете красными и должны **уничтожить все клетки врага** (добиться нуля зелёных). Враг сам не ходит — поле живёт обычной детерминированной симуляцией Конвея, поэтому врага уничтожают, подводя его фигуры под смерть/перерождение по правилам B3/S23.
- **Зона рисования** на уровне — прямоугольник (может быть меньше поля или совпадать с ним); неактивная область затемняется, как и половины в обычной партии.
- **Бюджет клеток** — свой на каждый уровень, виден в HUD; при нуле остатка метка краснеет.
- **Врага нельзя трогать напрямую:** краска и штамп не перезаписывают зелёные клетки (и не тратят за них бюджет), ластик их не стирает и бюджет не возвращает.
- **Вымирание игрока — не проигрыш:** даже если красных клеток не осталось, можно продолжать, пока есть бюджет.
- **Победа** — на доске не осталось клеток врага. **Поражение** — бюджет исчерпан, а враг ещё жив (фиксируется, когда поле стабилизировалось и симуляция сама встала на паузу).
- HUD показывает **«Уровень N/T»** и остаток бюджета; обычный счёт скрыт.
- **Прогресс** (сколько уровней открыто) сохраняется, победа открывает следующий. Всего **103 уровня**: ручные («Разминка» — блок, «Каравай», «Рой» — много фигур, «Издалека» — враг вне зоны игрока, а сразу за ним showcase-уровни «Флотилия»/«Погоня»/«Кролики» с новыми фигурами), остальные **96** расставлены **генератором** `tools/gen-campaign-levels.mjs` (детерминированно, фиксированный seed) и вшиты в `index.html` статически. Размер поля у сгенерированных уровней постепенно растёт до 300×300; у ручных вставок (в т.ч. «Издалека» и showcase) размер свой. Зоны рисования разнообразны (половина, центр, углы, узкие полосы, произвольный прямоугольник); зона на всё поле есть только у ручных «Разминки» и «Роя» — у сгенерированных её нет. Список уровней прокручивается. Решаемость уровней намеренно не проверяется: среди них есть заведомо почти непроходимые (например с ружьём Госпера) — это задумка.
- Когда поле уровня **стабилизировалось**, игра сама открывает фазу дорисовки — жать «Дорисовать» не нужно: дорисуйте свои клетки и нажмите **«Готов»** — симуляция запустится сразу (цикл «нарисовал → побежало»). Кнопка **«Заново»** в панели действий перезапускает уровень в любой момент (с подтверждением).
- Уровень можно **отложить в меню** (кнопка «В меню»/⚙️ посреди уровня) и вернуться к нему кнопкой «Продолжить партию»; из финала — «Следующий уровень», «К уровням» или «Заново».
- Настройки уровня (размер поля, бюджет) действуют только внутри уровня и **не меняют ваши настройки из меню**: обычная партия после кампании стартует с прежним размером и бюджетом.
## Управление
- **Инструменты:** карандаш 1×1, кисть 3×3, ластик 1×1, ластик 4×4. **Правая кнопка мыши — ластик:** удерживайте её, чтобы стирать, не переключая инструмент (если активен ластик, ПКМ сохраняет его размер, иначе стирает 1×1).
- **Готовые паттерны (штампы):** дропдаун **«Паттерн»** в тулбаре, сгруппированный по категориям — **Стойкие** (блок, улей, ванна, лодка, кораблик, каравай, пожиратель, пруд, авианосец, длинная лодка, каноэ), **Осцилляторы** (мигалка, жаба, маяк, часы, пульсар, пентадекатлон), **Корабли** (глайдер, LWSS, MWSS, HWSS, Loafer), **Долгожители** (R-пентамино, жёлудь, Diehard, громовая птица, кролики) и **Ружья** (ружьё Госпера, ружьё Симкина). Выберите фигуру и кликните: она встанет целиком, **левый верхний угол — в точке клика**. Клетки вне вашей половины не ставятся (в превью они подсвечены серым), бюджет тратится только за поставленные. Одно нажатие — одна постановка (перетаскивание фигуру не повторяет); ПКМ по-прежнему стирает. Поворот фигуры на 90° — кнопкой **«↻»** в тулбаре (видна при выбранном паттерне) или **колесом мыши** над доской. Выбор паттерна и его поворот не сохраняются между сессиями.
- **Режим «постановка по отпусканию» (для сенсорных экранов):** кнопка **«👆 тап»** в тулбаре. Когда режим включён, нажатие/касание только подсвечивает будущее место, а клетка (или фигура) ставится в момент **отпускания** — палец не закрывает точку постановки, а перетаскивание не рисует линию. Ластик и ПКМ работают так же — по отпусканию. Режим не сохраняется между сессиями и не сбрасывается при новой партии.
- **Поле:** 40 / 60 / 80 / 100 / 120 / 150 / 200 / 250 / 300 клеток в стороне (по умолчанию 60×60) плюс прямоугольные 60×100 и 100×60; размер выбирается дропдауном на экране **«Игра на двоих»**; правила партии открываются там же по кнопке «Как играть».
- **Бюджет клеток** на партию (по умолчанию 100): комбобокс на экране **«Игра на двоих»** — можно выбрать пресет из списка (100/250/500) или ввести любое своё число.
- **«Замкнутая карта (torus)»** — опция на экране **«Игра на двоих»** (по умолчанию выключена, границы жёсткие).
- **Тема оформления** — селект в **стартовом меню**: **«Тёмная»** (по умолчанию) или **«Светлая»**. Выбор сохраняется и применяется после перезагрузки.
- **Зоны рисования:** первый игрок ставит клетки только в левой половине, второй — только в правой.
- **Скорость симуляции** задаётся константой `STEPS_PER_SEC` (~8 шагов/сек) в коде `index.html`. Во время симуляции в панели действий есть ряд из четырёх кнопок **1× / 2× / 4× / 8×** — клик выбирает множитель скорости (выбранная кнопка подсвечена); множитель не сохраняется и сбрасывается при новой партии.
- **Клавиатура:** **пробел** ставит и снимает паузу во время симуляции (как у медиаплеера) — то же, что кнопки «Пауза»/«Старт симуляции». В меню, фазах расстановки/дорисовки, при передаче устройства, в финале и при открытом диалоге подтверждения пробел ничего не делает; в поле ввода (бюджет) работает как обычный пробел.
- **«Сброс»** сбрасывает только текущий ход — возвращает поле и бюджет к началу фазы рисования.
## Сохранение и резюм партии
Игра сама сохраняет прогресс в `localStorage` браузера (ключ `life-save-v1`) — без сервера и аккаунтов. Сохраняются:
- **настройки**: размер поля, активный инструмент, скорость симуляции, опция «Замкнутая карта», тема оформления, бюджет клеток;
- **текущая партия**: состояние (чей ход / симуляция / пауза / передача устройства), поле целиком и счёт;
- **кампания**: прогресс (сколько уровней открыто) и незавершённый уровень кампании (с полем врага и зоной рисования) — он резюмится так же, как обычная партия.
Поэтому после **перезагрузки страницы** незавершённая партия продолжается с того же места (включая фазу расстановки и идущую симуляцию). Если партия завершена или сейва нет — игра открывается в меню. Если сохранённые данные битые или устаревшего формата, игра просто стартует в меню, не падая. Кнопка **«В меню»** (и ⚙️) **не теряет партию**: она откладывает её, и в меню появляется кнопка **«Продолжить партию»** для возврата. Кнопка **«Игра на двоих»** (а также «Реванш») при наличии незавершённой партии сначала спрашивает подтверждение, чтобы случайно её не потерять.
> ⚠️ `localStorage` привязан к origin, поэтому сейв/резюм **не переносятся** между `file://` и `http://localhost:8000` — для проверки резюма используйте один и тот же способ открытия.
## Запуск
Просто открой `index.html` в браузере (десктоп или телефон) — игра работает с `file://`. Для локальной разработки:
```bash
python3 -m http.server 8000
# → http://localhost:8000
```
## Тесты
Сначала проверка синтаксиса JS, встроенного в `index.html`:
```bash
node tools/check.mjs
```
Затем headless-суита на puppeteer-core в Chromium (нужен установленный Chromium; путь при необходимости правится переменной `CHROMIUM`):
```bash
cd tests
npm install
npm test
```
Ждать все **PASS** и exit 0. Суита также включает проверки персистентности/резюма, миграции сейва, resize и проводки DOM-кнопок.
Отдельно — **паритет с прототипом** (harness в `tools/old-ref/`, опционально): сборка `g++` и дифтест, подробности в `tools/old-ref/README.md`.
## Структура
```
index.html # вся игра: HTML + CSS + один <script>
tests/ # headless-тесты (puppeteer-core): test.js, parity-fixture.json, package.json
tools/check.mjs # проверка синтаксиса JS из index.html
tools/gen-campaign-levels.mjs # генератор 96 уровней кампании (seeded PRNG, без Math.random)
tools/old-ref/ # harness паритета с прототипом (C++ + генераторы; см. README там)
AGENTS.md # правила разработки и тестирования
README.md # этот файл
old/ # референс старого C++/Qt4 прототипа (не часть продукта, не трогать)
```