Навіщо симуляція?
Я люблю симуляції з дитинства - спостерігати, як крихітні системи оживають, ухвалюють власні рішення, виростають у щось, чого ти не планував. Це захоплення нікуди не зникло.
Ця почалася з малого. Я додав функцію, що давала відвідувачам садити ASCII-квіти на головній сторінці кліком. І все - лише квіти. Потім подумав, що було б добре, якби були ще й дерева. Потім захотів, щоб дерева розмножувалися самі. Потім світ почав відчуватися порожнім, тож я додав селян - а щойно вони з’явилися, їм треба було чимось зайнятися. Вони почали рубати дерева, обробляти поля, будувати будинки. Кожна нова функція робила наступну очевидною. Симуляція росла так само, як ростуть села всередині неї - органічно, одна частина за раз.
Результат - крихітна ASCII-цивілізація, що живе за героєм: селяни збирають ресурси, будують містечка й співіснують із процедурно згенерованою екосистемою дерев, озер, хмар, риб, бджіл, метеликів і світляків. Це не гра - тут немає умови перемоги. Це фоновий світ, що винагороджує увагу.
Стек: React, TypeScript, Astro
Рендеринг: DOM-елементи (без canvas)
Архітектура: кастомне сховище pub/sub із циклом симуляції на основі тіків
Архітектура
Симуляцію розділено на два шари, що працюють незалежно:
Шар логіки (simulationStore.ts) - машина станів на основі тіків, що працює з інтервалом 200 мс. Вона керує всіма сутностями, їхніми ШІ-рішеннями, обліком ресурсів і станом світу. Сховище надає функцію subscribe() і знімок getSimRender(), який читає шар рендерингу.
Шар рендерингу (AsciiFlowersBackground.tsx) - React-компонент, що синхронізується зі сховищем кожні кілька тіків, а не щокадру. Він переводить стан симуляції в абсолютно позиціоновані DOM-елементи з ASCII-символами, кожен стилізований кольорами з урахуванням теми й тінями тексту для естетики «піксель-арт зустрічає термінал».
Це розділення означає, що симуляція продовжує працювати, навіть коли React не перерендерюється, а вартість рендеру лишається передбачуваною незалежно від того, наскільки складною стає логіка ШІ.
Процедурний ландшафт
Кожна сесія генерує унікальний світ за допомогою техніки під назвою фрактальний броунівський рух (FBM) - шаруватий шум, що дає природний на вигляд ландшафт.
Ідея проста: береш гладку випадкову функцію (value noise) і семплюєш її в кожній точці сітки. Кожна точка отримує число від 0 до 1 - свою «висоту». Низькі значення стають водою, середні - прохідною землею, високі - пагорбами.
Що робить FBM цікавим - це шаруватість. Замість одного гладкого проходу шуму ти складаєш кілька проходів (октав) із дедалі вищими частотами:
- Октава 1 дає широкі, розмашисті форми - континенти й океанські западини
- Октава 2 додає середню деталізацію - півострови й затоки
- Октава 3+ додає дрібну шорсткість - нерівність берегів і малі острови
Кожна наступна октава має менший вплив (керується персистентністю - тут установлено 0,35, тобто кожен шар має ~35% сили попереднього). Масштаб керує тим, наскільки шум наближений чи віддалений - малий масштаб = багато дрібних деталей, великий масштаб = кілька великих.
Результат - унікальна мапа висот для кожної сесії. Висота визначає все:
- Значення нижче 0,22 стають озерами, відрендереними ASCII-символами на основі глибини - глибша вода отримує щільніші гліфи, як
≈і≋, береги - світліші~і· - Середні значення стають прохідними луками з точковим візерунком
- Високі значення уповільнюють рух селян, імітуючи пагорби
Озера підтримують риб, що плавають уздовж берега, відбиваючись від країв і змінюючи напрямок. Висота ландшафту кешується на кожну плитку 8 пікселів, щоб не перераховувати шум щотіку.
ШІ селян
Кожен селянин керується скінченним автоматом на 18 станів. На кожному тіку незайняті селяни оцінюють світ і обирають найтерміновіше завдання:
- Їжі мало? → Іти на поле, почати обробку
- Дерева мало? → Знайти дерево, почати рубати
- Населення на межі? → Знайти місце під забудову, збудувати будинок
- Ресурсів удосталь? → Знайти пару, піти до будинку, розмножитися
- Нічого термінового? → Спілкуватися, відпочивати, збирати врожай чи досліджувати
Стани включають idle, moving_to_tree, chopping, moving_to_field, farming, moving_to_build, building, moving_to_house, waiting_at_house, in_house, socializing, resting, harvesting і exploring. На швидкість руху впливає нахил ландшафту - селяни сповільнюються, ідучи вгору.
Селяни живуть близько 2500 тіків (~8 хвилин). Коли населення падає, нові селяни з’являються, якщо досить їжі й житла. Колонія самопідтримується: вона росте, стабілізується й інколи занепадає, якщо ресурси вичерпуються - а тоді відбудовується.
Colony is full – need more housing to grow
Будівництво міста
Містечка виникають органічно. Коли населення сягає межі житла, селянин обирає місце поблизу наявних будинків (з випадковим зміщенням, щоб розкладки відчувалися природно, а не ідеально-сітково) і будує.
Будинки проходять три візуальні рівні покращення з часом і мають чотири кольорові варіанти на тему. Після появи п’яти будинків є шанс, що нові будівлі стануть особливими спорудами - ринком, вежею, церквою чи таверною - кожна зі своїм ASCII-артом.
Дороги автоматично генеруються між сусідніми будинками й рендеряться як пунктирні ASCII-шляхи, що з’єднують поселення.
Уночі будинки сяють теплим жовтим зі шаруватими тінями, перетворюючи містечко на скупчення крихітних освітлених вікон на тлі темного ландшафту.
House upgrade levels
Special buildings
Екосистема
Окрім селян і міст, світ населений фоновим життям:
Дерева мають чотири ASCII-форми й самовідтворюються - кожні 10 секунд кожне дерево має шанс породити нове поруч. На старті чотири лісові кластери засівають світ густими гаями.
Хмари пливуть небом, використовуючи процедурний шум для створення органічних форм із накладених плям, відрендерених ASCII-символами на основі щільності (█▓▒░·). Кожна хмара має унікальний силует.
Бджоли запилюють квіти й випускають ASCII-сердечка. Риби плавають озерами. Світляки з’являються лише вночі, сяючи зелено-золотим і дрейфуючи з фізичним блуканням. Метелики сідають на квіти й згасають за 40 секунд. Птахи інколи пролітають екраном.
День і ніч
Уся палітра зсувається залежно від перемикача теми сайту. У світлому режимі: яскраві зелені, теплі коричневі, яскраві кольори квітів. У темному режимі: глибокі лісові тони, прохолодні сині, місячні фіолетові й активні світляки. Кожна сутність - дерева, квіти, селяни, будинки, ландшафт, озера - має окремі денні й нічні набори кольорів.
Будинки отримують найдраматичніше оформлення вночі: тепле золотисте сяйво з трьома шарами тіні, що змушує містечко відчуватися заселеним.
Продуктивність
Безперервна симуляція зі 150+ DOM-елементами потребує ретельної оптимізації:
- Тротлінг синхронізації рендеру - шар React оновлюється кожні 8 тіків, а не щотіку
- Кешування ландшафту - пошук висот мемоїзується на кожну плитку 8 пікселів
- Кешування тіней - CSS-рядки text-shadow будуються раз на колір і повторно використовуються
- Мемоїзовані шари - трава, озера й статичні елементи перерендерюються лише тоді, коли змінюються дані
- Ліміти сутностей - жорсткі обмеження на селян (25), дерева (150), будинки (20), хмари (5)
- Пакетування інтервалів - створіння на кшталт світляків і риб оновлюються на довших інтервалах (350 мс, 800 мс)
Результат працює плавно на 60 кадрах/с на сучасному залізі, поки фонова симуляція цокає кожні 200 мс.
Інструменти гравця
Панель HUD угорі дає відвідувачам долучитися:
- Садити квіти - клікніть будь-де, щоб розсипати ASCII-цвіт
- Садити дерева - клікніть, щоб виростити дерево в цьому місці
- Створити людей - додайте нових селян до колонії
- Інспектувати - клікніть будь-яке створіння, щоб побачити процедурно згенеровану картку зі статистикою: ім’я, вид, вага, темперамент, рідкість і польові нотатки
- Взаємодіяти - повноцінний режим маніпуляцій, де кожна сутність у світі реагує на кліки
Є також Steevie - усміхнений талісман на панелі. Клікніть його, і він стане вашим курсором, рухаючись за мишею з желейною фізикою, виразними реакціями обличчя й кольорами з урахуванням теми. Подвійний клік відпускає його назад.
Режим взаємодії
Взаємодія - найбагатший на функції інструмент. Кожен тип сутності має унікальну реакцію:
Дерева потребують 3 кліки, щоб їх зрубати. Кожен клік показує напис «CHOP!» і відтворює звуковий ефект. На третій клік дерево відтворює анімацію падіння (нахил + згасання), видаляється із симуляції й дає +1 дерево.
Квіти вибухають сплеском частинок із власних пелюсток - 12 спрямованих частинок розлітаються з гравітацією, а тоді згасають.
Метелики «лякаються» - обирають випадковий кут утечі й мчать геть на високій швидкості, зрештою вилітаючи за екран.
Птахи теж лякаються й відлітають.
Риби відкривають міні-гру з риболовлею - найскладнішу взаємодію в симуляції.
Міні-гра з риболовлею
Клік по рибі в режимі взаємодії запускає повноцінну міні-гру з риболовлею. Це випробування на час і реакцію, побудоване цілком на requestAnimationFrame і прямій маніпуляції DOM (без перерендерів React під час гри для плавних 60 кадрів/с).
Як це працює
Вертикальна смуга позначає водяний стовп. Іконка риби (><>) хаотично стрибає - твоє завдання втримати зелену зону лову вирівняною з нею, клікаючи й утримуючи. Утримуй, щоб штовхати зону вгору, відпускай, щоб гравітація тягла її вниз. Зона відбивається від верхнього й нижнього країв.
Смуга прогресу наповнюється, коли риба всередині твоєї зони, і спорожнюється, коли поза нею. Наповни її до 100%, щоб упіймати рибу; дай їй спорожнитися до 0% - і риба втече.
Кожна риба має процедурно згенеровану рідкість (Звичайна, Незвичайна, Рідкісна, Легендарна), що визначає складність:
| Зона лову | Швидкість риби | Хаотичність | Приріст прогресу | Втрата прогресу | |
|---|---|---|---|---|---|
| Звичайна | 36% | Повільна | Низька | Швидкий | Повільна |
| Незвичайна | 32% | Середньо-низька | Низька | Швидкий | Повільна |
| Рідкісна | 33% | Середньо-низька | Низька | Швидкий | Повільна |
| Легендарна | 28% | Середня | Середня | Середній | Середня |
Легендарні риби отримують золоту іконку ★><>, особливий банер «★ Легендарний улов ★» і більший процедурно згенерований ASCII-арт на екрані лову.
Після впіймання риби ти бачиш її повну картку зі статистикою - процедурне ім’я, вид, розмір у см, вага, стать, темперамент, раціон і нотатки польового дослідника. ASCII-арт риби також генерується процедурно на основі розміру риби й хеш-сіду, з до 6 візуальних варіацій на категорію розміру.
Спробуй сам:
Що далі
Симуляція продовжує рости. Нові створіння, взаємодії й системи додаються регулярно - цю сторінку буде оновлено в міру еволюції світу.