Игра Морской бой на JavaScript. Выстрел компьютера.

Вступление.

Заключительная статья из цикла «Игра Морской бой на чистом JavaScript». В этой статье мы рассмотрим:

  1. Выбор координат для выстрела компьютера.
  2. Обработка результата выстрела.
  3. Определение гарантированно пустых клеток, в которых не может быть кораблей противника.
  4. Вычисление координат следующего выстрела после попадания.
  5. Анализ количества оставшихся кораблей противника (игрока) и их тип.

Для реализации всех выше перечисленных действий будем использовать Искусственный Интеллект (ИИ) или, другими словами, некий гибкий алгоритм действий, меняющийся в зависимости от обстановки на игровом поле.

Внимание.
Перед изучением JavaScript, реализующего выстрел компьютера, необходимо ознакомится со статьёй «Игра Морской бой на JavaScript. Выстрел игрока.». Ряд функций используется как для выстрела игрока, так и для выстрела компьютера и в данной статье повторно рассматриваться не будут.

Игра «Морской бой». Оптимальная тактика ведения морского боя компьютера.

Для того, чтобы компьютер мог, как минимум, играть на равных с человеком, он (компьютер) должен иметь некую тактику игры. Причём, данная тактика должна быть оптимальна применительно к игре «Морской бой» и обеспечивать высокие шансы на победу над человеком.
Исходя из этого, сразу же откажемся от рандомного обстрела поля игрока в начальной стадии игры. В рандомном формировании координат выстрела компьютера в полной мере присутствует элемент случайности, а нам, для победы компьютера над человеком, нужно свести этот элемент к минимуму.
В чём же будет заключаться оптимальная тактика игры? А в том, что компьютер будет методично обстреливать поле игрока по определённому алгоритму. Посмотрите внимательно на рисунок.

Игра Морской бой. Выбор координат для выстрела компьютера.

Заштрихованные клетки отображают координаты выстрелов компьютера. При такой тактике, компьютер максимум за 24 выстрела попадёт в четырёхпалубный корабль, и это в худшем варианте. В реальности, будут попадания и в другие корабли. Конечно, если игрок знает про такую тактику, то он может расположить все корабли, за исключением четырёх палубного, в клетках, которые не подвергнутся обстрелу.
Давайте усложним задачу и изменим наклон диагоналей, по которым будет вестись стрельба, и сведём полученные координаты в единый массив.

Игра Морской бой. Оптимальные координаты выстрела компьютера.

После такого обстрела, добить оставшиеся корабли игрока не составит труда, т. к. необстрелянных клеток, где может располагаться корабль, практически не останется.

Игра «Морской бой». Инициализация ведения морского боя компьютером.

Для формирования и хранения координат выстрела компьютера нам понадобится несколько несколько массивов и объектов, являющимися свойствами экземпляра comp конструктора Field. Рассмотрим подробно их название и назначение:

  1. comp.shootMatrixAI — массив с заранее сгенерированными координатами выстрелов.

  2. comp.shootMatrix — данный массив координат будет использован для рандомного обстрела, после того, как будут использованы все координаты из массива comp.shootMatrixAI. Изначально в данном массиве содержаться координаты всех клеток игрового поля. При каждом выстреле компьютера, координата выстрела будет удаляться из данного массива. Так же будут удаляться и координаты гарантированно пустых клеток. В результате, после того, как будут использованы все значения массива comp.shootMatrixAI, в массиве comp.shootMatrix останутся лишь те координаты, где реально может быть расположен корабль игрока. Вот эти оставшиеся координаты и будут использоваться для следующего выстрела.

  3. comp.shootMatrixAround — в данный массив будут записаны координаты клеток, расположенных вокруг попадания.

  4. comp.startPoints — служебный массив, в котором находятся координаты начала диагоналей. Эти данные используется для формирования координат, которые будут записываться в массив comp.shootMatrixAI.

  5. comp.tempShip — временный объект для хранения первого и второго попадания (данная информация необходима для определения положения корабля), координаты первой палубы и количество попаданий.

Теперь инициализируем эти массивы и объекты. Делать это будем в свойстве init объекта battle, который находится в модуле Controller.

Внимание.
Более подробно о модуле Controller и его объекте battle изложено в статье «Игра Морской бой на JavaScript. Редактирование положения корабля и начало игры.»

Добавим следующий JS-код в свойство init:

Игра «Морской бой». Формирование массивов с координатами выстрела компьютера.

Для заполнения массивов shootMatrixAI и shootMatrix координатами выстрелов запускается функция setShootMatrix, являющаяся методом объекта battle и использующая в своей работе данные массива startPoints. Прежде чем рассматривать работу данной функции, давайте сначала более подробно разберём содержание массива startPoints.

Массив startPoints содержит в себе всего лишь два элемента, каждый из которых, в свою очередь, так же является массивом. Первый массив содержит координаты начальных точек четырёх диагоналей направленных вправо-вниз. На рисунке они изображены красным цветом. Второй — координаты начальных точек четырёх диагоналей направленных вправо-вверх. На рисунке они отображены синим цветом.

Добавим в конец объекта battle следующий JS-код:

Игра «Морской бой». Получение координат для выстрела компьютера.

Добавим в конец свойства init JS-код, который выводит сообщение о том, что первым стреляет компьютер и после небольшой задержки, запускающий первый выстрел компьютера в игре «Морской бой»:

Координаты очередного выстрела компьютера будем получать, в зависимости от игровой обстановки, в одном из трёх массивов: shootMatrixAround, shootMatrixAI или shootMatrix.

В-первую очередь обращаемся к массиву shootMatrixAround в котором хранятся координаты выстрелов для обстрела клетки с попаданием, получаем его элемент и считываем из него координаты выстрела.
Если попадания ещё не было или координаты, хранившиеся в данном массиве, уже использованы для предыдущих выстрелов, т. е. массив shootMatrixAround в данный момент пустой, то обращаемся к массиву shootMatrixAI и получаем элемент с координатами из него.
Если и массив shootMatrixAI пустой (все диагонали обстреляны), то координаты выстрела берём из массива shootMatrix.
Данные из массивов получаем используя метод pop. Данный метод удаляет последний элемент из массива и возвращает его.

В предыдущей статье мы рассматривали работу функции shoot применительно к выстрелу игрока. Теперь рассмотрим JS-код, который относится к выстрелу компьютера:

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

Как видно из приведённого кода, у нас появилась ещё одна новая функция — deleteElementMatrix, с помощью которой можно удалить элемент массива содержащий определённые координаты. Данная функции имеет два аргумента:
— массив, из которого нужно удалить элемент;
— объект с координатами выстрела.

Рассмотрим подробно код функции:

Итак, координаты для выстрела получены. Теперь рассмотрим сам выстрел и оценку его результатов.

Игра «Морской бой». Выстрел компьютера и оценка его результата.

Внимание.
Ещё раз настоятельно рекомендую ознакомится со статьёй «Игра Морской бой на JavaScript. Выстрел игрока.», в частности, с разделом «Игра «Морской бой». Обработка результатов выстрела.»

Выстрел представляет из себя извлечение значения двумерного массива игрового поля противника (user.matrix) по координатам, взятым из одного из трёх массивов: shootMatrixAround, shootMatrixAI или shootMatrix. Для обработки полученного значения будем использовать конструкцию switch, аргументом которой является переменная val.

Игра «Морской бой». Промах.

Промах подробно я рассматривать не буду, т. к. JS-код в этом случае является практически общим, как для игрока, так и для компьютера. Всё отличие в устанавливаемых и удаляемых обработчиках событий. Всё это описано в предыдущей статье. Единственное, что я сделаю — это добавлю несколько строчек кода, которые сбрасывают свойства объекта comp.tempShip в исходное состояние, если массив shootMatrixAround пустой.
Этот сброс имеет значение в том случае, если происходит обстрел вокруг попадания и, если все координаты из массива использованы, значит корабль потоплен.

У нас появилась новая функция — resetTempShip, которая приводит все свойства объекта tempShip в исходные значения, которые были установлены при инициализации морского боя.

Если вы обратили внимание, то код этой функции, кроме одной строчки, совпадает с инициализацией обекта tempShip в функции init. Логично будет заменить код инициализации, вызовом функции resetTempShip.

Игра «Морской бой». Попадание в корабль игрока.

JS-код обработки попадания в корабль игрока во многом совпадает с JS-кодом обработки попадания в корабль компьютера, но есть существенные дополнения, касающиеся ИИ компьютера.

Алгоритм обработка результата попадания в корабль игрока:

  • 1

    Визуально отображаем попадание и записываем в матрицу игрового поля игрока по этим координатам значение 4. Выводим сообщение о попадании.

  • 2

    Перебираем массив эскадры игрока — user.squadron, в котором храниться информация о каждом корабле, и определяем корабль в который произошло попадание. Увеличиваем счётчик попадания на 1. Если количество попаданий в корабль становится равным количеству палуб, считаем этот корабль уничтоженным и удаляем его из эскадры, но перед этим сохраняем координаты первой палубы удаляемого корабля в объект comp.tempShip. Эти координаты понадобятся для отметки клеток по краям корабля.

  • 3

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

  • 4

    Увеличиваем счётчик попаданий, являющийся свойством объекта comp.tempShip и отмечаем гарантированно пустые клетки вокруг попадания.

  • 5

    Проверяем, остались ли в эскадре игрока корабли, количество палуб которых больше, чем значение сётчика попаданий. Если таких кораблей не найдено, то:

    1. считаем, что обстреливаемый корабль потоплен;
    2. помечаем клетки вокруг корабля, как гарантированно пустые;
    3. сбрасываем значения свойств объекта comp.tempShip в исходное состояние;
    4. переходим к следующему выстрелу по игровому полю игрока.
  • 6

    Если найдены корабли, у которых количество палуб больше, чем количество попаданий, то продолжаем уничтожение текущего корабля путём обстрела вокруг палубы, в которую было сделано попадание.

Игра «Морской бой». Сохраняем координаты первой палубы потопленного корабля.

На первом и втором пунктах приведённого алгоритма я останавливаться не буду, т. к. JS-код используется тот же самый, что и при выстреле игрока. Рассмотрим только сохранение координат первой палубы корабля в объект comp.tempShip. Напомню, что каждый элемент массива squadron представляет из себя объект (ассоциативный массив), в котором хранится вся информация по кораблю. В частности, координаты первой палубы находятся в свойствах x0 и y0.

Игра «Морской бой». Выводим корабли компьютера на экран.

Внимание.
Для лучшего понимания ниже изложенного материала, рекомендую ещё раз прочитать раздел «Игра Морской бой. Вывод корабля на экран монитора» в статье «Игра Морской бой на JavaScript. Рандомная расстановка кораблей.»

Для вывода оставшихся кораблей компьютера, в случае его победы, необходимо перебрать в цикле массив comp.squadron. При каждой итерации будет создаваться элемент DIV, к которому присвоим класс и применим стили на основе информации, находящейся в объекте, являющемся элементом массива comp.squadron.

Обновлённый и дополненный JS-код обработки попадания при выстреле компьютера:

Игра «Морской бой». Отмечаем гарантированно пустые клетки вокруг попадания.

После каждого удачного выстрела ИИ компьютера определяет координаты клеток, находящихся по диагонали от клетки с попаданием. Таких клеток может быть от одной до четырёх, в зависимости от координат попадания — в центре, на краю или в углу игрового поля.

Игра Морской бой. Гарантированно пустые клетки.

Полученные координаты передаются в качестве аргумента в функцию markEmptyCell.

Обновлённый и дополненный JS-код обработки попадания при выстреле компьютера:

Как вы обратили внимание, у нас появилась новая функция — markEmptyCell, которая выполняет следующие задачи:

  1. Перебирает полученный массив и удаляет из него координаты по которым уже установлены ранее отметки пустых клеток, попаданий или промахов, а так же, если эти координаты оказываются за пределами игрового поля игрока.
  2. Записывает по этим координатам в двумерный массив игрового поля игрока значение ‘2’, что соответствует отметке пустой клетки.
  3. Вызывает функцию showIcons, для визуального отображения гарантированно пустых клеток на игровом поле игрока.
  4. Удаляет эти координаты из массивов shootMatrixAround, shootMatrixAI и shootMatrix, чтобы исключить в дальнейшем стрельбу по однозначно пустым клеткам.

Ниже представлен полный JS-код функции markEmptyCell:

Игра «Морской бой». Проверяем, потоплен ли корабль.

На первый взгляд, реализация этой проверки не должна вызвать трудностей. Достаточно сравнить в массиве user.squadron значения свойств decks (количество палуб) и hits (количество попаданий) обстреливаемого корабля. Если эти значения равны, то корабль считается потопленным. К сожалению, при таком подходе мы поставим игрока в заведомо неравное положение, ведь он обстреливает корабль до тех пор, пока явно не будет видно, что корабль потоплен. Откажемся от этого способа и научим компьютер самостоятельно определять необходимость дальнейшего обстрела раненого корабля — потоплен корабль или нет.

Вариант с использованием ИИ компьютера тоже достаточно прост. Для его реализации достаточно:

  1. Найти, из оставшихся кораблей в эскадре игрока, корабль с максимальным количеством палуб;
  2. Сравнить полученное значение с количеством попаданий, которое хранится свойстве comp.tempShip.totalHit.

ИИ должен анализировать результаты после каждого попадания, чтобы не делать лишних выстрелов. Например, если уничтожены две палубы, необходимо проверить, остались ли не потопленными трёхпалубные и четырёх палубный (если трёхпалубные все потоплены) корабли

Для удобства чтения, будем писать код проверки отдельно, а потом уже целиком вставим его в ветвь case 1:

Рассмотрим код функции checkMaxDecks, возвращающей максимальное количество палуб:

Теперь нужно сформировать массив points, являющийся аргументом функции markEmptyCell. Для этого нам понадобятся данные записанные в объект comp.tempShip:

  1. количество попаданий в корабль;
  2. координаты первой палубы обстреливаемого корабля;
  3. направление расположения палуб (горизонтальное или вертикальное).

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

Игра Морской бой. Гарантированно пустые клетки вокруг потопленного корабля.

Варианты расположения гарантированно пустых клеток
в зависимости от положения потопленного корабля.

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

Добавим полученный JS-код в раздел обработки попадания case 1

Игра «Морской бой». Обстрел игрового поля вокруг попадания.

Если корабль, в который было попадание, не потоплен, компьютер должен продолжить его обстрел, а именно, обстрелять клетку вокруг попадания. Для этого используется функция setShootMatrixAround.

Функция setShootMatrixAround является основной частью ИИ компьютера и способна менять алгоритм формирования координат в зависимости от результатов обстрела «раненого» корабля. Кроме этого, функция способна определить, когда следует прекратить обстрел «раненного» корабля, посчитав его уничтоженным.
В своей работе функция использует информацию хранящуюся в объектах coords и comp.tempShip. Эти объекты являются глобальными в пределах области видимости модуля Controller, поэтому нет необходимости передавать их явно, в качестве аргументов. Все возможные координаты обстрела клеток вокруг попадания записываются в массив shootMatrixAround.

После первого попадания в корабль, ИИ не может вычислить, как расположен этот корабль — горизонтально или вертикально. Поэтому ему необходимо обстрелять клетку с попаданием со всех сторон: сверху, снизу, слева и справа. Количество точек обстрела может варьироваться от двух до четырёх, в зависимости от координат попадания.

Игра Морской бой. Обстрел клетки с попаданием.

В данном случае, точками обозначены возможные координаты выстрелов.

После второго попадания ИИ уже может определить направление расположения корабля — вертикальное или горизонтальное и исключить лишние координаты для последующего обстрела, заменив их отметками гарантировано пустых клеток.

Игра Морской бой. Координаты обстрела корабля.

В данном случае, точками обозначены возможные координаты выстрелов.

Начнём с того, что разберёмся, как ИИ вычисляет направление расположения палуб, используя координаты первого и второго попадания.
Данные вычисления запускаются, если значения коэффициентов kx и ky, хранящихся в объекте comp.tempShip равны 0.

Ещё раз напомню значение комбинаций коэффициентов:

  1. comp.tempShip.kx == 1 && comp.tempShip.ky == 0 — корабль расположен вертикально;
  2. comp.tempShip.kx == 0 && comp.tempShip.ky == 1 — корабль расположен горизонтально.

Рассмотрим JS-код, отвечающий за вычисление координат выстрелов и использующий при этом значения коэффициентов kx и ky:

Очень важно.
В условии коэффициенты kx и ky сравниваются с 0, а не с 1. Это сделано для универсальности вычислений как для первого попадания, так и для последующих, т. к. при первом попадании оба коэффициента равны 0.

Получив координаты обстрела попадания, необходимо проверить их валидность. Координата валидна, если значение двумерного массива игрового поля игрока не равно или 2 (гарантированно пустая клетка), или 3 (промах), или 4 (попадание).
Перебирать массив shootMatrixAround будем с конца, чтобы после удаления элемента массива не нарушился порядок дальнейшего перебора.

Теперь необходимо проверить, остались после валидации элементы в массиве shootMatrixAround или нет. Если массив пустой — это значит, что обстреливать корабль далее нет смысла и он считается потопленным.

Соберём весь JS-код этого подраздела вместе в одной функции:

Итак, на этом я на этом заканчиваю рассмотрение алгоритма выстрела компьютера.

Заключение.

В пяти статьях, посвящённых игре «Морской бой», я постарался подробно и доступно рассказать, как написать эту популярную игру на JavaScript. Я не претендую на то, что написанный мною код идеален и полностью оптимизирован. Возможно вы сможете предложить свои варианты каких-либо участков кода, другие пути решения. С удовольствием обсудим их в комментариях.

Можно сделать ряд существенных изменений и дополнений к существующему коду, которые позволят сделать игру «Морской бой» более интересной и сложной:

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

  2. Набор кораблей, которые игроку необходимо перетащить на игровое поле, создавать «налету», по событию:

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

Если у вас возникли вопросы или что-то не совсем понятно написано — пишите в комментарии, постараюсь объяснить более доступно и, при необходимости, перепишу или дополню статьи.

Комментарии

Всего: 12 комментариев
Требования при посте комментариев:
  1. Комментарии должны содержать вопросы и дополнения по статье, ответы на вопросы других пользователей.
    Комментарии содержащие обсуждение политики, будут безжалостно удаляться.
  2. Для удобства чтения Вашего кода, не забываейте его форматировать. Вы его можете подсветить код с помощью тега <pre>:
    <pre class="lang:xhtml"> - HTML;
    <pre class="lang:css"> - CSS;
    <pre class="lang:javascript"> - JavaScript.
  3. Если что-то не понятно в статье, постарайтесь указать более конкретно, что именно не понятно.
  • В статье присутствует ошибка синтаксиса в коде, в функции getCoordinatesShot (восьмая строка в вашем блоке кода). Поправьте

  • В это игре есть один существенный баг. Если в расположить коробли близко друг к други и попытаться повернуть один корабль так, чтобы он попал в зону нахождения второго корабля, то корабль изчезает. Можно начать игру и затащить на изи. Но корабли перед использование этой уязвимости должны ВСЕ находиться на поле.

    • Ошибку исправил. Это не баг логики, а косяк при работе с редактором. В скрипте примера, в функции Instance.prototype.rotationShip, случайно были поменяны местами несколько строк кода.
      Код приведённый в статье в разделе «Поворот корабля на 90°» — верный, там править ничего не надо.
      Спасибо за указанную ошибку.

  • Статьи пока не читал, добавил в закладки, почитаю на свежую голову.
    Поиграл немного, нашел много разных багов:
    1. Корабли можно перетаскивать даже во время боя.
    2. Хотелось бы, чтобы убитые корабли автоматически окружались промахами по периметру.
    3. Отсюда вытекает следующий пункт: никогда не знаешь точно убит ли корабль полностью. Соперник добивает корабль и следующий выстрел всегда делает в следующую клетку, как если бы там была еще одна палуба.

    • По первому пункту, действительно, баг присутствует — разберусь. А по п.п. 2 и 3 — так и задумывалось.

      • Так это же идёт вразрез с правилами игры. Когда корабль убит полностью сопернику сообщают об этом, и он не должен делать бессмысленный выстрел рядом с полностью убитым кораблём

  • Еще бывает так, что в ячейке стоит промах, а на самом деле там палуба корабля

  • Вы не правы на счет того, что компьютер с рандомным вариантом стрельбы без тактики не соперник для человека, поиграйте в мою версию игры, там 5 уровней сложности компьютера и на всех пяти уровнях компьютер стреляет рандомно, но я вас уверяю, победить там задача не из легких. Вот скриншот ( https://drive.google.com/file/d/1q12eQyAXhttDgoJa5LWuDx6sSmYW00jY/view?usp=sharing ), счет в его пользу 25:71.
    Плюс, по Вашей игре есть замечания, убитый корабль должен как-то обозначаться, и компьютер не должен обстреливать уже потопленный корабль, это противоречит правилам игры.
    Добавьте больше интерактива в игру, звуки, кнопки, возможность начать игру заново после раунда.
    Вот ссылка на мою версию игры (не работает на Internet Explorer и Microsoft Eagle):
    https://eugene-kul.github.io/seaBattle/index.html

    • Вальдемар Евгений Ответить 18 июня 2020 в 11:28

      Процитируйте, плиз, где написано «компьютер с рандомным вариантом стрельбы без тактики не соперник для человека». А вообще, по тактике игры в Морской бой есть множество статей с математическими вкладками по теории вероятности, картинками и т.д., где доказывается преимущество тактики перед рандомом.
      Уничтоженный корабль видно по попаданиям, а повторных обстрелов быть не может, т.к. из массива возможных выстрелов удаляются координаты, по которым стреляли. Хотя может есть какие-то не выявленные баги, на 100% не буду утверждать.
      В игру можно добавить много чего, но тогда статья удлинится в несколько раз и, вряд ли кто-то будет читать её до конца. Я не ставлю перед собой целью распространить (навязать) готовый плагин, скрипт, программу и т.д. Смысл этого блога показать направления и пути решения часто возникающих задач. А окончательное «вылизование» — это на усмотрение читателей. Возможно мои решения не всегда оптимальны и реализованы не лучшим способом, для этого есть блок комментариев — давайте это обсуждать.
      А так ваш коммент больше похож на пиар вашей программы.

      • Цитата из пункта Введение:
        «Для того, чтобы компьютер мог, как минимум, играть на равных с человеком, он (компьютер) должен иметь некую тактику игры. Причём, данная тактика должна быть оптимальна применительно к игре «Морской бой» и обеспечивать высокие шансы на победу над человеком.
        Исходя из этого, сразу же откажемся от рандомного обстрела поля игрока в начальной стадии игры. В рандомном формировании координат выстрела компьютера в полной мере присутствует элемент случайности, а нам, для победы компьютера над человеком, нужно свести этот элемент к минимуму.»
        Поэтому я Вам показал свой вариант игры, где реализована тактика игры компьютера на уровне человека с рандомным обстрелом поля. Я свое приложение не пиарю, это первое что я сделал на javaScript, и я знаю, что там в коде миллион ошибок. Я только учусь.
        По поводу обстрела убитых кораблей компьютером, ссылка на запись: ( https://drive.google.com/file/d/1UPLGqovxXbvAswLvOuAjMnqPBlD6A_Ei/view?usp=sharing ). И я не вижу никаких выделений уничтоженных кораблей и я не могу понять, убил я корабль или нет, и начинаю бессмысленно простреливать вокруг потопленного корабля. Чисто мое мнение, если ты начал что-то делать, то это что-то должно быть похоже на законченную вещь. Я не критикую Вашу игру, Вы молодец, что все это делаете, я просто опровергаю слова сказанные в статье, вот и все, не стоит на меня обижаться)

        • Вальдемар Евгений 18 июня 2020 в 13:49

          Обид никаких нет, для того комменты и прикручены, чтобы можно было обсуждать, указывать на ошибки и предлагать свои варианты реализации.
          По поводу обстрела корабля вокруг — именно так я планировал, так сложнее код в выстреле компьютера. Кстати, компьютер тоже обстреливает все возможные клетки рядом с кораблём, так что игрок и комп в этом случае находятся в равном положении.
          А с косяком буду разбираться, всё-равно хотел переписать на ES6

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *