Splix.io - король земли

37

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

Если вы еще не пробовали, перейдите на Splix.io и попробуйте игру. Используйте клавиши со стрелками для управления вашим движением.

GIF

введите описание изображения здесь

Кредит: http://splix.io/

конкретика

Все игроки начинают со случайных позиций на доске 200х200. (Я оставляю за собой право изменить это :). У вас будет определенное количество ходов, чтобы набрать как можно больше очков. Очки подсчитываются по:

  • Количество игроков, которых вы убили раз 300
  • Количество земли, которой вы владеете в конце раунда

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

Каждый раунд имеет случайно выбранную группу игроков (максимум 5 уникальных игроков) (возможны изменения). Каждый игрок участвует в равном количестве раундов. Окончательный счет вашего бота определяется его средним за игру счетом. Каждая игра состоит из 2000 ходов (также могут быть изменены). Все боты делают свои ходы одновременно.

Случаи смерти

Голова прикладом

прикладом

Оба игрока умирают, когда они сталкиваются друг с другом. Это все еще верно, даже когда оба игрока находятся на краю своего пространства.

прикладом

Однако, когда только один из игроков находится на своей земле, другой игрок умирает.

введите описание изображения здесь

Линия Крест

введите описание изображения здесь

В этом случае умирает только фиолетовый игрок.

Вы не можете пересечь свою собственную линию.

введите описание изображения здесь

Выход из доски

игрок сходит с доски

Если игрок попытается покинуть игровое поле, он умрет и потеряет все очки.

Захват области

Игрок захватит область, когда у него будет след, и он снова войдет в свою землю.

введите описание изображения здесь

Красный цвет заполняет две красные линии. Единственный случай, когда игрок не заполняется, это когда другой игрок находится в цикле. Чтобы было ясно, это применимо только тогда, когда другой игрок сам находится в цикле, а не только земля, принадлежащая ему. Игрок может захватить землю у другого человека. Если игрок не может заполнить область, окруженную его следом, след превращается непосредственно в обычную землю. Если игрок внутри лупа другого игрока умирает, область в этом лупе заполняется. Каждый раз, когда игрок умирает, доска пересматривается для области, которая может быть заполнена.

Детали контроллера

Контроллер здесь . Она очень похожа на оригинальную игру, но были внесены небольшие изменения, чтобы сделать ее более подходящей для KotH и по техническим причинам. Он построен с помощью библиотеки @ NathanMerrill 's KotHComm , а также при значительной помощи @NathanMerrill. Пожалуйста, дайте мне знать о любых ошибках, которые вы найдете в контроллере в чате . Чтобы соответствовать KotHComm, я использовал коллекции Eclipse по всему контроллеру, но боты могут быть написаны с использованием только библиотеки Java Collections.

Все упаковано в uberjar на странице релизов github . Чтобы использовать его, загрузите его и прикрепите к своему проекту, чтобы вы могли использовать его для автозаполнения (инструкции для IntelliJ , Eclipse ). Чтобы проверить ваши представления, вы запускаете банку с java -jar SplixKoTH-all.jar -d path\to\submissions\folder. Убедитесь, что у вас path\to\submissions\folderесть имя субфолера java, и разместите там все ваши файлы. Не используйте имена пакетов в своих ботах (хотя это может быть возможно с KotHComm, это просто немного больше проблем). Чтобы увидеть все варианты, используйте --help. Чтобы загрузить все боты, используйте --question-id 126815.

Написание бота

Чтобы начать писать бота, вы должны продлить SplixPlayer.

  • Direction makeMove(ReadOnlyGame game, ReadOnlyBoard board)
    • Именно здесь вы решаете, какой шаг вы хотите сделать своим ботом. Не должен возвращать ноль.
  • HiddenPlayer getThisHidden()
    • Получить HiddenPlayerверсию this. Полезно для сравнения вашего бота с доской.

enum Direction

  • Значения
    • East (х = 1; у = 0)
    • West (х = -1; у = 0)
    • North (х = 0; у = 1)
    • South (х = 0; у = -1)
  • Direction leftTurn()
    • Получить то, Directionчто вы получите, если вы сделали левый поворот.
  • Direction RightTurn()
    • Получить то, Directionчто вы получите, если вы сделали правильный поворот.

ReadOnlyBoard

Это класс, где вы получаете доступ к доске. Вы можете получить либо локальное представление (20x20) доски с указанием позиций игрока, либо глобальное представление (всю доску) только с информацией о том, кто владеет и претендует на позиции на доске. Это также, где вы получаете свою позицию.

  • SquareRegion getBounds()
    • Получить размер доски.
  • MutableMap<com.nmerrill.kothcomm.game.maps.Point2D,ReadOnlySplixPoint> getGlobal()
    • Получить глобальную карту доски.
  • MutableMap<com.nmerrill.kothcomm.game.maps.Point2D,ReadOnlySplixPoint> getView()
    • То же самое getGlobal(), за исключением того, что оно ограничено областью 20x20 вокруг вашего игрока и показывает позиции игрока.
  • Point2D getPosition(SplixPlayer me)
    • Получить позицию вашего игрока. Использовать как board.getPosition(this).
  • Point2D getSelfPosition(ReadOnlyBoard)
    • Получить свою позицию на доске. Использование:Point2D mypos = getSelfPosition(board)

ReadOnlyGame

ReadOnlyGameтолько обеспечивает доступ к количеству ходов, оставшихся в игре до конца int getRemainingIterations().

ReadOnlySplixPoint

  • HiddenPlayer getClaimer()
    • Получить HiddenPlayerверсию о том, кто претендует на очко - утверждая = след.
  • HiddenPlayer getOwner()
    • Получите, кому принадлежит точка.
  • HiddenPlayer getWhosOnSpot()
    • Если игрок находится в этой точке, верните скрытую версию. Работает только в getLocal().

Point2D

В отличие от других классов здесь, Point2Dсодержится в библиотеке KotHComm.com.nmerrill.kothcomm.game.maps.Point2D

  • Point2D(int x, int y)
  • int getX()
  • int getY()
  • Point2D moveX(int x)
  • Point2D moveY(int y)
  • Point2D wrapX(int maxX)
    • Оберните xзначение, чтобы быть в пределах диапазона maxX.
  • Point2D wrapY(int maxY)
    • Оберните yзначение, чтобы быть в пределах диапазона maxY.
  • int cartesianDistance(Point2D other)
    • Это переводит на то, сколько ходов потребуется игроку, чтобы переместиться из пункта a в пункт b.

Поддержка Clojure

Компилятор Clojure в комплекте с SplixKoTH-all.jar, так что вы можете использовать Clojure для своего бота! Обратитесь к моему, random_botчтобы увидеть, как его использовать.

Отладка бота

Контроллер поставляется с отладчиком для тестирования стратегий. Чтобы запустить его, запустите банку с --guiопцией.

Чтобы прикрепить отладчик к банке, следуйте этим инструкциям для IntelliJ или этим инструкциям для Eclipse (версия Eclipse не проверена).

введите описание изображения здесь

Если вы используете отладчик с вашим кодом, вы можете использовать это, чтобы помочь визуализировать то, что видит ваш бот. Установите точку останова в начале makeMoveвашего бота и убедитесь, что он останавливает только текущий поток. Затем нажмите кнопку «Пуск» в пользовательском интерфейсе и выполните пошаговый просмотр кода.

введите описание изображения здесь

Теперь, чтобы сложить все вместе:

Бегущие боты

Чтобы запускать своих ботов вместе с другими, вам нужно запустить jar на странице релизов. Вот список флагов:

  • --iterations( -i) <= int(по умолчанию 500)
    • Укажите количество игр для запуска.
  • --test-bot( -t) <=String
    • Запускайте только те игры, в которые включен бот.
  • --directory( -d) <= Путь
    • Каталог для запуска представлений. Используйте это, чтобы запустить своих ботов. Убедитесь, что ваши боты находятся в подпапке пути с именем java.
  • --question-id( -q) <= int(только использовать 126815)
    • Скачайте и скомпилируйте другие материалы с сайта.
  • --random-seed( -r) <= int(по умолчанию случайное число)
    • Дайте семя бегуну, чтобы у ботов, использующих случайное число, могли быть воспроизведены результаты.
  • --gui( -g)
    • Запустите интерфейс отладчика вместо запуска турнира. Лучше всего использовать с --test-bot.
  • --multi-thread( -m) <= boolean(по умолчанию true)
    • Запустите турнир в многопоточном режиме. Это обеспечивает более быстрый результат, если ваш компьютер имеет несколько ядер.
  • --thread-count( -c) <= int(по умолчанию 4)
    • Количество потоков для запуска, если разрешено использование нескольких потоков.
  • --help( -h)
    • Распечатать справочное сообщение, подобное этому.

Чтобы запустить все представления на этой странице, используйте java -jar SplixKoTH-all.jar -q 126815.

Форматирование вашего поста

Чтобы гарантировать, что контроллер может загрузить все боты, вы должны следовать этому формату.

[BotName], Java                     // this is a header
                                    // any explanation you want
[BotName].java                      // filename, in the codeblock
[code]

Также не используйте декларацию пакета.


Табло

+------+--------------+-----------+
| Rank | Name         |     Score |
+------+--------------+-----------+
|    1 | ImNotACoward | 8940444.0 |
|    2 | TrapBot      |  257328.0 |
|    3 | HunterBot    |  218382.0 |
+------+--------------+-----------+

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

Повеселись!

Дж Аткин
источник
Эй, это наконец опубликовано! Мне было интересно: D
MD XF
Как долго ты ожидаешь? ;) Планируете ли вы представить?
Дж Аткин
Я не знаю, смогу ли я решить такую ​​задачу, так как я пишу программы в основном на esolangs. Но я видел это в песочнице, и это выглядело как большой вызов!
MD XF
@hyperneutrino Я видел редактирование, это действительно беспокоит тебя? Политическая корректность нигде не входит в сферу действия этого поста, и это совершенно правильная грамматика английского языка ...
J Atkin
2
0.о маленький мир? Я знаю (о) разработчика splix.io. (Твитнул это @ ему)
CAD97

Ответы:

2

ImNotACoward, Java

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

ImNotACoward.java
import org.eclipse.collections.api.map.MutableMap;
import org.eclipse.collections.api.set.MutableSet;
import org.eclipse.collections.impl.factory.Lists;
import org.eclipse.collections.impl.factory.Maps;
import org.eclipse.collections.impl.factory.Sets;

import com.jatkin.splixkoth.ppcg.game.Direction;
import com.jatkin.splixkoth.ppcg.game.SplixPlayer;
import com.jatkin.splixkoth.ppcg.game.readonly.HiddenPlayer;
import com.jatkin.splixkoth.ppcg.game.readonly.ReadOnlyBoard;
import com.jatkin.splixkoth.ppcg.game.readonly.ReadOnlyGame;
import com.jatkin.splixkoth.ppcg.game.readonly.ReadOnlySplixPoint;
import com.nmerrill.kothcomm.game.maps.Point2D;
import com.nmerrill.kothcomm.game.maps.graphmaps.bounds.point2D.SquareRegion;

public class ImNotACoward extends SplixPlayer {
    private static final MutableSet<Direction> DIRECTIONS = Sets.mutable.of(Direction.values());

    private static class Board {
        public MutableSet<Point2D> allPoints = null;
        private SquareRegion globalBounds = null;
        private SquareRegion viewBounds = null;
        private MutableMap<Point2D, ReadOnlySplixPoint> global = null;
        private MutableMap<Point2D, ReadOnlySplixPoint> view = null;

        public void update(ReadOnlyBoard readOnlyBoard) {
            if (this.allPoints == null) {
                this.allPoints = readOnlyBoard.getGlobal().keysView().toSet();
                this.globalBounds = readOnlyBoard.getBounds();
            }
            this.viewBounds = readOnlyBoard.viewingArea;
            this.global = readOnlyBoard.getGlobal();
            this.view = readOnlyBoard.getView();
        }

        public boolean inBounds(Point2D point) {
            return globalBounds.inBounds(point);
        }

        public boolean inView(Point2D point) {
            return viewBounds.inBounds(point);
        }

        public ReadOnlySplixPoint getSplixPoint(Point2D point) {
            return inView(point) ? view.get(point) : global.get(point);
        }

        public MutableSet<Point2D> getNeighbors(Point2D point) {
            return DIRECTIONS.collect(d -> point.move(d.vector.getX(), d.vector.getY())).select(this::inBounds);
        }

        public MutableSet<Point2D> getNeighbors(MutableSet<Point2D> points) {
            return points.flatCollect(this::getNeighbors);
        }

        public MutableSet<Point2D> getBorders(SquareRegion region) {
            return allPoints.select(p -> region.inBounds(p) &&
                    (p.getX() == region.getLeft() || p.getX() == region.getRight() ||
                    p.getY() == region.getTop() || p.getY() == region.getBottom() ||
                    p.getX() == globalBounds.getLeft() || p.getX() == globalBounds.getRight() ||
                    p.getY() == globalBounds.getTop() || p.getY() == globalBounds.getBottom()));
        }
    }

    private class Player {
        public final HiddenPlayer hiddenPlayer;
        public MutableSet<Point2D> owned = Sets.mutable.empty();
        private MutableSet<Point2D> unowned = null;
        private MutableSet<Point2D> oldClaimed = Sets.mutable.empty();
        public MutableSet<Point2D> claimed = Sets.mutable.empty();
        private MutableSet<Point2D> oldPos = Sets.mutable.empty();
        public MutableSet<Point2D> pos = Sets.mutable.empty();

        public Player(HiddenPlayer hiddenPlayer) {
            super();
            this.hiddenPlayer = hiddenPlayer;
        }

        public void nextMove() {
            owned.clear();
            unowned = null;
            oldClaimed = claimed;
            claimed = Sets.mutable.empty();
            oldPos = pos;
            pos = Sets.mutable.empty();
        }

        public MutableSet<Point2D> getUnowned() {
            if (unowned == null) {
                unowned = board.allPoints.difference(owned);
            }
            return unowned;
        }

        public void addOwned(Point2D point) {
            owned.add(point);
        }

        public void addClaimed(Point2D point) {
            claimed.add(point);
        }

        public void setPos(Point2D point) {
            pos.clear();
            pos.add(point);
        }

        public void calcPos() {
            if (pos.isEmpty()) {
                MutableSet<Point2D> claimedDiff = claimed.difference(oldClaimed);
                if (claimedDiff.size() == 1) {
                    pos = board.getNeighbors(claimedDiff).select(p -> !claimed.contains(p) && !board.inView(p));
                } else if (!oldPos.isEmpty()) {
                    pos = board.getNeighbors(oldPos).select(p -> owned.contains(p) && !board.inView(p));
                } else {
                    pos = owned.select(p -> !board.inView(p));
                }
            }
        }
    }

    private Board board = new Board();
    private Point2D myPos = null;
    private final Player nobody = new Player(new HiddenPlayer(null));
    private final Player me = new Player(new HiddenPlayer(this));
    private MutableMap<HiddenPlayer, Player> enemies = Maps.mutable.empty();
    private MutableMap<HiddenPlayer, Player> players = Maps.mutable.of(nobody.hiddenPlayer, nobody, me.hiddenPlayer, me);
    private MutableSet<Point2D> path = Sets.mutable.empty();

    private Player getPlayer(HiddenPlayer hiddenPlayer) {
        Player player = players.get(hiddenPlayer);
        if (player == null) {
            player = new Player(hiddenPlayer);
            players.put(player.hiddenPlayer, player);
            enemies.put(player.hiddenPlayer, player);
        }
        return player;
    }

    private Direction moveToOwned() {
        MutableSet<Point2D> targets = me.owned.difference(me.pos);
        if (targets.isEmpty()) {
            return moveTo(myPos);
        } else {
            return moveTo(targets.minBy(myPos::cartesianDistance));
        }
    }

    private Direction moveTo(Point2D target) {
        return DIRECTIONS.minBy(d -> {
            Point2D p = myPos.move(d.vector.getX(), d.vector.getY());
            return !board.inBounds(p) || me.claimed.contains(p) ? Integer.MAX_VALUE : target.cartesianDistance(p);
        });
    }

    @Override
    protected Direction makeMove(ReadOnlyGame readOnlyGame, ReadOnlyBoard readOnlyBoard) {
        board.update(readOnlyBoard);
        myPos = readOnlyBoard.getPosition(this);
        path.remove(myPos);

        for (Player e : players.valuesView()) {
            e.nextMove();
        }
        for (Point2D point : board.allPoints) {
            ReadOnlySplixPoint splixPoint = board.getSplixPoint(point);
            getPlayer(splixPoint.getOwner()).addOwned(point);
            getPlayer(splixPoint.getClaimer()).addClaimed(point);
            getPlayer(splixPoint.getWhosOnSpot()).setPos(point);
        }
        for (Player e : players.valuesView()) {
            e.calcPos();
        }

        if (me.owned.contains(myPos) && path.allSatisfy(p -> me.owned.contains(p))) {
            path.clear();
        }

        if (path.isEmpty()) {
            MutableSet<Point2D> enemyPositions = enemies.valuesView().flatCollect(e -> e.pos).toSet();
            int enemyDistance = enemyPositions.isEmpty() ? Integer.MAX_VALUE :
                    enemyPositions.minBy(myPos::cartesianDistance).cartesianDistance(myPos);

            if (enemyDistance < 20) {
                MutableSet<Point2D> enemyClaimed = enemies.valuesView().flatCollect(e -> e.claimed).toSet();
                if (!enemyClaimed.isEmpty()) {
                    Point2D closestClaimed = enemyClaimed.minBy(myPos::cartesianDistance);
                    if (closestClaimed.cartesianDistance(myPos) < enemyDistance) {
                        return moveTo(closestClaimed);
                    } else if (enemyDistance < 10) {
                        return moveToOwned();
                    }
                }
            }

            if (me.owned.contains(myPos)) {
                if (!me.getUnowned().isEmpty()) {
                    Point2D target = me.getUnowned().minBy(myPos::cartesianDistance);
                    if (target.cartesianDistance(myPos) > 2) {
                        return moveTo(target);
                    }
                }

                int safeSize = Math.max(1, Math.min(enemyDistance / 6, readOnlyGame.getRemainingIterations() / 4));
                SquareRegion region = Lists.mutable
                        .of(new SquareRegion(myPos, myPos.move(safeSize, safeSize)),
                                new SquareRegion(myPos, myPos.move(safeSize, -safeSize)),
                                new SquareRegion(myPos, myPos.move(-safeSize, safeSize)),
                                new SquareRegion(myPos, myPos.move(-safeSize, -safeSize)))
                        .maxBy(r -> me.getUnowned().count(p -> r.inBounds(p)));
                path = board.getBorders(region);
            } else {
                return moveToOwned();
            }
        }

        if (!path.isEmpty()) {
            return moveTo(path.minBy(myPos::cartesianDistance));
        }

        return moveToOwned();
    }
}
Sleafar
источник
1

TrapBot, Java

TrapBot.java

import com.jatkin.splixkoth.ppcg.game.Direction;
import com.jatkin.splixkoth.ppcg.game.SplixPlayer;
import com.jatkin.splixkoth.ppcg.game.readonly.ReadOnlyBoard;
import com.jatkin.splixkoth.ppcg.game.readonly.ReadOnlyGame;
import com.jatkin.splixkoth.ppcg.game.readonly.ReadOnlySplixPoint;
import com.nmerrill.kothcomm.game.maps.Point2D;
import com.nmerrill.kothcomm.game.maps.graphmaps.bounds.point2D.SquareRegion;
import javafx.util.Pair;
import org.eclipse.collections.api.map.MutableMap;
import org.eclipse.collections.impl.factory.Lists;

import java.util.Comparator;

/**
 * Trap bot goes to the wall and traces the entirety around. Hopes that
 * the players in the middle die and that nobody challenges him. Nearly 
 * all turns are left turns.
 */
public class TrapBot extends SplixPlayer {

    /**
     * Mode when the bot is attempting to reach the wall from it's original spawn
     * location.
     */
    public static final int MODE_GOING_TO_WALL = 1;

    /**
     * Mode when we have reached the wall and are now going around the board.
     */
    public static final int MODE_FOLLOWING_WALL = 2;

    private int mode = MODE_GOING_TO_WALL;

    public static int WALL_EAST = 1;
    public static int WALL_NORTH = 2;
    public static int WALL_WEST = 3;
    public static int WALL_SOUTH = 4;


    /**
     * How long the bot would like to go before he turns around to go back home.
     */
    private static final int PREFERRED_LINE_DIST = 5;

    private int distToTravel = 0;

    private Direction lastMove = Direction.East;// could be anything that's not null
    private int lastTrailLength = 0;

    @Override
    protected Direction makeMove(ReadOnlyGame game, ReadOnlyBoard board) {
        Direction ret = null;
        MutableMap<Point2D, ReadOnlySplixPoint> view = board.getView();
        int trailLength = getTrailLength(board, view);

        if (trailLength == 0) {

            int closestWall = getClosestWall(board);
            Direction directionToWall = getDirectionToWall(closestWall);

            if (lastTrailLength != 0) {
                ret = lastMove.leftTurn();
                // move to the other half of 2 width line so we can start without shifting to the left
            }

            if (mode == MODE_GOING_TO_WALL && ret == null) {
                int distCanTravel = getDistCanTravel(
                        getSelfPosition(board), board.getBounds(), directionToWall);
                if (distCanTravel == 0) mode = MODE_FOLLOWING_WALL;
                else ret = directionToWall;
                distToTravel = distCanTravel;

            }

            if (mode == MODE_FOLLOWING_WALL && ret == null) {
                int distCanTravel = 0;
                ret = directionToWall;
                while (distCanTravel == 0) {// keep turning left until we can get somewhere
                    ret = ret.leftTurn();
                    distCanTravel = getDistCanTravel(
                            getSelfPosition(board), board.getBounds(), ret);
                }

                distToTravel = distCanTravel;
            }
        }

        // once we have started we are on auto pilot (can't run after the before block)
        else if (trailLength == distToTravel || trailLength == (distToTravel + 1))
            ret = lastMove.leftTurn();

        if (ret == null)// if we don't have a move otherwise, we must be on our trail. ret same as last time
            ret = lastMove;

        lastTrailLength = trailLength;
        lastMove = ret;
        return ret;
    }

    int getClosestWall(ReadOnlyBoard board) {
        Point2D thisPos = getSelfPosition(board);
        return Lists.mutable.of(
                new Pair<>(WALL_NORTH, board.getBounds().getTop() - thisPos.getY()),
                new Pair<>(WALL_SOUTH, thisPos.getY()), 
                new Pair<>(WALL_EAST, board.getBounds().getRight() - thisPos.getX()),
                new Pair<>(WALL_WEST, thisPos.getX())
        ).min(Comparator.comparingInt(Pair::getValue)).getKey();
    }

    /**
     * This goes around some intended behavior in the controller to get the correct result. When a player goes outside
     * his territory the land under him is converted to a trail -- on the next step of the game. So a trail length may
     * be the count of the trail locations plus one. That is what this function calculates. Depends on the whole trail
     * being contained inside the view passed to it.
     * @return
     */
    int getTrailLength(ReadOnlyBoard board, MutableMap<Point2D, ReadOnlySplixPoint> view) {
        boolean isPlayerOutsideHome = !view.get(getSelfPosition(board)).getOwner().equals(getThisHidden());
        int trailLength = view.count(rop -> rop.getClaimer().equals(getThisHidden()));
        return trailLength + (isPlayerOutsideHome? 1 : 0);
    }

    /**
     * Calculate how far we can travel in the direction before we hit the wall.
     * @return
     */
    int getDistCanTravel(Point2D currPos, SquareRegion bounds, Direction direction) {
        for (int i = 1; i <= PREFERRED_LINE_DIST; i++) {
            if (!bounds.inBounds(currPos.move(direction.vector.getX()*i, direction.vector.getY()*i)))
                return i-1;
        }
        return PREFERRED_LINE_DIST;
    }

    /**
     * Get which direction needs to be traveled to reach the specified wall.
     * Requires that neither Direction nor the values of `WALL_...` change.
     * @param targetWall
     * @return
     */
    Direction getDirectionToWall(int targetWall) {
        return Direction.values()[targetWall-1];
    }
}

Это, пожалуй, самый простой бот. Все, что он делает, это прослеживает край доски, удваивая себя, чтобы уменьшить риск быть убитым.

Дж Аткин
источник
Приятно видеть, что вы использовали Eclipse Collections. В EC есть интерфейс Pair. Вы можете использовать Tuples.pair (), чтобы получить экземпляр Pair. Существует также класс PrimitiveTuples, если одно или оба значения в паре являются примитивами.
Дональд Рааб
1

random_bot, Clojure

Это RandomBot , но мне пришлось придерживаться соглашений об именах, и некоторые проблемы не позволяют мне использовать дефис в имени, поэтому подчеркиваю царствование! make-moveП возвращает VEC с первым пунктом является Directionвы хотите двигаться, а второй является состояние вы хотите передать обратно к вам на следующий ход. Не используйте внешние атомы, так как этот код может запускать несколько игр параллельно.

 random_bot.clj
 (ns random-bot
     (:import
      [com.jatkin.splixkoth.ppcg.game Direction]))

 (defn make-move [game board state]
       [(rand-nth [Direction/East
                   Direction/West
                   Direction/North
                   Direction/South])
        nil])
Дж Аткин
источник
0

HunterBot, Java

HunterBot.java

import com.jatkin.splixkoth.ppcg.game.Direction;
import com.jatkin.splixkoth.ppcg.game.SplixPlayer;
import com.jatkin.splixkoth.ppcg.game.readonly.HiddenPlayer;
import com.jatkin.splixkoth.ppcg.game.readonly.ReadOnlyBoard;
import com.jatkin.splixkoth.ppcg.game.readonly.ReadOnlyGame;
import com.jatkin.splixkoth.ppcg.game.readonly.ReadOnlySplixPoint;
import com.nmerrill.kothcomm.game.maps.Point2D;
import org.eclipse.collections.api.map.MutableMap;
import org.eclipse.collections.api.set.ImmutableSet;
import org.eclipse.collections.impl.factory.Sets;

import java.util.Comparator;

/**
 * This bot looks for any trail points left behind by another player and sets that as his target. If the target ever
 * disappears, it will continue on in hopes that the player will return soon, or if another target appears, it will
 * go towards that one. Works best when the other player repeatedly goes in the same general direction.
 */
public class HunterBot extends SplixPlayer {

    private Point2D lastTarget;

    private Direction lastMove = Direction.East;

    @Override
    protected Direction makeMove(ReadOnlyGame game, ReadOnlyBoard board) {
        Point2D thisPos = getSelfPosition(board);
        MutableMap<Point2D, ReadOnlySplixPoint> global = board.getGlobal();
        MutableMap<Point2D, ReadOnlySplixPoint> targets = global.select((pt, rosp) ->
                !rosp.getClaimer().equals(getThisHidden()) 
                        && !rosp.getClaimer().equals(new HiddenPlayer(null)));

        if (targets.size() == 0 && lastTarget == null) {
            lastMove = lastMove.leftTurn();
            return lastMove;
        }

        Point2D target = null;
        if (targets.size() == 0) target = lastTarget;
        else target = targets.keysView().min(Comparator.comparingInt(thisPos::cartesianDistance));
        if (target.equals(thisPos)) {
            lastTarget = null;
            if (global.get(thisPos).getOwner().equals(getThisHidden())) {
                lastMove = lastMove.leftTurn();
                return lastMove;
            } else 
            // time to go home
            target = global.select((z_, x) -> getThisHidden().equals(x.getOwner())).keySet().iterator().next();

        }

        lastTarget = target;
        lastMove = makeSafeMove(target, global, board, thisPos);
        return lastMove;
    }

    private Direction makeSafeMove(Point2D targetLocation, MutableMap<Point2D, ReadOnlySplixPoint> map, ReadOnlyBoard board, Point2D currLoc) {
        Point2D dist = targetLocation.move(-currLoc.getX(), -currLoc.getY());
        ImmutableSet<Direction> possibleMoves = Sets.immutable.of(Direction.values())
                .select(x -> {
                    Point2D pos = currLoc.move(x.vector.getX(), x.vector.getY());
                    return !board.getBounds().outOfBounds(pos) && !getThisHidden().equals(map.get(pos).getClaimer());
                });
        Direction prefMove;
        if (Math.abs(dist.getX()) > Math.abs(dist.getY()))
            prefMove = getDirectionFroPoint(new Point2D(normalizeNum(dist.getX()), 0));
        else
            prefMove = getDirectionFroPoint(new Point2D(0, normalizeNum(dist.getY())));

        if (possibleMoves.contains(prefMove)) return prefMove;
        if (possibleMoves.contains(prefMove.leftTurn())) return prefMove.leftTurn();
        if (possibleMoves.contains(prefMove.rightTurn())) return prefMove.rightTurn();
        return prefMove.leftTurn().leftTurn();
    }

    private Direction getDirectionFroPoint(Point2D dir) {
        return Sets.immutable.of(Direction.values()).select(d -> d.vector.equals(dir)).getOnly();
    }

    private int normalizeNum(int n) { if (n < -1) return -1; if (n > 1) return 1; else return n;}

}

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

Дж Аткин
источник