Splix.io-大地之王


37

您是一个有进取心的人,希望增加其控制下的土地。这很简单-离开您当前的土地,然后循环回到您的土地,该循环中的所有东西现在都归您所有。但是有一个问题!如果其他点以某种方式找到您的循环并穿过该循环,则您将死亡。

如果尚未尝试过,请转到Splix.io并尝试游戏。使用箭头键控制您的移动。

GIF

在此处输入图片说明

图片来源:http//splix.io/

细节

所有玩家都从200x200板上的随机位置开始。(我保留更改此内容的权利:)。您将有一定数量的动作来积累尽可能多的积分。得分如下:

  • 您杀死的玩家人数乘以300
  • 一轮结束时您拥有的土地数量

这就提出了别人可以偷走你的土地的观点。如果他们开始了与您的某些土地相交的循环,那么他们可以要求它。如果您在回合中死亡,则该回合的所有分都将丢失。

每个回合都有一个随机选择的玩家组(最多5个唯一的玩家)(可能会发生变化)。每个玩家都参加相同数量的回合。您的机器人的最终得分取决于其平均每游戏得分。每个游戏包括2000回合(可能随时更改)。所有漫游器都同时移动。

死亡案件

头屁股

头屁股

两名球员在彼此对撞时死亡。即使两个玩家都在自己的空间边缘,这仍然是正确的。

头屁股

但是,当只有一名玩家在他的土地上时,另一名玩家死亡。

在此处输入图片说明

线交叉

在此处输入图片说明

在这种情况下,只有紫色玩家死亡。

你不能越过自己的界限。

在此处输入图片说明

退出董事会

球员离开董事会

如果玩家试图退出棋盘,他将死亡并失去所有积分。

捕获区域

玩家在有足迹并再次进入自己的土地时将占领区域。

在此处输入图片说明

红色填充在两条红线之间。玩家不填写的唯一情况是其他玩家进入循环。需要明确的是,这仅在其他玩家本人处于循环中时才适用,而不仅仅是他所拥有的土地。玩家可以从另一个人手中夺取土地。如果玩家无法填写其足迹周围的区域,则足迹将直接转换为正常土地。如果另一个玩家落地循环中的玩家死亡,则该循环中的区域将被填充。每次玩家死亡时,都会重新检查棋盘上可以填充的区域。

控制器详细信息

控制器在这里。它与原始游戏非常相似,但由于技术原因,已进行了一些小的改动以使其更适合KotH。它是使用@NathanMerrillKotHComm库构建的,并且在@NathanMerrill的大力帮助下也是如此。如果您在聊天室的控制器中发现任何错误,请告诉我。为了与KotHComm保持一致,我在整个控制器中都使用了Eclipse集合,但是只能使用Java集合库来编写机器人。

一切都打包在github版本页面上的uberjar中。要使用它,请下载它并将其附加到您的项目中,以便可以将其用于自动完成(IntelliJ的说明,Eclipse)。要测试您的提交,请使用运行jar 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)
    • 在这里,您可以决定要让机器人执行哪一步。不得返回null。
  • 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选项的jar 。

为了调试器附加到您的罐子,请按照这些指示的IntelliJ,或者这些说明适用于Eclipse(Eclipse版本未经测试)。

在此处输入图片说明

如果您在代码中使用调试器,则可以使用它来帮助可视化您的机器人所看到的内容。makeMove在您的漫游器的开始处设置一个断点,并确保它仅暂停当前线程。接下来,单击UI上的开始按钮,然后逐步执行代码。

在此处输入图片说明

现在,将它们放在一起:

正在运行的机器人

要与其他人一起运行您的机器人,您需要在发布页面上运行jar。这是标志列表:

  • --iterations-i)<= int(默认500
    • 指定要运行的游戏数。
  • --test-bot-t)<=String
    • 仅运行该机器人所包含的游戏。
  • --directory-d)<=路径
    • 从中运行提交的目录。使用它来运行您的机器人。确保您的漫游器位于名为的路径的子文件夹中java
  • --question-id-q)<= int(仅使用126815
    • 从站点下载并编译其他提交。
  • --random-seed-r)<= int(默认为随机数)
    • 给跑步者撒一粒种子,这样使用随机的机器人就可以复制结果。
  • --gui-g
    • 运行调试器ui而不是运行锦标赛。最好与结合使用--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

你等了多久了?;)您打算提交吗?
J Atkin

我不知道我是否能够解决这样的挑战,因为我主要用esolangs编写程序。但是我在沙盒中看到了它,这看起来像是一个巨大的挑战!
MD XF

@hyperneutrino我看到了编辑,它真的困扰您吗?政治上的正确性在这篇文章的范围内无处可寻,而且它是完全正确的英语语法...
J Atkin

2
0.o小世界?我知道splix.io的开发人员。(
@tweet上发布

Answers:


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();
    }
}

有趣。非常好!我不知道这可以做得更好...
J Atkin

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,克洛瑞尔

这是RandomBot,但是我必须遵守命名约定,并且某些问题使我无法在名称中使用连字符,因此强调下划线!该make-moveFN返回A 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

Java的HunterBot

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;}

}

最基本的机器人之一。它会在棋盘上搜寻可以杀死其他人的地点,然后将按照最短的路径杀人。如果它不在其领土内,它将随机移动,直到它具有另一个开口杀死另一个玩家。它有一些逻辑可以防止它继续运行,并且当所有其他玩家都死了时,它可以返回它的家中。到家后,它会进入一个小方块。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.