Перейти к содержанию

Соревнование по программированию искусственного интеллекта


MasterGH

Рекомендуемые сообщения

logo-150.png

 

Russian AI Cup — соревнование по программированию искусственного интеллекта

 
Скрытый текст

0-5-500x282.png

 

 

Russian AI Cup — открытое соревнование по программированию искусственного интеллекта. Попробуйте свои силы в программировании игровой стратегии! Это просто, наглядно и весело!

Чемпионат пятого Russian AI Cup называется CodeWizards. Вам предстоит программировать искусственный интеллект для управления волшебником. Ваши стратегии будут соревноваться между собой в Песочнице и чемпионате. Вы можете использовать любой из языков программирования: С++, Java, C#, Python, Pascal или Ruby. Песочница уже открыта. Удачи!

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

Расписание чемпионата

  • Песочница: с 7 ноября до 25 декабря открыта для всех желающих; первая и четвертая недели будут проходить в режиме бета-тестирования, в эти периоды возможны нестабильная работа системы и существенные изменения в правилах.
  • Раунд 1: 26–27 ноября.
  • Раунд 2: 10-11 декабря.
  • Финал: 17-18 декабря.

Начните с Быстрого старта. Написать простейшую стратегию совсем несложно!

 

prizes-codewizards_ru.jpg

 

 

Пример

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

 

Скрытый текст


import model.*;

import java.util.*;

public final class MyStrategy implements Strategy {
    private static final double WAYPOINT_RADIUS = 100.0D;

    private static final double LOW_HP_FACTOR = 0.25D;

    /**
     * Ключевые точки для каждой линии, позволяющие упростить управление перемещением волшебника.
     * <p>
     * Если всё хорошо, двигаемся к следующей точке и атакуем противников.
     * Если осталось мало жизненной энергии, отступаем к предыдущей точке.
     */
    private final Map<LaneType, Point2D[]> waypointsByLane = new EnumMap<>(LaneType.class);

    private Random random;

    private LaneType lane;
    private Point2D[] waypoints;

    private Wizard self;
    private World world;
    private Game game;
    private Move move;

    /**
     * Основной метод стратегии, осуществляющий управление волшебником.
     * Вызывается каждый тик для каждого волшебника.
     *
     * @param self  Волшебник, которым данный метод будет осуществлять управление.
     * @param world Текущее состояние мира.
     * @param game  Различные игровые константы.
     * @param move  Результатом работы метода является изменение полей данного объекта.
     */
    @Override
    public void move(Wizard self, World world, Game game, Move move) {
        initializeStrategy(self, game);
        initializeTick(self, world, game, move);

        // Постоянно двигаемся из-стороны в сторону, чтобы по нам было сложнее попасть.
        // Считаете, что сможете придумать более эффективный алгоритм уклонения? Попробуйте! ;)
        move.setStrafeSpeed(random.nextBoolean() ? game.getWizardStrafeSpeed() : -game.getWizardStrafeSpeed());

        // Если осталось мало жизненной энергии, отступаем к предыдущей ключевой точке на линии.
        if (self.getLife() < self.getMaxLife() * LOW_HP_FACTOR) {
            goTo(getPreviousWaypoint());
            return;
        }

        LivingUnit nearestTarget = getNearestTarget();

        // Если видим противника ...
        if (nearestTarget != null) {
            double distance = self.getDistanceTo(nearestTarget);

            // ... и он в пределах досягаемости наших заклинаний, ...
            if (distance <= self.getCastRange()) {
                double angle = self.getAngleTo(nearestTarget);

                // ... то поворачиваемся к цели.
                move.setTurn(angle);

                // Если цель перед нами, ...
                if (StrictMath.abs(angle) < game.getStaffSector() / 2.0D) {
                    // ... то атакуем.
                    move.setAction(ActionType.MAGIC_MISSILE);
                    move.setCastAngle(angle);
                    move.setMinCastDistance(distance - nearestTarget.getRadius() + game.getMagicMissileRadius());
                }

                return;
            }
        }

        // Если нет других действий, просто продвигаемся вперёд.
        goTo(getNextWaypoint());
    }

    /**
     * Инциализируем стратегию.
     * <p>
     * Для этих целей обычно можно использовать конструктор, однако в данном случае мы хотим инициализировать генератор
     * случайных чисел значением, полученным от симулятора игры.
     */
    private void initializeStrategy(Wizard self, Game game) {
        if (random == null) {
            random = new Random(game.getRandomSeed());

            double mapSize = game.getMapSize();

            waypointsByLane.put(LaneType.MIDDLE, new Point2D[]{
                    new Point2D(100.0D, mapSize - 100.0D),
                    random.nextBoolean()
                            ? new Point2D(600.0D, mapSize - 200.0D)
                            : new Point2D(200.0D, mapSize - 600.0D),
                    new Point2D(800.0D, mapSize - 800.0D),
                    new Point2D(mapSize - 600.0D, 600.0D)
            });

            waypointsByLane.put(LaneType.TOP, new Point2D[]{
                    new Point2D(100.0D, mapSize - 100.0D),
                    new Point2D(100.0D, mapSize - 400.0D),
                    new Point2D(200.0D, mapSize - 800.0D),
                    new Point2D(200.0D, mapSize * 0.75D),
                    new Point2D(200.0D, mapSize * 0.5D),
                    new Point2D(200.0D, mapSize * 0.25D),
                    new Point2D(200.0D, 200.0D),
                    new Point2D(mapSize * 0.25D, 200.0D),
                    new Point2D(mapSize * 0.5D, 200.0D),
                    new Point2D(mapSize * 0.75D, 200.0D),
                    new Point2D(mapSize - 200.0D, 200.0D)
            });

            waypointsByLane.put(LaneType.BOTTOM, new Point2D[]{
                    new Point2D(100.0D, mapSize - 100.0D),
                    new Point2D(400.0D, mapSize - 100.0D),
                    new Point2D(800.0D, mapSize - 200.0D),
                    new Point2D(mapSize * 0.25D, mapSize - 200.0D),
                    new Point2D(mapSize * 0.5D, mapSize - 200.0D),
                    new Point2D(mapSize * 0.75D, mapSize - 200.0D),
                    new Point2D(mapSize - 200.0D, mapSize - 200.0D),
                    new Point2D(mapSize - 200.0D, mapSize * 0.75D),
                    new Point2D(mapSize - 200.0D, mapSize * 0.5D),
                    new Point2D(mapSize - 200.0D, mapSize * 0.25D),
                    new Point2D(mapSize - 200.0D, 200.0D)
            });

            switch ((int) self.getId()) {
                case 1:
                case 2:
                case 6:
                case 7:
                    lane = LaneType.TOP;
                    break;
                case 3:
                case 8:
                    lane = LaneType.MIDDLE;
                    break;
                case 4:
                case 5:
                case 9:
                case 10:
                    lane = LaneType.BOTTOM;
                    break;
                default:
            }

            waypoints = waypointsByLane.get(lane);

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

            /*Point2D lastWaypoint = waypoints[waypoints.length - 1];

            Preconditions.checkState(ArrayUtils.isSorted(waypoints, (waypointA, waypointB) -> Double.compare(
                    waypointB.getDistanceTo(lastWaypoint), waypointA.getDistanceTo(lastWaypoint)
            )));*/
        }
    }

    /**
     * Сохраняем все входные данные в полях класса для упрощения доступа к ним.
     */
    private void initializeTick(Wizard self, World world, Game game, Move move) {
        this.self = self;
        this.world = world;
        this.game = game;
        this.move = move;
    }

    /**
     * Данный метод предполагает, что все ключевые точки на линии упорядочены по уменьшению дистанции до последней
     * ключевой точки. Перебирая их по порядку, находим первую попавшуюся точку, которая находится ближе к последней
     * точке на линии, чем волшебник. Это и будет следующей ключевой точкой.
     * <p>
     * Дополнительно проверяем, не находится ли волшебник достаточно близко к какой-либо из ключевых точек. Если это
     * так, то мы сразу возвращаем следующую ключевую точку.
     */
    private Point2D getNextWaypoint() {
        int lastWaypointIndex = waypoints.length - 1;
        Point2D lastWaypoint = waypoints[lastWaypointIndex];

        for (int waypointIndex = 0; waypointIndex < lastWaypointIndex; ++waypointIndex) {
            Point2D waypoint = waypoints[waypointIndex];

            if (waypoint.getDistanceTo(self) <= WAYPOINT_RADIUS) {
                return waypoints[waypointIndex + 1];
            }

            if (lastWaypoint.getDistanceTo(waypoint) < lastWaypoint.getDistanceTo(self)) {
                return waypoint;
            }
        }

        return lastWaypoint;
    }

    /**
     * Действие данного метода абсолютно идентично действию метода {@code getNextWaypoint}, если перевернуть массив
     * {@code waypoints}.
     */
    private Point2D getPreviousWaypoint() {
        Point2D firstWaypoint = waypoints[0];

        for (int waypointIndex = waypoints.length - 1; waypointIndex > 0; --waypointIndex) {
            Point2D waypoint = waypoints[waypointIndex];

            if (waypoint.getDistanceTo(self) <= WAYPOINT_RADIUS) {
                return waypoints[waypointIndex - 1];
            }

            if (firstWaypoint.getDistanceTo(waypoint) < firstWaypoint.getDistanceTo(self)) {
                return waypoint;
            }
        }

        return firstWaypoint;
    }

    /**
     * Простейший способ перемещения волшебника.
     */
    private void goTo(Point2D point) {
        double angle = self.getAngleTo(point.getX(), point.getY());

        move.setTurn(angle);

        if (StrictMath.abs(angle) < game.getStaffSector() / 4.0D) {
            move.setSpeed(game.getWizardForwardSpeed());
        }
    }

    /**
     * Находим ближайшую цель для атаки, независимо от её типа и других характеристик.
     */
    private LivingUnit getNearestTarget() {
        List<LivingUnit> targets = new ArrayList<>();
        targets.addAll(Arrays.asList(world.getBuildings()));
        targets.addAll(Arrays.asList(world.getWizards()));
        targets.addAll(Arrays.asList(world.getMinions()));

        LivingUnit nearestTarget = null;
        double nearestTargetDistance = Double.MAX_VALUE;

        for (LivingUnit target : targets) {
            if (target.getFaction() == Faction.NEUTRAL || target.getFaction() == self.getFaction()) {
                continue;
            }

            double distance = self.getDistanceTo(target);

            if (distance < nearestTargetDistance) {
                nearestTarget = target;
                nearestTargetDistance = distance;
            }
        }

        return nearestTarget;
    }

    /**
     * Вспомогательный класс для хранения позиций на карте.
     */
    private static final class Point2D {
        private final double x;
        private final double y;

        private Point2D(double x, double y) {
            this.x = x;
            this.y = y;
        }

        public double getX() {
            return x;
        }

        public double getY() {
            return y;
        }

        public double getDistanceTo(double x, double y) {
            return StrictMath.hypot(this.x - x, this.y - y);
        }

        public double getDistanceTo(Point2D point) {
            return getDistanceTo(point.x, point.y);
        }

        public double getDistanceTo(Unit unit) {
            return getDistanceTo(unit.getX(), unit.getY());
        }
    }
}

 

Скрытый текст

Данное соревнование предоставляет вам возможность проверить свои навыки программирования, создав искусственный интеллект (стратегию), управляющий волшебником в специальном игровом мире. Правила соревнования базируются на популярном в мире компьютерных игр жанре MOBA. В каждой игре вам будет противостоять пять стратегий других игроков. В то же время, у вас будет четыре союзника. Пять стратегий, находящиеся на одной стороне, составляют фракцию: Академию или Отступников. Основной командной целью этих пяти игроков является уничтожение базы противоположной фракции. Основной персональной целью каждого волшебника является сбор максимально возможного количества баллов. Звание победителя игры, а также все остальные места распределяются в соответствии с количеством набранных баллов. Игроку начисляются баллы, если его волшебник наносит урон, уничтожает или просто находится рядом во время смерти юнита другой фракции, а также за некоторые другие действия. Всем игрокам фракции начисляется значительное количество баллов в случае достижения основной командной цели.

 

Правила игры практически полностью соответствуют классическим канонам жанра. Фракционные базы соединены тремя дорожками (верхней, центральной и нижней), в промежутках между которыми находятся лесные массивы. На самих дорожках находятся охранные башни: по 2 на дорожку от каждой фракции. Таким образом, в начале игры на карте присутствует 14 строений. С определённым периодом база каждой фракции генерирует 3 одинаковых отряда приспешников («миньонов») волшебников: по одному на каждую дорожку. Они сразу же устремляются по своей дорожке в направлении базы противоположной фракции, атакуя всех противников на пути.

 

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

 

В Раунде 1 вам предстоит изучить правила игры и освоить базовое управление волшебником. В каждой игре данного этапа примет участие 10 игроков, которые будут распределены по двум фракциям. В данном режиме волшебникам доступны только удар посохом и заклинание «Магическая ракета», а количество жизненной энергии всех строений составляет половину от нормального. Коэффициент повреждения при случайном попадании по дружественному волшебнику равен 25%. Независимо от этапа чемпионата, волшебник не может нанести урон дружественным миньонам и строениям. Раунд 1, как и все последующие этапы, состоит из двух частей, между которыми будет небольшой перерыв (с возобновлением работы Песочницы), который позволит улучшить свою стратегию. Для игр в каждой части выбирается последняя стратегия, отправленная игроком до начала этой части. Игры проводятся волнами. В каждой волне каждый игрок участвует ровно в одной игре. Количество волн в каждой части определяется возможностями тестирующей системы, но гарантируется, что оно не будет меньше десяти. 300 участников с наиболее высоким рейтингом пройдут в Раунд 2. Также в Раунд 2 будет проведён добор 60 участников с наибольшим рейтингом в Песочнице (на момент начала Раунда 2) из числа тех, кто не прошёл по итогам Раунда 1.

 

 

В Раунде 2 вам предстоит улучшить свои навыки управления волшебником, а также изучить механику получения волшебником новых уровней и изучения им умений. Правильный подход к выбору и использованию умений является ключом к победе в данном этапе. Компоновка игр осуществляется аналогично Раунду 1. Строения в данном этапе имеют нормальное количество жизненной энергии, а коэффициент повреждения при случайном попадании по дружественному волшебнику равен 50%. Дополнительно усложняет задачу то, что после подведения итогов Раунда 1 часть слабых стратегий будет отсеяна и вам придётся противостоять более сильным соперникам. По итогам Раунда 2 лучшие 50 стратегий попадут в Финал. Также в Финал будет проведен добор 10 участников с наибольшим рейтингом в Песочнице (на момент начала Финала) из числа тех, кто не прошёл в рамках основного турнира.

 

Финал является самым серьёзным этапом. После отбора, проведённого по итогам двух первых этапов, останутся сильнейшие. И в каждой игре вам придётся сойтись лицом к лицу с одним из них. Именно так. Для управления пятью волшебниками одной фракции будет запущено 5 экземпляров вашей стратегии. Для управления пятью волшебниками противоположной фракции будет запущено 5 экземпляров стратегии оппонента. Коэффициент повреждения при случайном попадании по дружественному волшебнику равен 100%. Для определения победителя баллы, набранные всеми волшебниками каждой из фракций, будут просуммированы. В остальном правила игры не будут отличаться от правил Раунда 2. Система проведения Финала имеет свои особенности. Этап по-прежнему делится на две части, однако они уже не будут состоять из волн. В каждой части этапа будут проведены игры между всеми парами участников Финала. Если позволит время и возможности тестирующей системы, операция будет повторена.

 

Russian AI Cup — крупнейший в России ежегодный чемпионат по программированию искусственного интеллекта и третье по счёту открытое соревнование для талантливых IT-специалистов, являющееся частью стратегии Mail.Ru Group по формированию и развитию конкурентной в мировых масштабах российской IT-индустрии. В рамках этой серии соревнований также проводятся:

  • Russian Code Cup — крупнейшая в России ежегодная олимпиада по спортивному программированию, проходящая под эгидой Mail.Ru Group. Это «гоночная трасса» для самых быстрых умов, на которой можно проверить свои навыки в состязании с сильнейшими соперниками и заявить о себе на всю IT-среду. В первом Russian Code Cup приняли участие более 3000 человек. Задачи для участников и техническую часть олимпиады вместе с Mail.Ru Group обеспечивают эксперты Санкт-Петербургского национального исследовательского университета информационных технологий, механики и оптики (СПбНИУ ИТМО).

  • Russian Design Cup — это открытый конкурс как для сильных, так и для начинающих дизайнеров и проектировщиков интерфейсов. Задачи, разработанные специалистами IT-отрасли, ждут тех, кто хочет показать свои способности в дизайне и готов это доказать в честной борьбе с участниками из разных городов. Задачи для Russian Design Cup будут похожи на те, с которыми команды дизайнеров Mail.Ru Group регулярно сталкиваются в реальной жизни.

  • В 2014 году Mail.Ru Group запустила ещё одно соревнование: Russian Developers Cup. Russian Developers Cup — конкурс, нацеленный на создание эффективных команд для разработки оригинальных проектов: от идеи до рабочего прототипа. В ходе конкурса участники смогут не только заявить о своей уникальной идее, но и получить советы и помощь IT сообщества, а также собрать свою команду для успешной разработки и возможной реализации проекта за пределами чемпионата и привлечь к своему проекту внимание инвесторов. Призовой фонд чемпионата 2014 года составил $18000.

 

Ссылка на комментарий
Поделиться на другие сайты

×
×
  • Создать...

Важная информация

Находясь на нашем сайте, Вы автоматически соглашаетесь соблюдать наши Условия использования.