Дамы и господа, вашему вниманию предлагается игра «Крестики-нолики» в нуль строк JS, а также, в отличие от игры, упомянутой выше, в нуль строк CSS (включая инлайн стили). Только голый HTML, только хардкор.
> Ссылка на игру
Выглядит неказисто, зато будет работать в любом браузере. Под катом я расскажу, почему игра без JS оказалась в рубрике «Пятничный JS», а также другие грязные подробности. Впрочем, Америку я никому не открою, если вы опытный кодер, можете под кат даже не заходить
Собственно, всё устроено очень тупо. Игра состоит из почти 6 тысяч страниц статического HTML, ссылающихся друг на друга. При тыке на клетку игрового поля происходит переход на страницу, где ход в эту клетку уже сделан. Очевидно, писать 6к страниц руками — удовольствие ниже среднего. Поэтому (сюрприз!) страницы генерируются JS-скриптами с помощью NodeJS.
Сначала мы строим так называемое дерево игры — совокупность всех возможных игровых состояний и переходов между ними. Начальное состояние игры в моём коде представлено следующим образом:
const initialState = {
player: PLAYER_X,
field: Array.from(Array(9)).map(() => EMPTY_CELL),
moves: {}
}
В нём содержится информация о том, чей сейчас ход и каково состояние игрового поля. В будущем в нём также явится информация о том, какие ходы могут быть сделаны и к каким состояниям они приведут, а также некоторые другие приятные вещи.
Дальше мы начинаем, прошу прощения за тавтологию, с начального состояния и делаем следующее:
- Проверяем, не является ли состояние терминальным (победа крестиков, победа ноликов, ничья).
- Если да, добавляем информацию об этом в объект состояния и заканчиваем.
- Если нет, проходимся по всем клеткам поля.
- Для каждой пустой клетки поля создаём новое состояние игры, в котором текущий игрок сделал ход в эту клетку, а ход перешёл к следующему игроку.
- В поле
moves
текущего состояния добавляем запись о возможном ходе. Ключом в этой записи служит индекс клетки, а значением — ссылка на новое состояние. - Повторяем этот алгоритм рекурсивно для всех вновь появившихся состояний.
На самом деле мой код чуть сложнее, я по привычке развернул рекурсию в цикл, а вместо ссылок на другие состояния в
moves
хранятся их строковые ключи в некоем ассоциативном массиве. Но это всё детали.Затем из каждого объекта игрового состояния мы генерируем HTML-страницу. Пройдясь по объекту
moves
, мы заполняем пустые клетки поля ссылками на страницы, соответствующие ходам, сделанным в эти клетки. Затем превращаем одномерный массив поля в двумерную HTML-таблицу. Добавляем всякие приятные мелочи вроде указания, который игрок ходит, и ссылки на начальную страницу — и вуаля!Кроме режима, когда и крестики, и нолики ставятся человеком, в моём мега-инди-хите есть также возможность играть против железного мозга. Достигается это следующим образом:
- Сначала рекурсивно (на самом деле нет) для каждого игрового состояния вычисляется ожидаемый результат игры — тот, который будет достигнут, если обе стороны будут играть идеально.
- Затем дерево игры модифицируется следующим образом: вместо хода игрока мы теперь делаем сразу два хода. Второй ход — ход искусственного интеллекта. При этом из всех возможных ответов на ход игрока выбирается тот, у которого самый лучший ожидаемый результат. Таким образом, тыкнув на пустую клетку, игрок сразу переходит на позицию, где в этой клетке появился крестик (или нолик), а в какой-то другой появился нолик (или крестик).
- Все игровые позиции, соответствующие ходам, которые ИИ не делает, безжалостно отбрасываются.
- Затем из оставшихся позиций в отдельную папочку генерится HTML — абсолютно аналогично случаю двух игроков.
По сходным принципам можно реализовать любую игру с не очень большим деревом. Впрочем, если я захочу подобным образом сделать шахматы, мне кажется, гитхаб откажется это хостить =)
Кстати о гитхабе: можете посмотреть там код целиком (ссылка присутствует на заглавной странице игры). На этом, в общем-то, всё. До свидания, девочки и мальчики. До новых встреч.
P.S. Замена переносов строки с Windows-style на Unix-style — это очень долго, когда речь идёт о 6 тысячах файлов. Я пожалел, что не позаботился об этом на этапе написания кода, но всё-таки мужественно дотерпел до конца
git add
. Комментарии (26)
GRaAL
03.08.2018 14:48+1Я в детстве так и писал крестики-нолики на QBasic.
If (игрок1 поставили крестик слева-вверху) then рисуем крестик слева-вверху if (игрок2 поставил нолик справа-снизу) then ... end if end if
Я был тогда очень целеустремлен и усидчив.
dmitryredkin
03.08.2018 22:12+2Пффф!
Вы так говорите, как будто это что-то новое.
В начале 2000-х на телефонах фирмы Siemens ещё не было J2ME, а играть уже хотелось.
Вот тогда-то я узнал, что такое WML-games.
Были среди них и довольно увлекательные, например — "быки и коровы"dmitryredkin
03.08.2018 22:33+1поправка: wmlc-игры.
З.Ы. А еще, благодаря самописному скрипту text2wmlc я и книжки на нем умудрялся читать… Эх, было времечко!SopaXT
03.08.2018 22:46А можно поподробнее про WMLC? Как на нём писали и т.п.
Нигде не могу найти толкового описания функционала WAP.dmitryredkin
05.08.2018 21:19Боюсь, не смогу вам ничем помочь.
Очень уж давно все было… Но форум сименс-клуба оказывается до сих пор жив!
Возможно, там что-то удастся найти…neit_kas
06.08.2018 05:00Заброшенный такой, мхом поросший. Воочию наблюдаешь, как выглядит вроде бы недавняя, но уже история.
Sirion Автор
04.08.2018 00:15Ну, я таки не очень претендовал на новизну) Более того, ещё в две тысячи лохматом году я делал что-то похожее даже не на HTML, а на BB-кодах на каком-то блогосервисе.
Gribs
04.08.2018 01:27Аналогичным образом можно сделать некое подобие калькулятора на HTML. Что безусловно докажет остальным что HTML — полновесный язык программирования.
klubben
В детстве я был уверен что игры так и делаются, художники сидят и рисуют все возможные варианты кадров «Марио»
Sirion Автор
Не помню, что я думал по поводу видеоигр, но в юности у меня было сходное заблуждение относительно видео. Я думал, что там хранятся все кадры в несжатом виде. Потом додумался, что можно использовать дельта-кодирование и уменьшить размер видеофайлы в разы. Предложил своей одноуниверситетнице, сведущей в кодинге, организовать, как бы сейчас сказали, стартап. Помню, она надо мной долго смеялась.
rjhdby
Помнится в далеком детстве на голом .bat делал книгу-игру. Все как полагается, одна страница — один .bat файл :)
Sirion Автор
батники хотя бы аргументы могут друг другу передавать)
Alexey2005
Объёмы современных игр растут так быстро, что скоро уже вариант «отрендерить каждую сцену в каждом ракурсе» будет весить меньше, нежели всё это раздутое нагромождение нормально-текстурных карт. 360 кадров на точку (рендер с поворотом на 1 градус), расстояние между точками равно одному шагу персонажа. Всё это пожать с помощью какого-нибудь H.264.
И не только весить будет меньше, но и ещё заработает даже на нетбуках, не требуя GeForce 1080Ti для каких-то жалких 30 FPS.