Как стать автором
Обновить

Разработка симулятора эволюции одноклеточных организмов «The strongest survives»

Время на прочтение7 мин
Количество просмотров23K

В данном посте я расскажу о своем опыте написания игры-симулятора эволюции одноклеточных организмов на прямоугольной плоскости. Кроме разработки алгоритмов симулятора, речь пойдет о проблемах с которыми я столкнулся при разработке данного проекта на C#, а также о его портировании для работы в браузере. В конце статьи будет ссылка на готовую к игре версию и на все исходники. Если я вас заинтересовал — прошу под кат.


Вдохновение


Перед тем как мы приступим — я хочу коротко рассказать о тех проектах, которые вдохновили меня на написание данного симулятора. Первую аналогию вы могли провести еще по скриншоту в шапке поста, ведь мой проект внешне довольно сильно похож на игру "Жизнь", придуманную математиком Джоном Конвем в 1970 году. Еще больше меня вдохновили вот эта и эта статьи других хаброюзеров. Чтоб не гонять уважаемого читателя по ссылкам, коротко опишу о чем идет речь в данных статьях: первая — модификация игры Конвея с добавлением туда естественного отбора, а вторая — моделирование разумной жизни на базе нейронных сетей.


Определение требований


Писать еще один клон игры "Жизнь" я не хотел, да и идея естественного отбора мне настолько понравилась, что я решил сконцентрироваться именно на эволюции. Название пришло само собой — "The strongest survives".


Вот требования, которые я ставил себе при разработке:


  • разработать симулятор эволюции, в котором будет множество видов организмов – клеток;
  • добавить несколько объектов для взаимодействия клеток с ними (еда, яд, мертвы клетки);
  • сделать симуляцию производительной в первую очередь, чтоб можно было быстро увидеть результат за большой период;
  • симуляция должна как можно более соответствовать реальной жизни;
  • должна быть возможность изменения условий обитания организмов.

В TSS клетки имеют много показателей, от которых зависит их следующее состояние. Они могут анализировать окружающую обстановку и принимать на основе этих данных решения, которые помогут им выжить. На их приоритеты при осмотре окрестностей влияют модификаторы, записанные в геноме. Например, есть параметр агрессия. Когда клетка видит другую клетку с чужеродным геномом, то она прибавляет к стремлению двигаться в ту сторону этот модификатор (агрессию). То же самое с голодом, коллективностью, тягой к яду и тягой к мертвым клеткам. После расчетов выбирается сторона с наибольшей суммой модификаторов.


Основные правила игры:


  • вселенная игры имеет свою единицу времени – тик;
  • каждая клетка имеет определенный уровень энергии, убывающий во время каждого тика;
  • главная цель клетки – выжить и размножиться. Для этого ей нужно собрать как можно больше энергии;
  • все клетки имеют максимальный возраст;
  • клетки могут иметь разный геном, который влияет на их стратегию выживания, при этом их физические возможности равны;
  • во время каждого тика каждая клетка может сделать один шаг в одну из 4-х выбранных сторон. При выборе направления шага, в зависимости от находящегося на новом месте объекта, произойдет какое-то событие. Если там пустое место — она просто туда переместится, если там еда, яд, или мертвая клетка — она получит их уровень энергии и тоже переместиться туда. Если же там живая клетка — она останется на месте и, в зависимости от того, какая там клетка произойдет определенное событие. Если там клетка с таким же геномом, то обе получат энергию, если с другим геномом — данная клетка заберет у нее часть энергии.


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


Также клетка может размножиться. Размножение происходит делением, один раз за тик, если у клетки достаточно энергии. При этом, рядом должно быть пустое пространство, где создается новая клетка с таким же геномом, либо клетка с мутировавшим геномом. При этом ее уровень энергии делиться поровну между родительской и дочерней клетками. Требуемый для размножения уровень энергии определяет пользователь, но модификатором к нему выступает параметр "репродуктивность". Этот параметр храниться в геноме. Чем он выше — тем меньше энергии нужно клетке для размножения.



Как-то так представлял себе я эту игру, в то время. В целом, что-то подобное и получилось.


Суть геймплея


Геймплей заключается в том, что пользователю предоставляется редактор констант вселенной, в котором он может изменять условия обитания организмов и наблюдать за тем, как они приспосабливаются. Звучит не очень впечатляюще, но здесь довольно много значений, доступных игроку (около 30), меняя которые можно получить множество уникальных вариаций результата. Подробней о каждой константе вы можете почитать в мануале в папке с игрой или под спойлером.


Константы вселенной

MaxCountOfCellTypes — максимальное количество геномов клеток.


Mutation_Enable — позволяет включить или отключить мутацию клеток при размножении.


Mutation_AttackChildrenMutantsOfFirstGeneration — если это свойство отключено, то клетки не могут атаковать своих потомков первого поколения, когда те мутируют.


Mutation_AttackParentIfCellIsYouMutant — если это свойство отключено, то мутировавшие клетки не могут атаковать своих предков первого поколения.


Mutation_ChangedValuesAtOne — показывает сколько раз изменяются значения, отвечающие за поведение клеток, при каждой мутации. Например, если значение 10, то 10 раз будет выбрано и изменено на 1 случайное свойство клетки, отвечающее за поведение. Может быть от 1 до 200.


Mutation_ChancePercent — шанс мутации при размножении.


CellAge_Max — максимальный возраст клетки.


CellAge_AdultCell — возраст взрослой клетки. У клеток-детей уровень агрессии не выше CellGenome_Child_Aggression и они не могут размножаться.


EnergyLevel_CreatingCell — уровень энергии у клеток, при их генерации на старте.


EnergyLevel_NeededForReproduction — уровень энергии, при котором клетка может размножиться. Может варьироваться из-за CellGenome_ReproductionRange.


EnergyLevel_MaxForCell — максимальный уровень энергии, которую может накопить клетка.


EnergyLevel_DeadCell — уровень энергии, получаемый от мертвых клеток.


EnergyLevel_DefFood — уровень энергии, получаемый от еды.


EnergyLevel_PoisonedFood — уровень энергии, получаемый от яда.


EnergyLevel_MovesFriendly — уровень энергии, получаемый клеткой, когда к ней движется родственник или она движется к нему. Как правило он намного меньше чем уровень энергии обычной еды. Способствует созданию колоний.


EnergyLevel_MovesAggression — показывает сколько энергии отнимает клетка у чужеродных клеток при нападении.


CellsCount_MaxWithOneType — максимальное количество клеток на поле с одинаковым геномом.


CellGenome_Child_Aggression — уровень агрессии у клеток-детей.


CellsCount_MaxAtField — максимальное количество клеток на поле.


EnergyEntropyPerSecond — уровень энергии, который теряет клетка при каждом тике.


Special_FoodCountForTick — количество еды, генерируемое при каждом тике вселенной.


Special_PoisonCountForTick — количество яда, генерируемое при каждом тике вселенной.


CellGenome_HungerRange — диапазон значений голода при генерации клетки.


CellGenome_AggressionRange — диапазон значений агрессии при генерации клетки.


CellGenome_ReproductionRange — диапазон значений репродуктивности при генерации клетки. Чем выше значение, тем меньше энергии нужно клетке для размножения.


CellGenome_FriendlyRange — диапазон значений коллективности при генерации клетки.


CellGenome_PoisonRange — диапазон значений влечения к яду при генерации клетки. Обычно отрицателен.


CellGenome_CorpseRange — диапазон значений влечения к мертвым клеткам при генерации клетки.


Так выглядит этот редактор в веб версии.



А в версии под Windows можно еще и редактировать место, где генерируется еда или яд.


Разработка и результат


В результате нескольких месяцев разработки, я написал 4 версии своей игры. Первые две — для тестирования и отладки логики вселенной симулятора, третья — релизная версия под Windows, четвертая — версия для браузеров. Сейчас я быстро пробегусь по первым двум и уже потом буду рассказывать об особенностях основных версий.


Версия на WindowsForms



Это первая рабочая версия моей программы. В данной версии еще не реализовано большей части игровой логики и она имеет много багов. Рендеринга тут нет, все выводится в текстовое поле в виде символов. Такой вот текстовый вывод работал очень медленно (1-2 тика в секунду), но, все же, есть в нем что-то ламповое.


Версия в текстовой консоли



Как ни странно – вторая версия программы. Ее я писал для лабораторных. В этой и последующих версиях игровая логика идентична.


Версия на WPF



На эту версию я потратил больше всего времени, но она и является наиболее проработанной. Рендеринг фреймов здесь занимает 20-30 мс и не зависит от размеров поля, да и сам тик обрабатывается довольно быстро. Пользователь может создавать огромное игровое поле (хоть 2000х2000 ячеек). Есть возможность просматривать информацию о каждой клетке, просто кликнув на нее.
Демонстрация работы программы.



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


1) Ручной рендеринг. Так как я решил писать велосипед — я делал отрисовку без движка, используя стандартные средства WPF для отрисовки примитивов. Но это позволило мне максимально оптимизировать отрисовку именно под свою программу.


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


Но в этом способе есть и минусы — часто возникают помехи на изображении, особенно это заметно в WPF версии.


2) Динамический редактор классов. Чтоб не переделывать по сто раз форму, на которой отображались поля редактирования настроек вселенной, я использовал рефлексию и написал динамический редактор, который читал поля передаваемого объекта (в данном случае — объект с настройками) и строил форму для их редактирования.



Вот так выглядел этот редактор в версии WPF.


Веб версия


Через некоторое время после написания WPF версии мне захотелось портировать ее под браузер. Результат вы можете увидеть в шапке статьи, либо, лично перейдя на сайт и попробовав.


Я немного знал Javascript, но мне совсем не хотелось переписывать полторы тысячи строк кода игровой логики на нем. Но выход нашелся — я решил использовать Bridge.NET, открытый компилятор C# в Javascript. Да, дорогой читатель, ты все правильно понял — теперь мы пишем под браузер на C#. Благодаря этому компилятору я смог использовать язык, который хорошо знал. Оставалось только переписать рендеринг и остальной код, связанный с отображением. По причине Javascript лени, веб версия менее функциональна, чем версия под Windows, но служит неплохой демонстрацией работы симулятора.


Справедливо отметить, что мне, все же, очень пригодились знания Javascript и HTML из-за особенностей работы браузеров.


Итоги


При разработке данного проекта я сильно подтянул свои навыки в программировании в целом. На данный момент все версии игры доступны на этом сайте https://extremedotneting.github.io/tss/. Если вы сами хотите приступить к разработке или просто посмотреть код — вот мой гитхаб, тут вы можете найти все версии моей программы с обширными комментариями. Надеюсь, вам было интересно читать о моем опыте, возможно, эта статья вдохновит и вас на что-то подобное, как меня вдохновили другие статьи в свое время.

Теги:
Хабы:
+25
Комментарии25

Публикации

Изменить настройки темы

Истории

Работа

Ближайшие события

Weekend Offer в AliExpress
Дата20 – 21 апреля
Время10:00 – 20:00
Место
Онлайн
Конференция «Я.Железо»
Дата18 мая
Время14:00 – 23:59
Место
МоскваОнлайн