Гра ніколи не створює головоломку вручну. Вона вирощує її з одного-єдиного числа, а потім розігрує цю головоломку сама проти себе, щоб переконатися, що та не є ані тривіальною, ані неможливою, перш ніж ви взагалі її побачите.
Написано й відредаговано англійською. Цю українську версію створено машинним перекладом; там, де важлива точність, авторитетним є англійський оригінал. Читати оригінал англійською →
Рівень Contraption ніде не зберігається. Немає файлу, в якому записано, де стоять ворота чи як розташовані виступи. Натомість увесь рівень — стартова позиція м’яча, положення воріт, кількість і розміщення нерухомих стін, набір деталей, які вам видають, — обчислюється з одного початкового числа, яке називають сідом (seed). Дайте генератору той самий сід — і ви отримаєте, байт у байт, ту саму головоломку. 1
Це можливо тому, що єдине джерело «випадковості» в генератора — невеликий детермінований генератор псевдовипадкових чисел. Contraption використовує mulberry32, компактний 32-бітний генератор, автор якого — Томмі Еттінгер; його можна впізнати за характерною константою 0x6d2b79f5, яка додається до стану при кожному витягуванні числа. 2 Він швидкий, вміщується в кілька рядків і, що найважливіше, зовсім не випадковий: це фіксована математична послідовність, яка лише виглядає перемішаною. Дайте йому стартове значення — і він вічно видаватиме один і той самий потік чисел. Потім генератор читає цей потік, щоб вирішити, де що розмістити.
Режим «Щодня» спирається на цю детермінованість, щоб зробити дещо соціальне. Його сід не випадковий — він виготовляється з календаря. Гра бере сьогоднішню дату за всесвітнім часом, записує її в короткий рядок на кшталт «contraption:2026-7-28» і проганяє цей текст через хеш-функцію, щоб уварити його до одного 32-бітного числа. 1
Хеш-функція — це FNV-1a, добре відомий некриптографічний змішувач: він починає з фіксованого зміщення, десяткового 2166136261, тобто 0x811c9dc5, і для кожного символу вплітає літеру через виключне АБО, а потім множить на просте число FNV 16777619. 3 У результаті виходить число, стабільне для певного дня і геть інше наступного, тож зміна одного символу в даті повністю розкидає сід. Оскільки календар для всіх однаковий, однаковим є і сід, а отже, й головоломка. Двоє незнайомців на протилежних кінцях світу відкривають «Щодня» й бачать перед собою ідентичне поле — саме це й робить порівняння результатів осмисленим.
Щоденний виклик чесний не тому, що сервер надсилає всім один і той самий рівень, а тому, що кожна копія гри незалежно переобчислює той самий рівень із тієї самої дати.
Випадкова геометрія — це просто; хороша випадкова геометрія — ні. Генератор, який просто розкидає ворота й кілька виступів, іноді видасть рівень, де м’яч, скинутий згори, сам собою падає прямісінько у ворота. Це не головоломка, це кат-сцена. Тож перш ніж Contraption прийме світ-кандидат, він проводить тихий тест, який у коді називається preSolved: симулює рівень без жодної розміщеної деталі й перевіряє, чи досягає голий м’яч воріт лише під дією гравітації. Якщо так, рівень відкидається, і генератор кидає знову. 1
Це тонка інверсія звичного погляду на фізичний рушій. Та сама функція simulate(), яка розігрує вашу машину, повертається всередину, на етапі генерації, щоб проревізувати складність самого рівня. Рушій одночасно і суддя вашого розв’язку, і контролер якості головоломки.
Відкинути надто легкий рівень — лише половина справи. Інша небезпека — рівень неможливий, де ворота заховані там, куди не дістане жодне розташування деталей. Contraption страхується від цього невеликим розв’язувачем грубої сили на ім’я plausiblySolvable. Він не мудрує. Він просто пробує. 1
А саме: він проводить одну рампу по сітці позицій над полем і в кожній точці пробує її з чотирма різними нахилами. Для кожного з цих пробних розміщень він запускає повну симуляцію й запитує: чи довела ця одна рампа м’яч додому? Щойно будь-яке розміщення спрацьовує, рівень оголошується розв’язуваним і відправляється вам. Якщо після повного проходу не спрацювало жодне, генератор воліє відкинути рівень і спробувати новий сід.
| Параметр проходу | Діапазон | Що означає |
|---|---|---|
| Рампа X | 15 → 85, крок 14 | стовпці впоперек поля |
| Рампа Y | 30 → 120, крок 18 | рядки вниз по полю |
| Випробувані кути | 4 | ±0.45 і ±0.8 радіана |
| Тест на кожне розміщення | 1 повна симуляція | чи досягає м’яч воріт? |
Варто чесно сказати, що саме це доводить, — і код теж чесний щодо цього: коментар називає це «дешевою перевіркою на правдоподібність … не вичерпною». Рівень, який пройшов, напевно має принаймні один грубий розв’язок з однією рампою. Рівень, який не пройшов, усе ще може бути розв’язуваним за допомогою вентилятора, механізму чи хитрішої рампи, якої сітка ніколи не пробувала, тому генератор не вважає провал доказом неможливості. Він зберігає перший знайдений нетривіальний світ як запасний варіант, і якщо сорок спроб минають без чистого розв’язку однією деталлю, він відправляє цей запасний варіант замість того, щоб зациклюватися назавжди. Результат — прагматична гарантія: ніколи не буде дармової перемоги, майже завжди є справжній шлях усередину, і ніколи не буде нескінченного очікування «ідеального» рівня. 1
Відступіть на крок — і генератор рівнів Contraption виявиться чистим прикладом дуже старого й дуже загального шаблону розв’язання задач: генерувати й перевіряти. Запропонуйте кандидата навмання; перевірте його на відповідність вимогам; залиште, якщо пройшов; відкиньте й запропонуйте нового, якщо ні. Це цикл, що стоїть за процедурним контентом у незліченних іграх, за значною частиною еволюційних обчислень і, якщо примружитися, за самим природним добором: мутація пропонує, середовище перевіряє, ті, хто вижив, залишаються. 4
Що робить версію Contraption елегантною, так це те, що тест — не дешевий сурогат реальної гри, він і є реальна гра. Генератор не оцінює, чи чесний рівень, рахуючи виступи чи вимірюючи відстані. Він буквально грає в рівень, двічі: один раз порожнім, щоб переконатися, що це не подарунок, і один раз із пробною рампою, щоб переконатися, що шлях існує. Та сама детермінована фізика, яка судитиме ваш розв’язок, — це фізика, яка перевірила головоломку. Немає окремого «валідатора рівнів», який міг би розійтися зі справжніми правилами, бо набір правил лише один, і він використовується для всього.
А оскільки все це детерміноване, ревізія вільна від удачі в обидва боки. Щоденний сід не може змовитися, щоб дати одному гравцеві легше поле, ніж іншому, і не може нікому дати нечесне поле, бо перш ніж рівень стає викликом, він уже пережив те, що в нього зіграла сама гра — проти себе.