Splix.io - król ziemi

37

Jesteś przedsiębiorczą kropką, która chce powiększyć ziemię pod jej kontrolą. Jest to dość proste - podróżuj poza swoją obecną ziemię i zapętl się z powrotem do swojej ziemi, a wszystko w tej pętli jest teraz własnością ciebie. Ale jest w tym haczyk. Jeśli jakaś inna kropka w jakiś sposób znajdzie twoją pętlę i przekroczy ją, umrzesz.

Jeśli jeszcze tego nie próbowałeś, przejdź do Splix.io i wypróbuj grę. Użyj klawiszy strzałek, aby kontrolować swój ruch.

GIF

wprowadź opis zdjęcia tutaj

Źródło: http://splix.io/

Specyfika

Wszyscy gracze zaczynają od losowych pozycji na planszy 200 x 200. (Zastrzegam sobie prawo do zmiany tego :). Będziesz miał określoną liczbę ruchów, aby zebrać jak najwięcej punktów. Punkty są sumowane przez:

  • Liczba graczy, których zabiłeś, wynosi 300
  • Ilość ziemi, którą posiadasz na koniec rundy

To powoduje, że inni mogą ukraść twoją ziemię. Jeśli zaczną pętlę, która przecina część twojej ziemi, mogą ją przejąć. Jeśli zginiesz podczas rundy, tracisz wszystkie punkty za tę rundę.

Każda runda ma losowo wybraną grupę graczy (maksymalnie 5 unikalnych graczy) (może ulec zmianie). Każdy gracz uczestniczy w równej liczbie rund. Ostateczny wynik twojego bota zależy od jego średniej oceny na grę. Każda gra składa się z 2000 tur (również może ulec zmianie). Wszystkie boty wykonują ruchy w tym samym czasie.

Przypadki śmierci

Głowa tyłek

głowa tyłek

Obaj gracze umierają, gdy stykają się ze sobą. Jest to nadal prawdą, nawet gdy obaj gracze są na krawędzi swojego pola.

głowa tyłek

Jednak gdy tylko jeden z graczy znajduje się na jego ziemi, drugi gracz umiera.

wprowadź opis zdjęcia tutaj

Linia krzyżowa

wprowadź opis zdjęcia tutaj

W takim przypadku tylko fioletowy gracz umiera.

Nie możesz przekroczyć własnej linii.

wprowadź opis zdjęcia tutaj

Wyjście z planszy

gracz schodzi z planszy

Jeśli gracz spróbuje wyjść z planszy, umrze i straci wszystkie punkty.

Obszar przechwytywania

Gracz przejmie obszar, gdy będzie miał ślad i ponownie wkroczy na swoją ziemię.

wprowadź opis zdjęcia tutaj

Czerwony wypełnia dwie czerwone linie. Jedynym przypadkiem, w którym gracz nie wypełnia, jest sytuacja, gdy inny gracz znajduje się w pętli. Żeby było jasne, dotyczy to tylko sytuacji, gdy inny gracz jest w pętli, a nie tylko posiadanej przez niego ziemi. Gracz może przejąć ziemię od innej osoby. Jeśli gracz nie może wypełnić obszaru otoczonego szlakiem, szlak przekształca się bezpośrednio w normalny ląd. Jeśli gracz w pętli lądowej innego gracza umiera, obszar w tej pętli jest zapełniony. Za każdym razem, gdy gracz umiera, plansza jest ponownie sprawdzana pod kątem obszaru, który można wypełnić.

Dane kontrolera

Kontroler jest tutaj . Jest bardzo podobny do oryginalnej gry, ale wprowadzono niewielkie zmiany, aby lepiej pasowało do KotH i ze względów technicznych. Jest on zbudowany z @NathanMerrill „s KotHComm biblioteki , a przy znacznej pomocy ze @NathanMerrill również. Daj mi znać o wszelkich błędach, które znajdziesz w kontrolerze w pokoju rozmów . Aby zachować spójność z KotHComm, korzystałem z kolekcji Eclipse w całym kontrolerze, ale boty można pisać tylko przy użyciu biblioteki kolekcji Java.

Wszystko jest zapakowane w uberjar na stronie wydań github . Aby go użyć, pobierz go i dołącz do swojego projektu, abyś mógł go używać do autouzupełniania (instrukcje dla IntelliJ , Eclipse ). Aby przetestować swoje zgłoszenia, uruchom jar z java -jar SplixKoTH-all.jar -d path\to\submissions\folder. Upewnij się, że path\to\submissions\folderma on nazwany subfoler javai umieść tam wszystkie swoje pliki. Nie używaj nazw pakietów w swoich botach (chociaż może to być możliwe w przypadku KotHComm, to tylko trochę więcej problemów). Aby zobaczyć wszystkie opcje, użyj --help. Aby załadować wszystkie boty, użyj --question-id 126815.

Pisanie bota

Aby zacząć pisać bota, musisz go rozszerzyć SplixPlayer.

  • Direction makeMove(ReadOnlyGame game, ReadOnlyBoard board)
    • Tutaj decydujesz, który ruch chcesz wykonać. Nie może zwracać wartości null.
  • HiddenPlayer getThisHidden()
    • Pobierz HiddenPlayerwersję this. Przydatny do porównania bota z tablicą.

enum Direction

  • Wartości
    • East (x = 1; y = 0)
    • West (x = -1; y = 0)
    • North (x = 0; y = 1)
    • South (x = 0; y = -1)
  • Direction leftTurn()
    • Zdobądź to Direction, co dostaniesz, jeśli skręcisz w lewo.
  • Direction RightTurn()
    • Zdobądź to Direction, co dostaniesz, jeśli wykonasz skręt w prawo.

ReadOnlyBoard

W tej klasie masz dostęp do tablicy. Możesz uzyskać widok lokalny (20 x 20) planszy z pokazanymi pozycjami gracza lub widok globalny (cała plansza) z informacją tylko o tym, kto jest właścicielem i zajmuje pozycje na planszy. Tutaj też otrzymujesz swoją pozycję.

  • SquareRegion getBounds()
    • Odzyskaj rozmiar planszy.
  • MutableMap<com.nmerrill.kothcomm.game.maps.Point2D,ReadOnlySplixPoint> getGlobal()
    • Uzyskaj globalną mapę planszy.
  • MutableMap<com.nmerrill.kothcomm.game.maps.Point2D,ReadOnlySplixPoint> getView()
    • Taki sam getGlobal(), z wyjątkiem tego, że jest ograniczony do obszaru 20 x 20 wokół twojego gracza i pokazuje pozycje gracza.
  • Point2D getPosition(SplixPlayer me)
    • Zdobądź pozycję swojego gracza. Użyj jako board.getPosition(this).
  • Point2D getSelfPosition(ReadOnlyBoard)
    • Zdobądź swoją pozycję na tablicy. Stosowanie:Point2D mypos = getSelfPosition(board)

ReadOnlyGame

ReadOnlyGamezapewnia jedynie dostęp do liczby tur pozostałych w grze int getRemainingIterations().

ReadOnlySplixPoint

  • HiddenPlayer getClaimer()
    • Pobierz HiddenPlayerwersję tego, kto żąda punktu - twierdzenie = ślad.
  • HiddenPlayer getOwner()
    • Znajdź, kto jest właścicielem punktu.
  • HiddenPlayer getWhosOnSpot()
    • Jeśli gracz jest ustawiony w tym punkcie, zwróć jego ukrytą wersję. Działa tylko w getLocal().

Point2D

W przeciwieństwie do innych klas Point2Dzawartych w bibliotece 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)
    • Zawiń xwartość, aby była w zakresie maxX.
  • Point2D wrapY(int maxY)
    • Zawiń ywartość, aby była w zakresie maxY.
  • int cartesianDistance(Point2D other)
    • Przekłada się to na to, ile tur zajmie graczowi przejście z punktu a do punktu b.

Wsparcie Clojure

Kompilator Clojure jest dołączony do SplixKoTH-all.jar, więc możesz używać Clojure dla swojego bota! Zobacz mój, random_botaby zobaczyć, jak go używać.

Debugowanie bota

Kontroler jest wyposażony w debuger, który pomaga testować strategie. Aby go uruchomić, uruchom słoik z --guiopcją.

Aby dołączyć debuger do słoika, postępuj zgodnie z tymi instrukcjami dla IntelliJ lub tymi dla Eclipse (wersja Eclipse niesprawdzona).

wprowadź opis zdjęcia tutaj

Jeśli używasz debugera ze swoim kodem, możesz go użyć, aby pomóc w wizualizacji tego, co widzi twój bot. Ustaw punkt przerwania na początku makeMovebota i upewnij się, że wstrzymuje on tylko bieżący wątek. Następnie kliknij przycisk Start w interfejsie użytkownika i przejdź przez kod.

wprowadź opis zdjęcia tutaj

Podsumowując:

Uruchamianie botów

Aby uruchomić boty z innymi, musisz uruchomić jar na stronie wydań. Oto lista flag:

  • --iterations( -i) <= int(domyślnie 500)
    • Określ liczbę gier do uruchomienia.
  • --test-bot( -t) <=String
    • Uruchamiaj tylko gry, w których znajduje się bot.
  • --directory( -d) <= Ścieżka
    • Katalog, z którego uruchamiane są zgłoszenia. Użyj tego, aby uruchomić swoje boty. Upewnij się, że twoje boty znajdują się w podfolderze o nazwie ścieżka java.
  • --question-id( -q) <= int(tylko użyj 126815)
    • Pobierz i skompiluj pozostałe zgłoszenia ze strony.
  • --random-seed( -r) <= int(domyślnie losowa liczba)
    • Daj ziarno biegaczowi, aby boty korzystające z losowości mogły odtworzyć wyniki.
  • --gui( -g)
    • Uruchom interfejs debugera zamiast turnieju. Najlepiej stosować z --test-bot.
  • --multi-thread( -m) <= boolean(domyślnie true)
    • Uruchom tournoment w trybie wielowątkowym. Umożliwia to szybszy wynik, jeśli komputer ma wiele rdzeni.
  • --thread-count( -c) <= int(domyślnie 4)
    • Liczba wątków do uruchomienia, jeśli dozwolonych jest wiele wątków.
  • --help( -h)
    • Wydrukuj komunikat pomocy podobny do tego.

Aby uruchomić wszystkie zgłoszenia na tej stronie, użyj java -jar SplixKoTH-all.jar -q 126815.

Formatowanie postu

Aby mieć pewność, że kontroler może pobrać wszystkie boty, należy postępować zgodnie z tym formatem.

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

Nie używaj również deklaracji pakietu.


Tablica wyników

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

Daj mi znać, jeśli jakakolwiek część zasad jest niejasna lub jeśli znajdziesz jakieś błędy w kontrolerze w pokoju rozmów .

Baw się dobrze!

J Atkin
źródło
Hej, to w końcu zostało opublikowane! Zastanawiałem się: D
MD XF
Jak długo czekasz? ;) Czy planujesz złożyć?
J Atkin,
Nie wiem, czy będę w stanie rozwiązać takie wyzwanie, ponieważ piszę programy głównie w esolangach. Ale widziałem to w piaskownicy i wyglądało to na wielkie wyzwanie!
MD XF,
@hyperneutrino Widziałem edycję, czy to naprawdę przeszkadza? Polityczna poprawność nigdzie nie wchodzi w zakres tego wpisu i jest to całkowicie poprawna gramatyka angielska ...
J Atkin
2
0. mały świat? Znam twórcę splix.io. (Tweetował to @ go)
CAD97

Odpowiedzi:

2

ImNotACoward, Java

Ten bot jest tchórzem ekspert przeżycia. Jeśli w pobliżu nie ma wroga, przejmuje część ziemi. Jeśli można bezpiecznie dotrzeć do pętli innego gracza, dźga on drugiego gracza w plecy, angażując go w pojedynek. Jeśli drugiego gracza nie można bezpiecznie zaatakować, ucieka, wykonuje strategiczne odwrót do swojej ziemi.

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
źródło
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];
    }
}

To chyba najprostszy bot. Wystarczy prześledzić krawędź planszy, podwajając się, aby zmniejszyć ryzyko śmierci.

J Atkin
źródło
Fajnie, że użyłeś kolekcji Eclipse. W EC znajduje się interfejs Parowania. Możesz użyć Tuples.pair (), aby uzyskać instancję Pair. Istnieje również klasa PrimitiveTuples, jeśli jedna lub obie wartości w parze są prymitywami.
Donald Raab,
1

random_bot, Clojure

To jest RandomBot , ale musiałem trzymać się konwencji nazewnictwa, a niektóre problemy uniemożliwiają mi użycie łącznika w nazwie, więc podkreślam, że królują! make-moveFn zwraca vec z pierwszym elementem bycia Directionchcesz poruszać się, a drugi jest stan chcesz być przekazywane z powrotem do Ciebie na następnej tury. Nie używaj żadnych zewnętrznych atomów, ponieważ ten kod może uruchamiać wiele gier równolegle.

 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
źródło
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;}

}

Jeden z najbardziej podstawowych botów. Przeszukuje planszę w poszukiwaniu miejsc do zabicia innych i podąża najkrótszą możliwą ścieżką, aby dostać się do pozycji zabijania. Jeśli znajduje się poza swoim terytorium, wykona losowe ruchy, dopóki nie otworzy się ponownie, aby zabić innego gracza. Ma pewną logikę, by nie dopuścić do tego, by się nad nim przebiegł, a gdy wszyscy inni gracze nie żyją, wraca do swojego domu. Po powrocie do domu po prostu idzie na mały kwadrat.

J Atkin
źródło