Splix.io - Король землі


37

Ви заповзятлива точка, яка хоче збільшити землю під її контролем. Це досить просто - виїжджайте за межі своєї поточної землі і повертайтеся назад у свою землю, і все, що знаходиться в цій петлі, зараз належить вам. Але є улов. Якщо якась інша крапка якось знайде вашу петлю і перетне її, ти помреш.

Якщо ви ще не пробували цього, перейдіть на Splix.io і спробуйте гру. Використовуйте клавіші зі стрілками для управління своїм рухом.

GIF

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

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

Особливості

Усі гравці починаються з випадкових позицій на дошці 200x200. (Я залишаю за собою право змінити це :). У вас буде певна кількість рухів, щоб набрати якомога більше очок. Бали підсумовані:

  • Кількість гравців, яких ви вбили разів 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 (x = 1; y = 0)
    • West (x = -1; y = 0)
    • North (x = 0; y = 1)
    • South (x = 0; y = -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свого бота та переконайтесь, що вона лише призупиняє поточну нитку. Далі натисніть на кнопку запуску в інтерфейсі користувача і перегляньте свій код.

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

Тепер, щоб скласти все це разом:

Запуск ботів

Щоб запустити своїх ботів з іншими, вам потрібно запустити банку на сторінці випусків. Ось список прапорів:

  • --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

Як довго ви чекали? ;) Чи плануєте ви подати заявку?
Дж. Аткін

Я не знаю, чи зможу я вирішити подібне завдання, оскільки я в основному пишу програми в езоланг. Але я побачив це в пісочниці, і це виглядало як великий виклик!
МД XF

@hyperneutrino Я бачив правки, чи це вас насправді турбує? Політична коректність ніде не підпадає під цю посаду, і це абсолютно правильна граматика англійської мови ...
J Atkin

2
0.о малий світ? Я знаю (про) розробника splix.io. (Твітив (ла))
Твіт твітив ла njega 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();
    }
}

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. В EC є інтерфейс пари. Ви можете використовувати 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;}

}

Один з найосновніших ботів. Він шукає на дошці місця для вбивства інших, і він буде слідувати найкоротшому шляху, щоб дістатися до місця вбивства. Якщо він знаходиться за межами своєї території, він буде робити випадкові кроки, поки не буде ще одне відкриття для вбивства іншого гравця. Він має певну логіку, щоб не допустити, щоб він перебігав над собою, а коли всі інші гравці мертві, він повертається додому. Потрапивши додому, він просто пройде в невеликій площі.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.