Как команда разработчиков, которая каждый день работает с JavaScript, мы научились пользоваться преимуществами неизменяемых структур данных. Неизменяемость уменьшает побочные эффекты в наших приложениях и значительно упрощает отладку. Наш код становится намного проще для понимания и обслуживания.

Затем однажды нам поставили задачу создать игру с использованием JavaScript.

Все, что мы знаем и любим о неизменяемых структурах данных, вылетает в окно. Когда у вас есть сложное игровое состояние с сотнями объектов, создание полной копии игрового состояния при каждой итерации игрового цикла становится нецелесообразным.

Состояние игры как неизменяемая структура данных

Рассмотрим игру крестики-нолики. У нас есть сетка 3 x 3, представленная массивом с именем «сетка» длины 9.

Когда игрок X делает ход, мы выполняем следующую функцию:

рисоватьX(сетка, 4);

Если бы мы придерживались неизменной структуры данных, нам пришлось бы делать совершенно новую копию сетки при каждом перемещении. В этом случае вычислительная сложность составляет O(n).

Если мы отказываемся от неизменяемой структуры данных, нам просто нужно обновить сетку с индексом 4, что имеет вычислительную сложность O (1).

Это не так уж плохо с простой игрой в крестики-нолики. Влияние на производительность использования неизменяемой структуры данных незначительно, и мы даже можем реализовать функцию «отмены» с очень небольшими дополнительными усилиями.

Неизменяемость нецелесообразна, когда нам нужно часто обновлять сложное состояние игры

Что, если у нас будет более сложная игра, такая как Pac-Man? Игровое поле Pac-Man можно представить в виде двухмерной сетки, но в отличие от Tic-Tac-Toe у нас есть персонажи и элементы, которые постоянно меняются.

Для реализации Pac-Man нам нужен игровой цикл. Игровой цикл — это непрерывно работающий цикл, который обрабатывает несколько игровых событий и обновляет состояние игры при каждой итерации — мы называем такую ​​итерацию «тиком». Игра должна будет запускать дюжину тиков в секунду, иначе у нас будет очень медленная игра.

С игровым циклом становится очевидным, что мы больше не можем создавать совершенно новую копию состояния игры при каждом добавочном обновлении игровых объектов. Например, когда нам нужно всего лишь увеличить позицию призрака по оси x на 1, гораздо эффективнее просто обновить свойство объекта-призрака, чем клонировать все состояние игры.

С учетом игрового цикла неизменяемое игровое состояние создает сложность O(n²). Игра работает как слайд-шоу в браузере.

Когда мы можем изменить состояние игры, сложность становится O(n), и мы получаем приятный игровой опыт.