П’ятничний JS: гра в 0 рядків JS і CSS

Можливо, багато хто з старожилів пам’ятає епідемію статей із заголовками типу “%something% до 30 рядків JS”. А також наступну за нею епічність пост “Гра в 0 рядків коду на чистому JS”, після якого епідемія різко зійшла на немає. Повністю усвідомлюючи, що цей шедевр мені ніколи не перевершити, я все ж через п’ять років вирішив докинути свої п’ять копійок.

Пані та панове, до вашої уваги пропонується гра «Хрестики-нулики» в нуль рядків JS, а також, на відміну від гри, згаданої вище, в нуль рядків CSS (включаючи інлайн стилі). Тільки голий HTML, тільки хардкор.


→ Посилання на гру

Виглядає незграбно, зате буде працювати в будь-якому браузері. Під катом я розповім, чому гра без JS опинилася в рубриці «П’ятничний JS», а також інші брудні подробиці. Втім, Америку я нікому не відкрию, якщо ви досвідчений кодер, можете під кат навіть не заходити

Власне, все влаштовано дуже тупо. Гра складається з майже 6 тисяч сторінок статичного HTML, посилаються один на одного. При тыке на клітку ігрового поля відбувається перехід на сторінку, де хід в цю клітку вже зроблено. Очевидно, писати 6к сторінок руками — задоволення нижче середнього. Тому (сюрприз!) сторінки генеруються JS-скриптами з допомогою NodeJS.

Ліричний відступНаписавши попередню сходинку, я раптом задумався, чи не є вираз «JS-скрипт» тавтологією, як «CD-диск» або «VIP-персона». З одного боку, ніби як є. З іншого, JS — це все-таки не абревіатура, а скорочення дещо іншої природи. Однак пост все-таки не про філологію, бо ліричний відступ закінчується і починається лірична атака.

Спочатку ми будуємо так зване дерево гри — сукупність всіх можливих ігрових станів і переходів між ними. Початковий стан гри в моєму коді представлено наступним чином:

const initialState = {
 player: PLAYER_X,
 field: Array.from(Array(9)).map(() => EMPTY_CELL),
 moves: {}
}

У ньому міститься інформація про те, чий зараз хід і який стан ігрового поля. У майбутньому В ньому також з’явиться інформація про те, які кроки можуть бути зроблені і до яких станів вони приведуть, а також деякі інші приємні речі.

Читайте також  Підводний «GPS» на два прийомопередавача

Далі ми починаємо, прошу вибачення за тавтологію, з початкового стану і робимо наступне:

  1. Перевіряємо, чи не є стан термінальним (перемога хрестиків, перемога нуликів, нічия).
  2. Якщо так, додаємо інформацію про це в об’єкт стану і закінчуємо.
  3. Якщо ні, проходимся по всім клітинам поля.
  4. Для кожної порожній клітини поля створюємо новий стан гри, в якому поточний гравець зробив хід у цю клітину, а хід перейшов до наступного гравця.
  5. У полі moves поточного стану додаємо запис про можливий час. Ключем до цього запису служить індекс клітини, а значенням — посилання на новий стан.
  6. Повторюємо цей алгоритм рекурсивно для всіх знов з’явилися станів.

Насправді мій код трохи складніше, я за звичкою розгорнув рекурсію в цикл, а замість посилань на інші стани в moves зберігаються їх рядкові ключі в одному асоціативному масиві. Але це все деталі.

Потім з кожного об’єкта ігрового стану ми генеруємо HTML-сторінку. Пройшовшись по об’єкту moves, ми заповнюємо порожні клітини поля посиланнями на сторінки, відповідні ходів, зроблених в ці клітини. Потім перетворюємо одновимірний масив поля у двовимірну HTML-таблиці. Додаємо всякі приємні дрібниці на зразок вказівки, який гравець ходить, і посилання на початкову сторінку — і вуаля!

Крім режиму, коли і хрестики і нулики ставляться людиною, в моєму мега-інді-хіті є також можливість грати проти залізного мозку. Досягається це наступним чином:

  1. Спочатку рекурсивно (насправді немає) для кожного ігрового стану обчислюється очікуваний результат гри — той, який буде досягнутий, якщо обидві сторони будуть грати ідеально.
  2. Потім дерево гри модифікується таким чином: замість ходу гравця ми тепер робимо відразу два ходи. Другий хід — хід штучного інтелекту. При цьому з усіх можливих відповідей на хід гравця вибирається той, у якого найкращий очікуваний результат. Таким чином, тицьнувши на порожню клітку, гравець одразу переходить на позицію, де в цій клітці з’явився хрестик (або нулик), а в якійсь іншій з’явився нулик (або хрестик).
  3. Всі ігрові позиції, відповідні ходах, які ІЇ не робить, безжально відкидаються.
  4. Потім з решти позицій в окрему папочку генерується HTML — абсолютно аналогічно нагоди двох гравців.
Читайте також  Про систему блюра - Laplace Blur

За схожими принципами можна реалізувати будь-яку гру з не дуже великим деревом. Втім, якщо я захочу подібним чином зробити шахи, мені здається, гитхаб відмовиться це хостити =)

До речі про гітхабі: можете подивитися там код (посилання є на головній сторінці гри). На цьому, загалом-то, все. До побачення, дівчатка і хлопчики. До нових зустрічей.

P. S. Заміна переносів рядки з Windows-style на Unix-style — це дуже довго, коли мова йде про 6 тисяч файлів. Я пошкодував, що не подбав про це на етапі написання коду, але все-таки мужньо дотерпів до кінця git add.

Степан Лютий

Обожнюю технології в сучасному світі. Хоча частенько і замислююся над тим, як далеко вони нас заведуть. Не те, щоб я прям і знаюся на ядрах, пікселях, коллайдерах і інших парсеках. Просто приходжу в захват від того, що може в творчому пориві вигадати людський розум.

You may also like...

Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *