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

CGC — взгляд изнутри

Время на прочтение10 мин
Количество просмотров665
Итак, в продолжение топика про CGC раскрою технические детали систем для проведения подобного рода соревнований на основе собственного опыта.

  1. Состав системы
    Для начала, определимся из чего состоит система такого рода.
    Ну, во-первых, самое главное это модуль симуляции. Далее это, конечно же, клиент и сервер, ну и напоследок самая важная часть для Code Game Show это визуализатор. Теперь рассмотрим каждую из этих частей отдельно.
    1. Симулятор
      Модуль симуляции определяем физику мира, набор юнитов и вообще все, что связано с игровым миром. Кроме того, этот модуль отвечает за симуляцию боев между стратегиями.
      Здесь возникает первый вопрос – во что записывать результаты боев?
      Первым вариантом является запись некой истории о ходе боя, которая впоследствии может быть воспроизведена с помощью визуализатора.
      Вторым подходом является одновременная симуляция и воспроизведение.
      Каждый из подходов имеет свои плюсы и минусы. Плюсом второго подхода является уменьшение времени, затрачиваемого участниками на анализ своих стратегий во время их разработки. Однако первый подход позволяет реализовать функционал установки скорости и точки воспроизведения.
      Ещё одной важной деталью симулятора является то, что он должен «уметь» собирать данные от стратегий, написанных на разных языках.

      Здесь тоже существует несколько вариантов реализации.
      Первый вариант, который напрашивается это компиляция стратегий участников в DLL, и затем подключение этих библиотек из симулятора. Однако этот вариант несет в себе несколько проблем:
      • не все языки могут на выходе предоставить DLL
      • при использовании DLL они грузятся в общий процесс и могут его намерено «уронить».

      Более защищенным является вариант компиляции с исполняемые файлы и использование межпроцессорного взаимодействия для общения симулятора и стратегий участников. Для общения можно использовать кому что нравится – я на практике видел системы, которые используют стандартный ввод/вывод (но я вам не рекомендую), системы общающиеся через сокеты.
      Ну а логика работы симулятора достаточно проста:
      • на вход поступает информация об участвующих стратегиях и игровом мире (например, идентификатор используемой карты)
      • симулятор запускает стратегии участников и поочередно передает им команды на выполнение метода Init
      • симулятор получает результаты выполнения метода Init каждой стратегии и обрабатывает ошибки (run-time, time limit, memory limit) пользовательского кода
      • симулятор поочередно передает команды на выполнение метода Move и собирает результаты
      • приводит значения в допустимые интервалы и проводит симуляцию боя
      • обновляет значения игровых переменных и вновь вызывает метод Move
      • по окончании боя записывает результаты боя

    2. Клиент
      Главная задача клиента – отправка решений и запуск просмотра боев.
      Ещё может быть полезным сделать функционал для общения между жюри и участниками для решения неточностей правил. Иногда возникают ситуации, когда клиент изменяется в ходе самого соревнования и функция автообновления тоже может пригодиться.
      Каких-либо особенностей в клиенте нет.
    3. Сервер
      Сервер это одна из самых серьёзных частей. Первые и последние минуты соревнований обычно являются для сервера очень хорошим испытанием – потом заявок на проверку в эти минуты достаточно велик и ваш сервер должен в приемлемое время отвечать на них.

      Что же входит в состав сервера? едаются модулю симуляции.
      Собственно основной сервер, который обслуживает подключения пользователей и приемку от них решений. Далее эти решения проходят компиляцию. Успешно скомпилированные решения передаются модулю симуляции.

      Настоятельно рекомендую использовать те же компиляторы, что будут установлены и у участников – это позволит снизить количество ошибок.

      Для обеспечения высокой скорости обработки решений пользователей можно использовать кластер (как делают в ТТИ ЮФУ), ну а в отсутствии кластера можно реализовать распределенный сервер.
    4. Визуализатор
      Данный модуль служит для отображения боев. Красочные спецэффекты сделают их просмотр более интересным и захватывающим, а если в нем будет присутствовать функционал по управлению воспроизведением, то это упростит жизнь участников. Однако не увлекайтесь эффектами – помните, что если вам визуализатор будет сильно тормозить, то это сделает проведение соревнований невозможными.

  2. Пользовательское API
    Ну, здесь все зависит от вашей фантазии, единственно, что хотелось бы отметить – рекомендую сделать методы для отладки стратегий – например, вывод отладочных сообщений (выводятся только во время написания стратегии), вывод точек и векторов.

    Ещё рекомендую выдавать API за день – за два, а перед самим соревнованием устроить небольшой сбор участников, на котором ответить на возникшие вопросы. В качестве примера в конце данной статьи приведен API с одного из CGC.
  3. Замечания
    Ещё немного замечаний по игровому миру. Во-первых, рассчитывайте сложность – ведь у участников будет всего 4-5 часов на изучение игрового мира и реализацию стратегии. Чем сложнее будет игровой мир, тем менее интересные стратегии в итоге будут у игроков и тем менее зрелищным будет Code Game Show.

    Если ваш мир очень сложен, но вы его не хотите менять, попробуйте включить в пользовательское API более высокоуровневые методы (например, методы по поиску пути между двумя точками). Придется потратить не один и не два дня – обычно на это уходит от недели до месяца.
    Постарайтесь сделать поддержку наиболее распространенных языков – это расширит круг участников.
    Уделите время игровому балансу – для хорошего баланса вам придется потратить не один и не два дня – обычно на это уходит от недели до месяца.
  4.  Пример User API
    Интерфейс IBonus – описывает интерфейс объектов-бонусов
    IBonus: public IMoveableObject
    Получить бонус хит-поинтов
    Возвращаемое значение: количество хит-поинтов, которые получит бот если подберёт бонус
    int GetHP()

    Получить бонус боезапаса к пулемёту
    Возвращаемое значение: количество пуль для пулемёта, которые получит бот если подберёт бонус
    int GetMashinegunAmmo()
    Получить бонус боезапаса к лазеру
    Возвращаемое значение: количество зарядов для пулемёта, которые получит бот если подберёт бонус
    int GetLaserAmmo()

    Получить бонус боезапаса к пушке
    Возвращаемое значение: количество снарядов для пулемёта, которые получит бот если подберёт бонус
    int GetCannonAmmo()

    Получить бонус боезапаса к ракетнице
    Возвращаемое значение: количество ракет, которые получит бот если подберёт бонус
    int GetRocketAmmo()

    Получить время жизни бота
    Возвращаемое значение: оставшееся время жизни бонуса в ходах
    int GetElapsedTime()

    Интерфейс IBot – описывает интерфейс игрового юнита
    IBot: public IMoveableObject

    Получить имя команды
    Возвращаемое значение: возвращает имя команды
    string GetTeamName()

    Получить имя бота
    Возвращаемое значение: возвращает имя бота или пустую строку если оно не задано
    string GetName()

    Получить информацию о боезапасе и состоянии пушки
    Информация содержит в себе сведения о боезапасе (количестве снарядов) и время, через которое пушка будет готова к выстрелу
    Возвращаемое значение: текущее состояние пушки
    SWeaponInfo GetCannon()

    Получить информацию о боезапасе и состоянии пулемёта
    Информация содержит в себе сведения о боезапасе (количестве снарядов) и время, через которое пулемёта будет готова к выстрелу
    Возвращаемое значение: текущее состояние пулемета
    SWeaponInfo GetMashineGun()

    Получить информацию о боезапасе и состоянии лазера
    Информация содержит в себе сведения о боезапасе (количестве зарядов) и время, через которое лазера будет готова к выстрелу
    Возвращаемое значение: текущее состояние лазера
    SWeaponInfo GetLaser()

    Получить информацию о боезапасе и состоянии ракетницы
    Информация содержит в себе сведения о боезапасе (количестве выстрелов) и время, через которое ракетницы будет готова к выстрелу
    Возвращаемое значение: текущее состояние ракетницы
    SWeaponInfo GetRocket()

    Получить тип бота
    Возвращаемое значение: возвращает тип текущего бота
    BotType GetType()

    Получить поворот башни
    Поворот башни отсчитывается относительно диамитральной плоскости бота.
    Возвращаемое значение: относительный поворот башни в радианах
    float GetTurrelAngle()

    Получить уровень здоровья бота
    Возвращаемое значение: текущее количество хит-поинтов
    int GetHP()
    Интерфейс IMoveableObject – описывает интерфейс объектов, которые могут перемещаться.

    Получить позицию объекта
    Возвращает координату центра объекта.
    Возвращаемое значение: координата левого верхнего угла
    Point GetPosition()

    Получить угол поворота объекта
    Подробнее о координатной системе смотри описание интерфейса IWorld
    Возвращаемое значение: возвращает поворот объекта в радианах
    float GetDirection()

    Получить радиус объекта
    Бонусы, снаряды и боты представляют собой окружности,
    Возвращаемое значение: возвращает радиус объекта
    float GetRadius()

    Получить скорость объекта
    Возвращаемое значение: возвращает модуль вектора скорости
    float GetSpeed()

    Получить вектор импульсной скорости
    При столкновении бота с некоторым объектом (снаряд, другой бот, объект карты, кроме бонуса), а так же при выстреле он приобретает импульсную скорость.
    Возвращаемое значение: вектор импульсной скорости
    Vector GetImpulseVelocity()

    Установить текстовую метку для объекта
    Установка текстовых меток позволяет опознавать объекты. Установив однажды метку объекту можно в последствии получить его по этой метке. Текстовые метки нигде не отображаются. Участники не видят текстовых меток других участников.
    Запрещено ставить пометки на ботов — такие пометки будут сбрасываться каждый ход.
    Параметры:
        mark текстовая метка для объекта
    void SetMark(int mark)
    Получить текущую метку объекта
    Возвращаемое значение: текущая метка объекта, если метка не установлена возвращается -1
    int GetMark()

    Интерфейс ISelf – описывает интерфейс управляемого юнита.
    ISelf: public IBot

    Установить имя боту
    Установка имени ботов возможна лишь внутри функции Init. Вызовы внутри Move игнорируются
    Длина имени бота не должна превышать 20 символов. Если длина имени превышает их, то оно будет укорочено до необходимой длины.
    Параметры:
        name имя бота
    void SetName(string name)
    Установить тип бота
    Разные типы ботов обладают различными характеристиками. Установка типа бота возможна только внутри функции Init. Вызовы внутри Move игнорируются.
    Параметры:
        type Тип бота
    void SetType(BotType type)

    Установить скорость движения
    Изменяет значение вектора скорости. Отрицательное значение соответствует движению назад. Если скорость превышает максимальный порог или меньше минимального порога, оно будет уменьшено/увеличено до допустимых значений.
    Параметры:
        speed Новая скорость
    void SetSpeed(float speed)

    Установить поворот бота
    Параметры:
        angle Новый угол поворота в радианах
    void SetDirection(float angle)
    Изменить поворот башни бота
    Параметры:
        delta Приращение угла поворота башни в радианах
    void TurnTurrel(float delta)
    Произвести выстрел из пулемёта
    Производит выстрел из пулемета. Пулемёт стреляет всегда по ходу движения бота. Если пулемёт не готов к выстрелу ничего не происходит.
    void ShotMashinegun()

    Произвести выстрел из пушки
    Производит выстрел из пушки. Пушка установлена на башне. Если пушка не готова к выстрелу ничего не происходит.
    void ShotCannon()

    Произвести выстрел из лазера
    Производит выстрел из лазера. Лазер стреляет всегда по ходу движения бота. Если лазер не готов к выстрелу ничего не происходит.
    Параметры:
        angle угол поворота лазера
    void ShotLaser(float angle)

    Произвести выстрел из ракетницы
    Производит выстрел из ракетницы. Если ракетница не готова к выстрелу ничего не происходит.
    Параметры:
        target Цель
    void ShotRocket(IMoveableObject *target)

    Интерфейс IShell – описывает интерфейс снаряда, находящегося на карте.
    IShell: public IMoveableObject

    Получить владельца.
    Возвращаемое значение: бот, произведший выстрел этим снарядом
    IBot* GetOwner()

    Получить цель.
    Для снарядов типа лазер, пушка и пулемёт цель равна NULL
    Если объект был уничтожен, то возвращается NULL
    Возвращаемое значение: объект-цель снаряда.
    IMoveableObject* GetTarget()
    Получить тип снаряда.
    Возвращаемое значение: тип снаряда, подробнее смотри описание ShellType
    ShellType GetType()
    Возвращаемое значение: получить урон, наносимый снарядом.
    Возвращаемое значение: урон, наносимый снарядом
    int GetHP()

    Интерфейс IStaticObject – описывает интерфейс статических объектов карты.
    Получить количество хит-поинтов объекта
    Возвращаемое значение: текущее значение хит-поинтов
    int GetHP()

    Получить координаты объекта
    Координаты статического объекта соответствуют его левому верхнему углу. Подробнее о системе координат смотри IWorld
    Возвращаемое значение: координаты объекта
    Point GetPosition()

    Получить размер объекта
    Возвращаемое значение: размер объекта
    Size GetSize()

    Интерфейс IWorld – описывает интерфейс игрового мира.

    Получить номер текущего хода
    Нумерация производиться с 1. Нулевой ход соответствует вызову Init
    Возвращаемое значение: номер текущего хода
    int GetCurrentStep()

    Получить общее число ходов
    Возвращаемое значение: общее число ходов (без учета хода-инициализации)
    int GetTotalStep()

    Получить список статических объектов
    Список основан на типе std::vector и позволяет обращаться по индексу
    Разрушенные объекты удаляются из игры и в этом списке не отображаются
    Возвращаемое значение: список статических объектов карты
    IStaticObjectList GetStaticObjects()

    Получить список ботов противника
    Список основан на типе std::vector и позволяет обращаться по индексу
    Убитые боты удаляются из игры и в этом списке не отображаются
    Возвращаемое значение: список ботов противника
    IEnemyList GetEnemies()

    Получить список своих ботов
    Список основан на типе std::vector и позволяет обращаться по индексу
    Убитые боты удаляются из игры и в этом списке не отображаются
    Возвращаемое значение: список своих ботов
    ISelfList GetSelfs()

    Получить список снарядов находящихся на карте
    Список основан на типе std::vector и позволяет обращаться по индексу
    Снаряды, которые вылетели за пределы карты или столкнулись с объектом удаляются из игры и в этом списке не отображаются
    Возвращаемое значение: список снарядов находящихся на карте
    IShellList GetShells()

    Получить список бонусов находящихся на карте
    Список основан на типе std::vector и позволяет обращаться по индексу
    Бонусы, у которых окончилось время жизни или которые были подобраны удаляются из игры и в этом списке не отображаются
    Возвращаемое значениие: список ботов находящихся на карте
    IBonusList GetBonuses()

    Получить объект по метке
    Параметры:
        mark метка получаемого объекта
    Возвращаемое значение: если объект с такой меткой найден, то возвращается указатель на него, иначе возвращается NULL
    IMoveableObject* GetByMark(int mark)
    Добавить отладочное сообщение
    Добавляет отладочное сообщение, отображаемое при визуализации для отладки
    Параметры:
        format формат сообщения — строка длиной n символов. Далее идут n параметров в соответствии с задачным форматом.
            d — целое число (int)
            f — дробное число (float)
            s — строка (string)
    все остальные символы не интерпретируются и попадают в сообщение
    Общая длина одного сообщения не должна превышать 255 символов. Если строка окажется длиннее, то она будет сокращена до требуемого размера
    void AddMessage(string format, ...)

    Добавить отладочную точку
    Добавляет отладочную точку, отображаемую при визуализации для отладки
    Параметры:
        p координата точки
        lifetime время отображения вектора в ходах
    void AddPoint(Point p, int lifetime)

    Добавить отладочный вектор
    Добавляет отладочный вектор, отображаемый при визуализации для отладки
    Параметры:
        start начальная точка
        end конечная точка
        lifetime время отображения вектора в ходах
    void AddVector(Point start, Point end, int lifetime)
    Получить ширину карты
    Возвращаемое значение: ширина карты
    int GetWidth()

    Получить высоту карты
    Возвращаемое значение: высота карты
    int GetHeight()
Теги:
Хабы:
0
Комментарии0

Публикации

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

Истории

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

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