Splix.io - Rey de la tierra

37

Eres un punto emprendedor que quiere aumentar la tierra bajo su control. Esto es bastante simple: viaja fuera de tu tierra actual y regresa a tu tierra y todo en ese circuito ahora es tuyo. Pero hay una trampa. Si algún otro punto de alguna manera encuentra tu lazo y lo cruza, mueres.

Si aún no lo has probado, ve a Splix.io y prueba un juego. Usa los cursores para controlar tu movimiento.

GIF

ingrese la descripción de la imagen aquí

Crédito: http://splix.io/

Detalles específicos

Todos los jugadores comienzan en posiciones aleatorias en un tablero de 200x200. (Me reservo el derecho de cambiar esto :). Tendrás una cierta cantidad de movimientos para acumular la mayor cantidad de puntos posible. Los puntos son contados por:

  • El número de jugadores que has matado multiplicado por 300
  • La cantidad de tierra que posees al final de la ronda

Esto trae a colación el punto de que otros pueden robar tu tierra. Si comienzan un ciclo que cruza parte de su tierra, pueden reclamarlo. Si mueres durante la ronda, pierdes todos los puntos de esa ronda.

Cada ronda tiene un grupo de jugadores seleccionados al azar (máximo 5 jugadores únicos) (sujeto a cambios). Cada jugador participa en un número igual de rondas. El puntaje final de tu bot está determinado por su puntaje promedio por juego. Cada juego consta de 2000 turnos (también sujetos a cambios). Todos los bots hacen sus movimientos al mismo tiempo.

Casos de muerte

Cabeza a tope

cabeza a tope

Ambos jugadores mueren cuando se topan mutuamente. Esto sigue siendo cierto incluso cuando ambos jugadores están al borde de su espacio.

cabeza a tope

Sin embargo, cuando solo uno de los jugadores está en su tierra, el otro jugador muere.

ingrese la descripción de la imagen aquí

Cruz de línea

ingrese la descripción de la imagen aquí

En este caso, solo el jugador morado muere.

No puedes cruzar tu propia línea.

ingrese la descripción de la imagen aquí

Saliendo del tablero

jugador fuera del tablero

Si un jugador intenta salir del tablero, morirá y perderá todos los puntos.

Área de captura

Un jugador capturará el área cuando tenga un rastro y vuelva a entrar en su propia tierra.

ingrese la descripción de la imagen aquí

El rojo se llena entre las dos líneas rojas. El único caso en el que un jugador no completa es cuando otro jugador está dentro del circuito. Para ser claros, esto solo se aplica cuando el otro jugador está en el circuito, no solo la tierra que posee. Un jugador puede capturar tierras de otra persona. Si un jugador no puede completar el área rodeada por su rastro, el rastro se convierte directamente en tierra normal. Si el jugador dentro del circuito terrestre de otro jugador muere, el área en ese circuito se llena. Cada vez que un jugador muere, el tablero se vuelve a examinar para un área que se puede completar.

Detalles del controlador

El controlador es aquí . Es muy similar al juego original, pero se han realizado pequeños cambios para hacer que este se ajuste mejor a KotH y por razones técnicas. Está construido con @NathanMerrill 's biblioteca KotHComm , y con la ayuda sustancial de @NathanMerrill también. Avíseme de cualquier error que encuentre en el controlador en la sala de chat . Para ser coherente con KotHComm, he usado colecciones de Eclipse en todo el controlador, pero los bots pueden escribirse usando solo la biblioteca de Colecciones de Java.

Todo está empaquetado en un uberjar en la página de versiones de github . Para usarlo, descárguelo y adjúntelo a su proyecto para que pueda usarlo para completar automáticamente (instrucciones para IntelliJ , Eclipse ). Para probar sus envíos, ejecuta el jar con java -jar SplixKoTH-all.jar -d path\to\submissions\folder. Asegúrese de que path\to\submissions\foldertenga una subfoler llamada javay coloque todos sus archivos allí. No use nombres de paquetes en sus bots (aunque puede ser posible con KotHComm, es solo un poco más de problemas). Para ver todas las opciones, use --help. Para cargar todos los bots, use --question-id 126815.

Escribiendo un bot

Para comenzar a escribir un bot, debes extender SplixPlayer .

  • Direction makeMove(ReadOnlyGame game, ReadOnlyBoard board)
    • Aquí es donde decides qué movimiento quieres que haga tu bot. No debe devolver nulo.
  • HiddenPlayer getThisHidden()
    • Obtenga la HiddenPlayerversión de this. Útil para comparar tu bot con el tablero.

enum Direction

  • Valores
    • East (x = 1; y = 0)
    • West (x = -1; y = 0)
    • North (x = 0; y = 1)
    • South (x = 0; y = -1)
  • Direction leftTurn()
    • Obtenga lo Directionque obtendría si girara a la izquierda.
  • Direction RightTurn()
    • Obtenga lo Directionque obtendría si girara a la derecha.

ReadOnlyBoard

Esta es la clase donde accedes al tablero. Puede obtener una vista local (20x20) del tablero con las posiciones de los jugadores mostradas, o una vista global (todo el tablero) con solo la información de quién posee y reclama las posiciones en el tablero. Aquí también es donde obtienes tu posición.

  • SquareRegion getBounds()
    • Recupera el tamaño del tablero.
  • MutableMap<com.nmerrill.kothcomm.game.maps.Point2D,ReadOnlySplixPoint> getGlobal()
    • Obtenga un mapa global del tablero.
  • MutableMap<com.nmerrill.kothcomm.game.maps.Point2D,ReadOnlySplixPoint> getView()
    • Igual que getGlobal(), excepto que está limitado a un área de 20x20 alrededor de su jugador, y que muestra las posiciones de los jugadores.
  • Point2D getPosition(SplixPlayer me)
    • Obtén la posición de tu jugador. Usar como board.getPosition(this).
  • Point2D getSelfPosition(ReadOnlyBoard)
    • Obtenga su posición en el tablero. Uso:Point2D mypos = getSelfPosition(board)

ReadOnlyGame

ReadOnlyGamesolo proporciona acceso a la cantidad de turnos que quedan en el juego int getRemainingIterations().

ReadOnlySplixPoint

  • HiddenPlayer getClaimer()
    • Obtenga la HiddenPlayerversión de quién reclama un punto: reclamar = un rastro.
  • HiddenPlayer getOwner()
    • Averigua quién posee un punto.
  • HiddenPlayer getWhosOnSpot()
    • Si el jugador está posicionado en este punto, devuelve la versión oculta del mismo. Solo funciona en getLocal().

Point2D

A diferencia de las otras clases aquí, Point2Destá contenida en la biblioteca 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)
    • Envuelva el xvalor para estar dentro del rango de maxX.
  • Point2D wrapY(int maxY)
    • Envuelva el yvalor para estar dentro del rango de maxY.
  • int cartesianDistance(Point2D other)
    • Esto se traduce en cuántos turnos le tomaría a un jugador moverse del punto a al punto b.

Soporte Clojure

El compilador de Clojure se incluye con el SplixKoTH-all.jar, por lo que puede usar Clojure para su bot. Consulte mi random_botpara ver cómo usarlo.

Depuración de un bot

El controlador viene con un depurador para ayudar a probar estrategias. Para comenzar, ejecuta el frasco con el--gui opción.

Para adjuntar el depurador a su jar, siga estas instrucciones para IntelliJ, o estas instrucciones para Eclipse (versión de Eclipse sin probar).

ingrese la descripción de la imagen aquí

Si está usando un depurador con su código, puede usar esto para ayudar a visualizar lo que su bot está viendo. Establezca un punto de interrupción al comienzo de makeMovesu bot y asegúrese de que solo pause el hilo actual. A continuación, haga clic en el botón de inicio en la interfaz de usuario y revise su código.

ingrese la descripción de la imagen aquí

Ahora, para ponerlo todo junto:

Ejecutando bots

Para ejecutar sus bots con otros, debe ejecutar el jar en la página de lanzamientos. Aquí hay una lista de banderas:

  • --iterations( -i) <= int(predeterminado 500)
    • Especifique el número de juegos para ejecutar.
  • --test-bot( -t) <=String
    • Ejecute solo los juegos en los que está incluido el bot.
  • --directory( -d) <= Ruta
    • El directorio desde donde se ejecutarán los envíos. Use esto para ejecutar sus bots. Asegúrese de que sus bots estén en una subcarpeta de la ruta nombrada java.
  • --question-id( -q) <= int(solo uso 126815)
    • Descargue y compile los otros envíos del sitio.
  • --random-seed( -r) <=int (predeterminado a un número aleatorio)
    • Dele una semilla al corredor para que los bots que usan aleatoriamente puedan reproducir los resultados.
  • --gui (-g )
    • Ejecute la interfaz de usuario del depurador en lugar de ejecutar un torneo. Mejor usado con --test-bot.
  • --multi-thread( -m) <= boolean(predeterminadotrue)
    • Ejecute un torneo en modo multihilo. Esto permite un resultado más rápido si su computadora tiene múltiples núcleos.
  • --thread-count( -c) <= int(predeterminado4)
    • Número de subprocesos que se ejecutarán si se permiten subprocesos múltiples.
  • --help( -h)
    • Imprima un mensaje de ayuda similar a este.

Para ejecutar todos los envíos en esta página, use java -jar SplixKoTH-all.jar -q 126815.

Formateando tu publicación

Para asegurarse de que el controlador pueda descargar todos los bots, debe seguir este formato.

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

Además, no use una declaración de paquete.


Marcador

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

Avíseme si alguna parte de las reglas no está clara, o si encuentra algún error en el controlador en la sala de chat .

¡Que te diviertas!

J Atkin
fuente
¡Hola, esto finalmente se publicó! Me preguntaba: D
MD XF
¿Cuánto has estado esperando? ;) ¿Planea enviar?
J Atkin
No sé si podré resolver un desafío como este, ya que principalmente escribo programas en esolangs. ¡Pero lo vi en el cajón de arena y parecía un gran desafío!
MD XF
@hyperneutrino Vi la edición, ¿realmente te molesta? La corrección política no está en ninguna parte del alcance de esta publicación, y es la gramática inglesa perfectamente correcta ...
J Atkin
2
0.o mundo pequeño? Sé (de) el desarrollador de splix.io. (Tuiteó esto @ él)
CAD97

Respuestas:

2

ImNotACoward, Java

Este bot es un experto en supervivencia cobarde . Si no hay ningún enemigo cerca, reclama una parte de la tierra. Si se puede alcanzar el bucle de otro jugador de manera segura, apuñala al otro jugador en la parte posterior y lo involucra en un duelo. Si el otro jugador no puede ser atacado de forma segura, huye realiza una retirada estratégica a su propia tierra.

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

Este es quizás el bot más simple. Todo lo que hace es trazar el borde del tablero, doblarse sobre sí mismo para reducir el riesgo de ser asesinado.

J Atkin
fuente
Genial ver que usaste Eclipse Collections. Hay una interfaz de par en EC. Puede usar Tuples.pair () para obtener una instancia de Pair. También hay una clase PrimitiveTuples si uno o ambos valores en el par son primitivos.
Donald Raab
1

random_bot, Clojure

Esto es RandomBot , pero tuve que apegarme a las convenciones de nomenclatura, y algunos problemas me impiden usar el guión en el nombre, ¡así que subrayan el reinado! El make-movefn devuelve un vec con el primer elemento en el Directionque desea moverse, y el segundo es el estado en el que desea que se le devuelva en el próximo turno. No use átomos externos, ya que este código puede ejecutar múltiples juegos en paralelo.

 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])
J Atkin
fuente
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;}

}

Uno de los bots más básicos. Busca en el tablero puntos para matar a otros, y seguirá el camino más corto posible para llegar a una posición de asesinato. Si está fuera de su territorio, realizará movimientos aleatorios hasta que tenga otra abertura para matar a otro jugador. Tiene cierta lógica para evitar que se atropelle, y cuando todos los demás jugadores están muertos, regresa a su hogar. Una vez en casa, solo entra en una pequeña plaza.

J Atkin
fuente