Симуляция

Проект “Симуляция” #

Суть проекта — пошаговая симуляция 2D мира, населённого травоядными и хищниками. Кроме существ, мир содержит ресурсы (траву), которыми питаются травоядные, и статичные объекты, с которыми нельзя взаимодействовать — они просто занимают место.

2D мир представляет собой матрицу N×M, каждое существо или объект занимают клетку целиком, нахождение в клетке нескольких объектов/существ — недопустимо.

Идея взята отсюда и упрощена.
Комментарии по проекту — https://www.youtube.com/watch?v=3Vrwx4iryhw.

Что нужно знать #

  • Go — структуры, интерфейсы, композиция, embedding, map, срезы, пакеты, методы
  • Понимание разницы между императивной логикой и данными: в Go логика обычно не “живёт” внутри сущностей

Мотивация проекта #

Основная цель — продемонстрировать архитектурный дизайн в Go, где:

  • вместо иерархии классов — композиция и интерфейсы,
  • вместо наследования — встраивание и интерфейсная типизация,
  • вместо “умных объектов” — “глупые данные” + “умные сервисы”.

Дизайн проекта #

Position #

Структура для обозначения координат для всех существ и объектов существующих в симуляции, содержит пару координат.

EntityType #

Алиас над типом string для обозначения типа сущности: grass, rock, tree etc.

Occupier #

Интерфейс определяющий все, что может занимать клетку. Примеры методов Type() EntityType, Pos() Position

Grass, Rock, Tree #

Rock, Tree - статичные объекты. Grass - ресурс для травоядных. Будем реализовывать как имплементации интерфейса Occupier

Creature #

Подмножество Occupier. Существо, имеет скорость (сколько клеток может пройти за 1 ход), количество HP. Интерфейс должен позволять реализовать движение по карте и работу с HP.

Herbivore #

Травоядное, реализует интерфейс Creature. Стремятся найти ресурс (траву), может потратить свой ход на движение в сторону травы, либо на её поглощение.

Predator #

Хищник, реализует интерфейс Creature. В дополнение к Creature, имеет силу атаки. На что может потратить ход хищник:

  • Переместиться (чтобы приблизиться к жертве - травоядному)
  • Атаковать травоядное. При этом количество HP травоядного уменьшается на силу атаки хищника. Если значение HP жертвы опускается до 0, травоядное исчезает

Map #

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

Simulation #

Главный класс приложения, включает в себя:

  • Карту
  • Счётчик ходов
  • Рендерер поля
  • Actions - список действий, исполняемых перед стартом симуляции или на каждом ходу (детали ниже)

Методы:

  • NextTurn() - просимулировать и отрендерить один ход
  • Start() - запустить бесконечный цикл симуляции и рендеринга
  • Pause() - управляет работой симуляции

Actions #

Action - действие, совершаемое над миром. Например - сходить всеми существами. Это действие итерировало бы существ и управляло передвижением. Логика поведения живет здесь - не в сущностях. Каждое действие описывается отдельной структурой и совершает операции над картой. Симуляция содержит 2 массива действий:

  • initActions - действия, совершаемые перед стартом симуляции. Пример - расставить объекты и существ на карте
  • turnActions - действия, совершаемые каждый ход. Примеры - передвижение существ, добавить травы или травоядных, если их осталось слишком мало

Поиск пути #

Советую писать алгоритм поиска пути полностью с нуля, используя в качестве источника описание алгоритма на википедии. Проще всего начать с алгоритма поиска в ширину. Он относительно простой в реализации, но может работать медленно на больших полях, для которых лучше подойдет алгоритм A*.

Рендерер #

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

Конечная цель #

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

  • Размер поля
  • Диапазоны HP и скорости существ
  • Диапазон атаки хищников

Опциональные идеи для усложнения проекта:

  • Механика размножения существ
  • Механика голода, когда от отсутствия пищи у них начинает уменьшаться HP

Ресурсы для работы над ошибками #

  • Реализации проекта другими студентами и мои ревью этих реализаций
  • Чеклист для самопроверки с типовыми ошибками (в конце страницы)
  • Присылайте законченные проекты в чат, добавляю их в список, сообщество делает ревью проектов

Чеклист для самопроверки #

❗️Спойлеры: советую не читать этот список до того момента, пока не допишете первую самостоятельную работающую версию проекта❗️

Проблемы и ошибки в коде:

  • Интеграция библиотеки графического интерфейса ценой чистоты и понятности кода. Достаточно консольного интерфейса
  • Дублирование кода между структурами
  • Класс Map
    • Неоптимальный выбор коллекции для хранения состояния ячеек
    • Не использование интерфейсов, параллельные коллекции для разных типов существ (для каждого типа своя коллекция)
    • Заполнение карты “пустотами”
    • Неоднозначное именование полей, переменных, методов. Несовпадение имён смысловой нагрузке
    • Поиск пути в отдельном пакете
    • Логика поведение не в сущностях
    • Использование интерфейсов и duck typing
    • Использован context для управления жизненным циклом
  • Поиск пути
    • Реализация алгоритма поиска пути внутри классов существ. Следует вынести это в отдельный класс
    • Дублирование кода для поиска пути хищниками и травоядными

Мелочи:

  • Неиспользование .gitignore, из-за чего в репозиторий попадают лишние файлы и папки (например, logs, .env)
  • Неаккуратное форматирование кода
  • Неиспользование пакетов для разделения логики, все лежит в main.go