Предыдущие части: часть2, часть 1
После последней публикации значительно переработал весь код - кое-что убрал, кое-что добавил, кое-что изменил. Сейчас вроде как большая часть кода работает стабильно и виден "свет в конце тоннеля"))). Ниже будет выложен измененный код:
Класс Bot метод __init__:
Класс Bot, метод bot_blit:
Генерация популяции + координаты пространства:
Класс Food методы: __init__ и food_blit:
Генерация еды для ботов:
Основной цикл программы:
Эту функцию прокомментирую. Не знаю, правильно я ее сделал или нет, но так получилось... Хотел сделать так, чтобы обработка всех ботов проходила в промежутке 0,1 секунды, но этого времени не хватает (отрисовать, обработать столкновения, обработать нейросеть каждого бота, отрисовать перемещения ботов...). Время отслеживается специальным маркером каждую секунду и при количестве ботов больше 50 бывает, что секунды перескакивают. Цикличность этой функции 1 микросекунда. Возможно, надо было сделать вообще без функции, а просто в бесконечном цикле while...
Единственное, что в этом цикле точно сделано не так, как хотелось - это запись команд (выход нейросети) в память. Пока оставил так, но в последующем правильнее сделать запись не всех команд подряд, а запись лишь в том случае, когда текущие команды отличаются от предыдущих команд. Это значительно сократит временные затраты и при просмотре записи команд будет сразу видно их изменение во времени (запись осуществляется в словарь, ключ - время записи команд, сейчас ключ - это просто счетчик). Почему не получилось сразу сделать как надо - если ключом является время, то сравнивается текущее время с временем на минус один такт. Как применять математику ко времени (секундам и минутам) я не знаю, Если у кого получится сделать правильно - будет очень здорово!
На картинке видно, что нейросеть выдает 6 команд - движение верх, направо, вниз, налево, кушать, размножаться. При желании, кому будет интересно - количество команд можно увеличить, просто изменив количество нейронов в выходном слое нейросети. В моем случае команда на выполнение - это выход нейросети, который больше 0. В качестве функции активации я применил гиперболический тангенс, который показывает результат от -1 до 1, поэтому можно при желании для команд использовать другой диапазон значений.
Если кто захочет поэкспериментировать с количеством слоев нейросети, то в метод net_work дописать новый слой нейросети. Кто хорошо разбирается - можно всю обработку сделать через цикл for и тогда вручную ничего дописывать не надо.
Вторая часть функции time_game:
Данная часть цикла обрабатывает все команды нейросети, а также вносит изменения в тэги картинок ботов. В самом низу каждую секунду увеличивается возраст бота. При обработке команд на движение - размер шага бота зависит от уровня сигнала с выхода нейросети. Базовая величина - 5 пикселей за один шаг при условии, что уровень сигнала равен 1. Если уровень сигнала другой (от 0 до 1), то эта величина умножается на базовую величину и получается размер текущего шага.
Методы класса бот, которые обеспечивают движение ботов:
В данные методы добавил изменение энергии бота в зависимости от того, какой шаг делает бот - чем больше шаг, тем больше расходует энергии.
Если кого заинтересовало - можете вносить любые изменения в код и экспериментировать. Также будет интересно посмотреть и на ваш код.
В следующей статье будет о выявлении столкновений (что попадает в поле зрения радара), а также о подготовке данных для нейросети.