# 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 + один