Grid-Routing Battle

22

UWAGA: To wyzwanie jest obecnie martwe, ponieważ nie mogę zainstalować języków potrzebnych do uruchomienia meczu. Jeśli ktoś ma czas i zainteresowanie, aby to zrobić, nie mam nic przeciwko.

Tabela wyników znajduje się na dole ogłoszenia.

Jest to pół-kooperacyjne wyzwanie króla wzgórza, w którym boty konstruują ścieżki poprzez dwuwymiarowy wykres siatki. Wygrywa bot, który kontroluje węzły o największym ruchu. Jednak zbudowanie ścieżki łączącej wymaga więcej niż jednego bota, więc boty będą musiały współpracować - do pewnego stopnia.

Rozgrywka

Poniżej niech N > 0będzie liczba botów w grze.

Siatka

Gra rozgrywana jest na dwuwymiarowej siatce liczb całkowitych wielkości , której współrzędna u dołu po lewej znajduje się . Każdej współrzędnej z ma wychodzące krawędzie do trzech współrzędnych , i nad nim, przy czym -coordinates pochodzą modulo . Oznacza to, że siatka owija się wokół krawędzi wschodniej i zachodniej. Każda dolna współrzędna jest źródłem , a każda górna współrzędna jest zlewem .⌊4/3N2⌋ × ⌊4/3N2(0,0)(x,y)0 ≤ y < ⌊4/3N2⌋-1(x-1,y+1)(x,y+1)(x+1,y+1)x⌊4/3N2(x,0)(x,⌊4/3N2⌋-1)

Poniższy obrazek pokazuje 8 × 8siatkę.

Siatka 8x8.

Każdy wierzchołek wykresu jest nieaktywny , aktywny lub uszkodzony . Wszystkie wierzchołki zaczynają być nieaktywne i mogą być aktywowane przez boty, które będą ich właścicielami. Ponadto boty mogą łamać wierzchołki i nie można ich naprawić.

Kolejność

Tura składa się z fazy zniszczenia i fazy aktywacji . W fazie niszczenia każdy bot może złamać jeden nieaktywny wierzchołek. Ten wierzchołek jest odtąd łamany i nikt nie może go aktywować. W fazie aktywacji każdy bot może aktywować jeden nieaktywny wierzchołek. Od tego czasu są właścicielami tego wierzchołka i nikt inny nie może go reaktywować. Kilka botów może posiadać jeden wierzchołek, jeśli wszystkie aktywują go w tej samej turze. W każdej fazie wyboru wierzchołków dokonuje się jednocześnie.

Punktacja

Jedna runda wystarcza na dokładnie zakręty. Następnie rundę ocenia się w następujący sposób. Z każdego aktywnego wierzchołka źródłowego przeprowadzamy losowe wyszukiwanie w pierwszej kolejności głębokości wzdłuż aktywnych wierzchołków (co oznacza, że ​​dzieci każdego wierzchołka są odwiedzane w losowej kolejności). Jeśli ścieżka zostanie znaleziona od źródła do jakiegoś ujścia, wówczas dla wszystkich wierzchołków wzdłuż tej ścieżki każdy właściciel wierzchołka otrzymuje jeden punkt.N2N

Cała gra trwa 100 rund, a zwycięzcą jest bot z największą liczbą punktów. Mogę zwiększyć tę liczbę, jeśli wariancja wyników jest zbyt wysoka.

Dodatkowe zasady

  • Bez bałaganu ze sterownikiem lub innymi zgłoszeniami.
  • Maksymalnie jedno zgłoszenie na uczestnika.
  • Żadne zasoby zewnętrzne, z wyjątkiem jednego prywatnego pliku tekstowego, zostały wyczyszczone na początku gry.
  • Nie projektuj swojego bota do pokonania lub wspierania określonych przeciwników.
  • Podaj polecenia, aby skompilować i uruchomić bota. Każdy kompilator / interpreter, który jest swobodnie dostępny dla systemu Debian Linux, jest dopuszczalny.

Kontroler

Kontroler jest napisany w Pythonie 3 i można go znaleźć w GitHub . Szczegółowe instrukcje znajdują się w pliku README. Oto interfejs API na początek:

  • Boty są uruchamiane na początku każdej rundy i trwają do końca rundy. Komunikuj się ze sterownikiem przez STDIN i STDOUT, używając komunikatów zakończonych znakiem nowej linii.
  • BEGIN [num-of-bots] [num-of-turns] [side-length] jest wprowadzany na początku.
  • DESTROY [turn]jest wprowadzany na początku każdej fazy niszczenia. Twój bot odpowie albo VERTEX x,ywybierając wierzchołek, albo NONE.
  • BROKEN [turn] [your-choice] [other-choices]jest wprowadzany na końcu każdej fazy niszczenia. Kolejność innych botów jest losowa na początku każdej gry, ale pozostaje ustalona podczas jej trwania. Wybory są przedstawione jako x,ylub N.
  • ACTIVATE [turn]i OWNED [turn] [your-choice] [other-choices]są odpowiednikami powyższego dla fazy aktywacji i mają tę samą semantykę.
  • SCORE [your-score] [other-scores] jest wprowadzany na końcu gry.
  • Twój bot ma 1 sekundę na przeanalizowanie wyników fazy i wybranie następnego wierzchołka oraz 1 sekundę na zakończenie po otrzymaniu wyniku. Przetestuję zgłoszenia na moim stosunkowo starym laptopie, więc lepiej zostawić tutaj margines.

Pamiętaj, aby opróżnić bufor wyjściowy. W przeciwnym razie kontroler może zawiesić się w niektórych środowiskach.

Tabela liderów

Zaktualizowano 3/13/2015

Peacemaker jest uruchomiony i Funnelweb również otrzymał aktualizację. Wyniki podskoczyły o rząd wielkości. Złącze przekroczyło limit czasu w dwóch grach.

Funnelweb: 30911
Connector: 18431
Watermelon: 3488
Annoyance: 1552
Explorer: 735
Checkpoint: 720
Random Builder: 535
FaucetBot: 236
Peacemaker: 80

Pełny dziennik z grafiką artystyczną ASCII można znaleźć w repozytorium kontrolera, w graphical_log.txt.

Niektóre spostrzeżenia:

  • Złącze można bardzo łatwo zatrzymać, przerywając pojedynczy wierzchołek przed nim. Podejrzewam, że często to robi. Jednak obecnie nie ma to większego sensu, ponieważ tylko Łącznik może sobie wyobrazić ścieżkę.
  • Arbuz może uzyskać przyzwoity wynik, po prostu będąc na ścieżce łączącej (ponieważ DFS najprawdopodobniej użyje swoich wierzchołków).
  • Explorer lubi uprawiać winorośl z arbuzów.
  • Zaktualizowana Funnelweb otrzymuje naprawdę dobre wyniki, ponieważ Connector zwykle zaczepia się na nią w dolnej połowie siatki.
  • Gry stają się dość długie, średnia runda trwa na moim komputerze około 25 sekund.
Zgarb
źródło
1
@Alex Próbowałem zaprojektować wyzwanie, aby samobójcze boty nie popsuły wszystkiego. Trzy dobrze zaprojektowane boty powinny zawsze być w stanie zbudować prawidłową ścieżkę, jeśli współpracują ze sobą.
Zgarb
2
@Zgarb Samobójstwo nie powinno tego zepsuć, ale kilka współpracujących botów troll może prawdopodobnie również zablokować wszystkie ścieżki, rujnując grę.
Geobits
2
@CarpetPython Aktywnych węzłów nie można zniszczyć.
Zgarb
1
Wygląda na to, że nie zobaczymy żadnych interesujących gier z obecnymi graczami i zasadami. Sugeruję nieznaczną zmianę zasad, aby stworzyć możliwości ciekawych gier. Zmiana rozmiaru siatki na 1,5 * N ^ 2 zamiast 2 * N ^ 2 powinna być dobra i nie powinna zbytnio mylić istniejących robotów.
Logic Knight
1
@justhalf To prawda. Gry w dzienniku były rozgrywane przy dalszym zmniejszeniu rozmiaru siatki 4/3*N^2, a nawet tam boty miały problemy z tworzeniem prawidłowych ścieżek. Jednak Connector został tymczasowo zdyskwalifikowany z powodu błędu, a teraz, gdy został naprawiony, spodziewam się, że gry będą bardziej interesujące. Dziś wieczorem wypuszczę kolejną partię.
Zgarb,

Odpowiedzi:

7

Złącze (Java)

Próbuje utworzyć ścieżkę w losowej pozycji. Ponieważ nie może samodzielnie utworzyć ścieżki, szuka aktywnych komórek i używa ich. Podziękowania należą się Geobitom, od których ukradłem trochę kodu. To przesłanie jeszcze się nie zakończyło, ponieważ po prostu przestaje robić wszystko, jak tylko ścieżka zostanie zbudowana.

Edycja: Jeśli ścieżka jest zbudowana, Łącznik próbuje utworzyć wiele ścieżek wzdłuż istniejącej.

import java.awt.Point;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class Connector {
    private static final int INACTIVE = 0;
    private static final int ACTIVE   = 1;
    private static final int BROKEN   = 2;
    private static final int MINE     = 3;

    private int size = 0;
    private int[][] grid = new int[size][size];
    private Point previousCell = null;
    private final List<Point> path = new ArrayList<>();

    public static void main(String[] args) {
        new Connector().start();
    }

    private void start() {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        while(true) {
            try {
                String input = reader.readLine();
                act(input);
            } catch (Exception e) {
                e.printStackTrace();
                System.exit(0);
            }
        }
    }

    private void act(String input) throws Exception {
        String[] msg = input.split(" ");
        String output = "";
        int turn;
        switch(msg[0]){
        case "BEGIN":
            size = Integer.parseInt(msg[3]);
            grid = new int[size][size];
            break;
        case "DESTROY":
            output = "NONE";
            break;
        case "BROKEN":
            update(msg, true);
            break;
        case "ACTIVATE":
            turn = Integer.parseInt(msg[1]);
            output = activate(turn);
            break;
        case "OWNED":
            update(msg, false);
            break;
        case "SCORE":
            System.exit(0);
            break;
        }
        if (output.length() > 0) {
            System.out.println(output);
        }
    }

    private String activate(int turn) {
        if (turn == 0) {
            Random r = new Random();
            previousCell = new Point(r.nextInt(size), 0);
            return "VERTEX " + previousCell.x + "," + 0;
        }
        Point lastCell = findLastPathCell(previousCell.x, previousCell.y);
        if (lastCell.y == size-1) {
            //path is done
            Point extendingPathPoint = findExtendingPathPoint();
            if (extendingPathPoint == null) {
                return "NONE";
            }
            return "VERTEX " + extendingPathPoint.x + "," + extendingPathPoint.y;
        } else {
            int x = findBestX(lastCell.x, lastCell.y);
            return "VERTEX " + x + "," + (lastCell.y + 1);
        }
    }

    private int findBestX(int x, int y) {
        int bestScore = Integer.MIN_VALUE;
        int bestX = 0;
        for (int i = -1; i <= 1; i++) {
            int newY = y + 1;
            int newX = (x + i + size) % size;
            int score = calcCellScore(newX, newY, 10);
            if (score > bestScore) {
                bestScore = score;
                bestX = newX;
            } else if (score == bestScore && Math.random() < 0.3) {
                bestX = newX;
            }
        }
        return bestX;
    }

    private int calcCellScore(int x, int y, int depth) {
        int newY = y + 1;
        if (depth < 0) {
            return 1;
        }
        if (newY >= size)
            return 100;
        int cellScore = 0;
        for (int i = -1; i <= 1; i++) {
            int newX = (x + i + size) % size;
            if (grid[newX][newY] == ACTIVE || grid[newX][newY] == MINE) {
                cellScore += 5;
            } else if (grid[newX][newY] == INACTIVE) {
                cellScore += 1;             
            } else {
                cellScore -= 2;
            }
            cellScore += calcCellScore(newX, newY, depth -1);
        }
        return cellScore;
    }

    private Point findLastPathCell(int x, int y) {
        Point thisCell = new Point(x,y);
        int newY = y + 1;
        if (newY >= size) {
            return thisCell;
        }
        List<Point> endCells = new ArrayList<>();
        endCells.add(thisCell);
        path.add(thisCell);
        for (int i = -1; i <= 1; i++) {
            int newX = (x + i + size) % size;
            if (grid[newX][newY] == ACTIVE || grid[newX][newY] == MINE) {
                endCells.add(findLastPathCell(newX, newY));
            }
        }
        int bestY = -1;
        Point bestPoint = null;
        for (Point p : endCells) {
            if (p.y > bestY) {
                bestY = p.y;
                bestPoint = p;
            }
        }
        return bestPoint;
    }

    private Point findExtendingPathPoint() {
        if (path.size() == 0)
            return null;
        Random rand = new Random();
        for (int i = 0; i < size; i++) {
            Point cell = path.get(rand.nextInt(path.size()));
            for (int j = -1; j <= 1; j += 2) {
                Point newCellX = new Point((cell.x + j + size) % size, cell.y);
                if (grid[newCellX.x][newCellX.y] == INACTIVE)
                    return newCellX;

                Point newCellY = new Point(cell.x, cell.y + j);
                if (cell.y < 0 || cell.y >= size)
                    continue;
                if (grid[newCellY.x][newCellY.y] == INACTIVE)
                    return newCellY;
            }
        }
        return null;
    }

    private void update(String[] args, boolean destroyPhase) {
        for(int i = 2; i < args.length; i++) {
            String[] tokens = args[i].split(",");
            if(tokens.length > 1){
                int x = Integer.parseInt(tokens[0]);
                int y = Integer.parseInt(tokens[1]);
                if (grid[x][y] == INACTIVE) {
                    if (destroyPhase) {
                        grid[x][y] = BROKEN;
                    } else if (i == 2) {
                        grid[x][y] = MINE;
                        path.add(new Point(x,y));
                        previousCell = new Point(x,y);
                    } else {
                        grid[x][y] = ACTIVE;
                    }
                }
            }
        }
    }
}
CommonGuy
źródło
@Zgarb Przepraszamy, stworzyłem błąd podczas naprawy drugiego. Teraz działa
CommonGuy
@Manu, dobrze, że wróciłeś do gry. Jest zbyt wielu wyzyskiwaczy i za mało konstruktorów. Przy włączonym łączniku gry mogą stać się bardziej interesujące (więcej niż 1 gra na 100 z wynikiem).
Logic Knight
Łącznik odpowiedział 28 sekund na odpowiedź w jednej z najnowszych gier (patrz dziennik). Wygląda na to, że wpadł na Arbuza i miał trudności z podjęciem decyzji, gdzie iść dalej.
Zgarb,
Pobiegłem kilka gier dzięki ulepszonej Peacemaker i złącza wyrzucił błąd: java.lang.ArrayIndexOutOfBoundsException: -1 at Connector.findExtendingPathPoint(Connector.java:166).
Zgarb,
7

Funnelweb, Python 2

wersja 1.2 - Lepszy kod dołączania, dodano nową animację

Nazwany na cześć jednego z mniej przyjaznych pająków w Australii. Ten bot najpierw buduje gniazdo w kształcie lejka w górnych rzędach, a następnie wabi inne boty do budowania ścieżek dla ruchu do gniazda.

Oto nowa animacja gry 6 botów na planszy 4 / 3N ^ 2 przedstawiająca sieć funnelweb i kilka prostszych botów:

bots6.gif

Kod Pythona funnelweb:

from random import *
import sys
ME = 0
def pt(x,y): return '%u,%u' % (x % side_len, y)

while True:
    msg = raw_input().split()

    if msg[0] == 'BEGIN':
        turn = 0
        numbots, turns, side_len = map(int, msg[1:])
        R = range(side_len)
        top = side_len - 1
        grid = dict((pt(x, y), []) for x in R for y in R)
        mynodes = set()
        deadnodes = set()
        freenodes = set(grid.keys())
        mycol = choice(R)
        extra = sample([pt(x,top) for x in R], side_len)
        path = [(mycol, y) for y in range(top, top - side_len/6, -1)]
        moves = []
        fence = []
        for x,y in path:
            moves.append( [pt(x,y), pt(x+1,y), pt(x-1,y)] )
            fence.extend( [pt(x+1,y), pt(x-1,y)] )
        for dx in range(2, side_len):
            fence.extend( [pt(x+dx,y), pt(x-dx,y)] )
        for x,y in [(mycol, y) for y in 
                range(top - side_len/6, top - 3*side_len/4, -1)]:
            moves.append( [pt(x,y), pt(x+1,y), pt(x-1,y)] )

    elif msg[0] == 'DESTROY':
        target = 'NONE'
        while fence:
            loc = fence.pop(0)
            if loc in freenodes:
                target = 'VERTEX ' + loc
                break
        print target
        sys.stdout.flush()

    elif msg[0] == 'BROKEN':
        for rid, loc in enumerate(msg[2:]):
            if loc != 'N':
                grid[loc] = None
                deadnodes.add(loc)
                freenodes.discard(loc)
                if loc in extra: extra.remove(loc)

    elif msg[0] == 'ACTIVATE':
        target = 'NONE'
        while moves:
            loclist = moves.pop(0)
            goodlocs = [loc for loc in loclist if loc in freenodes]
            if goodlocs:
                target = 'VERTEX ' + goodlocs[0]
                break
        if target == 'NONE':
            if extra:
                target = 'VERTEX ' + extra.pop(0)
            else:
                target = 'VERTEX ' + pt(choice(R), choice(R))
        print target
        sys.stdout.flush()

    elif msg[0] == 'OWNED':
        for rid, loc in enumerate(msg[2:]):
            if loc != 'N':
                grid[loc].append(rid)
                if rid == ME:
                    mynodes.add(loc)
                freenodes.discard(loc)
                if loc in extra: extra.remove(loc)
        turn += 1

    elif msg[0] == 'SCORE':
        break

Pająk biegnie z python funnelweb.py.

Logic Knight
źródło
Zmieniono algorytm i przetestowałem go. Powinien już działać.
Logic Knight
Działa świetnie teraz!
Zgarb
6

Checkpoint, Java

Ten bot próbuje utworzyć punkty kontrolne, aby każda ważna ścieżka przechodziła przez jeden z moich wierzchołków. Ponieważ są N 2 zwoje, a plansza ma 2N 2 w poprzek, mogę aktywować / łamać każdy węzeł na jednej poziomej linii (zakładając, że jestem tam pierwszy). Zrób to na przemian ( xjest zepsuty, ojest mój):

xoxoxoxoxoxox...

Jeśli chcesz stworzyć ścieżkę, musisz przejść przez moje punkty kontrolne :)

Teraz może napotkać kilka problemów. Po pierwsze, nie będzie dobrze, jeśli nie będzie wielu ścieżek. Ponieważ sam nie tworzy żadnych produktywnych ścieżek, naprawdę jest całkowicie zależny od konkurencji. Nawet kilku konkurentów łączących się, by stworzyć jedną ścieżkę, niewiele pomoże, ponieważ zdobywa tylko jedno miejsce za każdą znalezioną ścieżkę. To, czego potrzebuje, by zabłysnąć, to prawdopodobnie sporo botów tworzących całkiem różne ścieżki. Nawet wtedy może nie być bardzo wysoko, ale to był pomysł, który miałem na czacie, więc ...

Jeśli jedno z miejsc na linii jest już zablokowane / zajęte, po prostu szukam najbliższego miejsca, z którego mogę skorzystać (najlepiej na tej samej xlinii, tylko przesunięty w pionie).


import java.io.BufferedReader;
import java.io.InputStreamReader;

public class Checkpoint {
    public static void main(String[] args) {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        while(true)
            try {
                String input = reader.readLine();
                act(input);
            } catch (Exception e) {
                e.printStackTrace();
                System.exit(0);
            }
    }

    static void act(String input) throws Exception{
        String[] msg = input.split(" ");
        String output = "";
        int turn;
        boolean found = false;
        switch(msg[0]){
        case "BEGIN":
            size = Integer.parseInt(msg[3]);
            grid = new int[size][size];
            target = size/2;
            break;
        case "DESTROY":
            turn = Integer.parseInt(msg[1]);
            for(int x=0;x<size;x+=2)
                for(int y=0;y<size&&!found;y++)
                    if(grid[(x+turn*2)%size][(y+target)%size]==INACTIVE){
                        output = "VERTEX " + ((x+turn*2)%size) + "," + ((y+target)%size);
                        found = true;
                    }
            if(output.length() < 1)
                output = "NONE";
            break;
        case "BROKEN":
            for(int i=2;i<msg.length;i++){
                String[] tokens = msg[i].split(",");
                if(tokens.length>1){
                    int x = Integer.parseInt(tokens[0]);
                    int y = Integer.parseInt(tokens[1]);                    
                    if(grid[x][y]==INACTIVE)
                        grid[x][y] = BROKEN;
                }
            }
            break;
        case "ACTIVATE":
            turn = Integer.parseInt(msg[1]);
            for(int x=1;x<size;x+=2)
                for(int y=0;y<size&&!found;y++)
                    if(grid[(x+turn*2)%size][(y+target)%size]==INACTIVE){
                        output = "VERTEX " + ((x+turn*2)%size) + "," + ((y+target)%size);
                        found = true;
                    }
            if(output.length() < 1)
                output = "NONE";
            break;
        case "OWNED":
            for(int i=2;i<msg.length;i++){
                String[] tokens = msg[i].split(",");
                if(tokens.length>1){
                    int x = Integer.parseInt(tokens[0]);
                    int y = Integer.parseInt(tokens[1]);
                    if(i==2){
                        if(grid[x][y]==INACTIVE)
                            grid[x][y] = MINE;
                    }else{
                        if(grid[x][y]==INACTIVE)
                            grid[x][y]=ACTIVE;
                    }
                }
            }
            break;
        case "SCORE":
            System.exit(0);
            break;
        }
        if(output.length()>0)
            System.out.println(output);
    }

    static int size = 2;
    static int target = size/2;
    static int[][] grid = new int[size][size];

    static final int INACTIVE = 0;
    static final int ACTIVE   = 1;
    static final int BROKEN   = 2;
    static final int MINE     = 3;
}

Aby skompilować, to jest javac Checkpoint.java. Aby uruchomić, java Checkpoint. Będziesz chciał dodać / zmienić ścieżkę, aby odzwierciedlała gdziekolwiek się znajduje.

Geobity
źródło
5

Arbuz, Java

Próby narysowania arbuzów na siatce.

import java.awt.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

public class Watermelon {

    private static int numberOfBots;
    private static int numberOfTurns;
    private static int sideLength;

    private static int turn = 0;

    private static int[][] theGrid;

    private static final int INACTIVE = -2;
    private static final int BROKEN   = -1;
    private static final int MINE     =  0;
    private static final int ACTIVE   =  1;

    private static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    private static PrintStream out = System.out;

    public static void main(String[] args) throws IOException {
        while (true){
            String[] input = in.readLine().trim().split(" ");
            String instruction = input[0];
            switch (instruction){
                case "BEGIN":
                    begin(input);
                    break;
                case "DESTROY":
                    destroy(input);
                    break;
                case "BROKEN":
                    broken(input);
                    break;
                case "ACTIVATE":
                    activate(input);
                    break;
                case "OWNED":
                    owned(input);
                    break;
                default:
                    return;
            }
            out.flush();
        }
    }

    private static void begin(String[] input) {
        numberOfBots = Integer.parseInt(input[1]);
        numberOfTurns = Integer.parseInt(input[2]);
        sideLength = Integer.parseInt(input[3]);
        theGrid = new int[sideLength][sideLength];
        for (int x = 0; x < sideLength; x++){
            for (int y = 0; y < sideLength; y++){
                theGrid[x][y] = INACTIVE;
            }
        }
    }

    private static void owned(String[] input) {
        turn = Integer.parseInt(input[1]);
        for (int i = input.length - 1; i >= 2; i--){
            if (input[i].equals("N")){
                continue;
            }
            String[] coordinates = input[i].split(",");
            int x = Integer.parseInt(coordinates[0]);
            int y = Integer.parseInt(coordinates[1]);
            int player = i - 2;
            if (player == 0){
                theGrid[x][y] = MINE;
            } else {
                theGrid[x][y] = ACTIVE;
            }
        }
    }

    private static void activate(String[] input) {
        turn = Integer.parseInt(input[1]);
        double[][] values = new double[sideLength][sideLength];
        List<Point> pointList = new ArrayList<>();
        for (int x = 0; x < sideLength; x++){
            for (int y = 0; y < sideLength; y++){
                if (theGrid[x][y] == MINE || theGrid[x][y] == ACTIVE){
                    for (int x1 = 0; x1 < sideLength; x1++){
                        for (int y1 = 0; y1 < sideLength; y1++){
                            double distance = Math.pow(x - x1, 2) + Math.pow(y - y1, 2);
                            values[x1][y1] += 1 / (distance + 1);
                        }
                    }
                }
                pointList.add(new Point(x, y));
            }
        }
        pointList.sort(Comparator.comparingDouble((Point a) -> values[a.x][a.y]).reversed());
        for (Point point : pointList){
            if (theGrid[point.x][point.y] == INACTIVE){
                out.println("VERTEX " + point.x + "," + point.y);
                return;
            }
        }
        out.println("NONE");
    }

    private static void broken(String[] input) {
        turn = Integer.parseInt(input[1]);
        for (int i = 2; i < input.length; i++){
            if (input[i].equals("N")){
                continue;
            }
            String[] coordinates = input[i].split(",");
            int x = Integer.parseInt(coordinates[0]);
            int y = Integer.parseInt(coordinates[1]);
            theGrid[x][y] = BROKEN;
        }
    }

    private static void destroy(String[] input) {
        turn = Integer.parseInt(input[1]);
        double[][] values = new double[sideLength][sideLength];
        List<Point> pointList = new ArrayList<>();
        for (int x = 0; x < sideLength; x++){
            for (int y = 0; y < sideLength; y++){
                if (theGrid[x][y] == MINE){
                    for (int x1 = 0; x1 < sideLength; x1++){
                        for (int y1 = 0; y1 < sideLength; y1++){
                            double distance = Math.pow(x - x1, 2) + Math.pow(y - y1, 2);
                            values[x1][y1] -= 1 / (distance + 1);
                        }
                    }
                }
                if (theGrid[x][y] == ACTIVE){
                    for (int x1 = 0; x1 < sideLength; x1++){
                        for (int y1 = 0; y1 < sideLength; y1++){
                            double distance = Math.pow(x - x1, 2) + Math.pow(y - y1, 2);
                            values[x1][y1] += 1 / (distance + 1) / (numberOfBots - 1);
                        }
                    }
                }
                pointList.add(new Point(x, y));
            }
        }
        pointList.sort(Comparator.comparingDouble((Point a) -> values[a.x][a.y]).reversed());
        for (Point point : pointList){
            if (theGrid[point.x][point.y] == INACTIVE){
                out.println("VERTEX " + point.x + "," + point.y);
                return;
            }
        }
        out.println("NONE");
    }
}
Numer jeden
źródło
5

FaucetBot (in R)

Tworzy wąskie gardło w drugiej linii i aktywuje węzły na ścieżce za nim.

infile <- file("stdin")
open(infile)
repeat{
    input <- readLines(infile,1)
    args <- strsplit(input," ")[[1]]
    if(args[1]=="BEGIN"){
        L <- as.integer(args[4])
        M <- N <- matrix(0,nrow=L,ncol=L)
        x0 <- sample(2:(L-1),1)
        }
    if(args[1]=="DESTROY"){
        if(args[2]==0){
            X <- x0
            Y <- 2
            }else{
                free <- which(M[,2] == 0)
                mine <- which(N[,2] == 1)
                X <- free[which.min(abs(free-mine))]
                Y <- 2
                }
        if(length(X)){cat(sprintf("VERTEX %s,%s\n",X-1,Y-1))}else{cat("NONE\n")}
        flush(stdout())
        }
    if(args[1]=="BROKEN"){
        b <- strsplit(args[args!="N"][-(1:2)],",")
        o <- strsplit(args[3],",")[[1]]
        b <- lapply(b,as.integer)
        if(o[1]!="N") N[as.integer(o[1])+1,as.integer(o[2])+1] <- -1
        for(i in seq_along(b)){M[b[[i]][1]+1,b[[i]][2]+1] <- -1}
        }
    if(args[1]=="ACTIVATE"){
        if(args[2]==0){
            broken <- which(M[,2] == -1)
            free <- which(M[,2] == 0)
            X <- free[which.min(abs(broken-free))]
            Y <- 2
            }else{
                y <- 3
                X <- NULL
                while(length(X)<1){
                    lastrow <- which(N[,y-1]==1)
                    newrow <- unlist(sapply(lastrow,function(x)which(M[,y]==0 & abs((1:L)-x)<2)))
                    if(length(newrow)){
                        X <- sample(newrow,1)
                        Y <- y
                        }
                    y <- y+1
                    if(y>L){X <- x0; Y <- 1}
                    }
                }
        cat(sprintf("VERTEX %s,%s\n",X-1,Y-1))
        flush(stdout())
        }
    if(args[1]=="OWNED"){
        b <- strsplit(args[args!="N"][-(1:2)],",")
        o <- strsplit(args[3],",")[[1]]
        b <- lapply(b,as.integer)
        if(o[1]!="N") N[as.integer(o[1])+1,as.integer(o[2])+1] <- 1
        for(i in seq_along(b)){M[b[[i]][1]+1,b[[i]][2]+1] <- 1}
        }
    if(args[1]=="SCORE") q(save="no")
    }

Gdybym nie spieprzył, końcowa konfiguracja powinna wyglądać następująco:

........    .a..aa..
..aaa...    ..aaa...
.xxaxx..    xxxaxxx.    etc.
........    ........

Dowództwo jest Rscript FaucetBot.R.

plannapus
źródło
5

Peacemaker, Java

Na podstawie kodu Manu.

Strefy konfliktu w wyszukiwaniu pokoju (tj. Większość ZŁAMANEGO lub AKTYWNEGO skupienia wierzchołków) i aktywuje losowy wierzchołek w pobliżu.

import java.awt.Point;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.stream.IntStream;

public class Peacemaker {
    private static final int INACTIVE = 0;
    private static final int ACTIVE   = 1;
    private static final int BROKEN   = 2;
    private static final int MINE     = 3;

    private int size = 0;
    private int[][] grid = new int[size][size];
    private int startingPoint = 0;

    public static void main(String[] args) {
        new Peacemaker().start();
    }

    private void start() {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        while(true) {
            try {
                String input = reader.readLine();
                act(input);
            } catch (Exception e) {
                e.printStackTrace();
                System.exit(0);
            }
        }
    }

    private void act(String input) throws Exception {
        String[] msg = input.split(" ");
        String output = "";
        int turn;
        switch(msg[0]){
        case "BEGIN":
            size = Integer.parseInt(msg[3]);
            grid = new int[size][size];
            break;
        case "DESTROY":
            output = "NONE";
            break;
        case "BROKEN":
            update(msg, true);
            break;
        case "ACTIVATE":
            turn = Integer.parseInt(msg[1]);
            output = activate(turn);
            break;
        case "OWNED":
            update(msg, false);
            break;
        case "SCORE":
            System.exit(0);
            break;
        }
        if (output.length() > 0) {
            System.out.println(output);
        }
    }

    private String activate(int turn) {
        Random r = new Random();
        if (turn == 0) {
            startingPoint = r.nextInt(size);
            return "VERTEX " + startingPoint + "," + 0;
        } else {

            Point point = searchConflicts();

            int posX = point.x;
            int posY = point.y;

            while (grid[posX][posY] != INACTIVE) {
                 int previousX = (posX - 1 < 0 ? size - 1 : posX - 1);
                 int nextX = (posX + 1 > size - 1 ? 0 : posX + 1);
                 int previousY = (posY - 1 < 0 ? size - 1 : posY - 1);
                 int nextY = (posY + 1 > size - 1 ? 0 : posY + 1);

                 int choice = r.nextInt(4);
                 switch (choice) {
                     case 0: posX = previousX; break;
                     case 1: posX = nextX; break;
                     case 2: posY = previousY; break;
                     case 3: posY = nextY; break;
                 }
            }

            return "VERTEX " + posX + "," + posY;
        }
    }

    private Point searchConflicts() {

        int previousCellScore = 0;
        int cellX = 0;
        int cellY = 0;
        for (int i = 0; i < size; i++) {
            for (int j = 0; j < size; j ++) {
                if (previousCellScore < adjacentCellsScore(i, j)) {
                    cellX = i; cellY = j;
                    previousCellScore = adjacentCellsScore(i, j);
                }
            }
        }
        return new Point(cellX, cellY);
    }

    /*  Format of adjacent cells :
     * 
     *   0 1 2
     *   3 . 4
     *   5 6 7
     */
    private int adjacentCellsScore(int x, int y) {

        int[] scores = new int[8];

        int previousX = (x - 1 < 0 ? size - 1 : x - 1);
        int nextX = (x + 1 > size - 1 ? 0 : x + 1);
        int previousY = (y - 1 < 0 ? size - 1 : y - 1);
        int nextY = (y + 1 > size - 1 ? 0 : y + 1);

        scores[0] = calcScore(previousX, nextY);
        scores[1] = calcScore(x, nextY);
        scores[2] = calcScore(nextX, nextY);
        scores[3] = calcScore(previousX, y);
        scores[4] = calcScore(nextX, y);
        scores[5] = calcScore(previousX, previousY);
        scores[6] = calcScore(x, previousY);
        scores[7] = calcScore(nextX, previousY);

        return IntStream.of(scores).reduce(0, (a, b) -> a + b);
    }

    private int calcScore(int x, int y) {
        int activeScore = 2;
        int mineScore = 1;
        int inactiveScore = 0;
        int brokenScore = 3;

        if (grid[x][y] == ACTIVE) 
            return activeScore;
        else if (grid[x][y] == MINE)
            return mineScore;
        else if (grid[x][y] == INACTIVE) 
            return inactiveScore;
        else if (grid[x][y] == BROKEN) 
            return brokenScore;
        else
            return 0;
    }


    private void update(String[] args, boolean destroyPhase) {
        for(int i = 2; i < args.length; i++) {
            String[] tokens = args[i].split(",");
            if(tokens.length > 1){
                int x = Integer.parseInt(tokens[0]);
                int y = Integer.parseInt(tokens[1]);
                if (grid[x][y] == INACTIVE) {
                    if (destroyPhase) {
                        grid[x][y] = BROKEN;
                    } else if (i == 2) {
                        grid[x][y] = MINE;
                    } else {
                        grid[x][y] = ACTIVE;
                    }
                }
            }
        }
    }       
}
Thrax
źródło
@Zgarb Dzięki, powinienem był rozwiązać ten problem teraz.
Thrax,
Rozjemca działa teraz i jest uwzględniony w tabeli liderów. Jednak wydaje się, że niewiele to robi, więc prawdopodobnie pozostało jeszcze kilka błędów.
Zgarb
Właściwie, patrząc na twój kod, myślę, że problem tkwi w whilepętli activatemetody. Zatrzymujesz wyszukiwanie, gdy znajdziesz wierzchołek, który nie jest twój i nie jest uszkodzony - ale może być własnością innej osoby, więc nie możesz go aktywować.
Zgarb
@Zgarb Źle odczytałem specyfikacje i pomyślałem, że wielu graczy może aktywować ten sam wierzchołek w dowolnym momencie. Chyba muszę tylko zmienić wyszukiwanie i szukać tylko nieaktywnego wierzchołka.
Thrax,
2

Random Builder, Python 3

To głupi przykładowy bot, który nigdy niczego nie niszczy i próbuje aktywować losowy wierzchołek co turę. Zauważ, że nie trzeba sprawdzać, czy wierzchołek jest nieaktywny; kontroler dba o to.

import random as r

while True:
    msg = input().split()
    if msg[0] == "BEGIN":
        side_len = int(msg[3])
    elif msg[0] == "DESTROY":
        print("NONE")
    elif msg[0] == "ACTIVATE":
        print("VERTEX %d,%d"%(r.randrange(side_len), r.randrange(side_len)), flush=True)
    elif msg[0] == "SCORE":
        break

Uruchom za pomocą polecenia

python3 random_builder.py

Konieczne może być zastąpienie python3, pythonzależnie od instalacji Pythona. Aby to zrobić, po prostu edytuj bots.txtplik. Zaktualizowałem kontroler i nie trzeba już bałaganić ścieżek plików.

Zgarb
źródło
Ponieważ używasz Pythona 3, zamiast sys.stdout.flush()ciebie możesz po prostu zrobić flush=Truejako argument print.
matsjoyce
@matsjoyce Dzięki, nie wiedziałem o tym. Zmienię wersję repozytorium później.
Zgarb
2

Explorer, Python 3

Strategia aktywacji:

Tworzy mapę cieplną na podstawie stanu każdego węzła (aktywny / nieaktywny / uszkodzony) i wybiera węzeł, który miałby największą oczekiwaną wartość mapy cieplnej na osobę, gdyby wybrał ten.

Strategia zniszczenia:

Nigdy niczego nie niszczy, ponieważ niewiele to pomaga botowi.

import sys

class bd:

    def __init__(s, l):

        s.l=l
        s.b=[]
        s.v=[]
        s.m=[]
        s.bm=[]
        s.utd=False #up_to_date
        s.bmc=1

        for i in range(s.l):
            s.b+=[[]]
            s.v+=[[]]
            s.m+=[[]]
            s.bm+=[[]]
            for k in range(s.l):
                s.b[i]+=[0]
                s.v[i]+=[0]
                s.m[i]+=[0]
                s.bm[i]+=[s.bmc]

    def update(s):
        s.utd=True

        vu=[]
        vd=[]
        for i in range(s.l):
            vu+=[[]]
            vd+=[[]]
            for k in range(s.l):
                vu[i]+=[1]
                vd[i]+=[1]

        #spread up
        for i in range(s.l):
            vu[i][0]*=s.bm[i][0]

        for k in range(1,s.l):
            for i in range(s.l):
                sumv=vu[(i-1)%s.l][k-1]+vu[(i)%s.l][k-1]+vu[(i+1)%s.l][k-1]  
                vu[i][k]*=sumv*s.bm[i][k]/3

        #spread down
        t=s.l-1
        for i in range(s.l):
            vd[i][t]*=s.bm[i][t]

        for k in range(s.l-2,-1,-1):
            for i in range(s.l):
                sumv=vd[(i-1)%s.l][k+1]+vd[(i)%s.l][k+1]+vd[(i+1)%s.l][k+1]  
                vd[i][k]*=sumv*s.bm[i][k]/3

        #mult
        for i in range(s.l):
            for k in range(s.l):
                if s.b[i][k]==-1 or s.m[i][k]==1:
                    s.v[i][k]=float(-1)
                else:
                    s.v[i][k]=vu[i][k]*vd[i][k]/(s.b[i][k]+1)

    def add_act(s,al):
        s.utd=False

        for ind, ap in enumerate(al):
            i,k=ap
            s.b[i][k]+=1            
            s.bm[i][k]=2*s.bmc            
            #doesn't work alone WHY???
            if ind==0: s.m[i][k]=1

    def add_ina(s,il):
        s.utd=False

        for ind, ip in enumerate(il):
            i,k=ip
            s.b[i][k]=-1
            s.bm[i][k]=0                    

    def get_newact(s):
        s.update()
        vm=-28
        pm=None
        for i in range(s.l):
            for k in range(s.l):
                if s.v[i][k]>vm:
                    vm=s.v[i][k]
                    pm=(i,k)
        #doesn't work alone WHY???
        s.m[pm[0]][pm[1]]=1
        return pm


b=None

while True:
    inp=input()
    msg = inp.split()
    if msg[0] == "BEGIN":        
        b = bd(int(msg[3]))
    elif msg[0] == "DESTROY":
        print("NONE")
    elif msg[0] == "BROKEN":
        pl=[]
        for m in msg[2:]:
            if m!='N':
                pl+=[tuple(map(int,m.split(',')))]
        b.add_ina(pl)
    elif msg[0] == "ACTIVATE":
        at=b.get_newact()
        print("VERTEX %d,%d"%(at[0], at[1]))
    elif msg[0] == "OWNED":
        pl=[]
        for m in msg[2:]:
            if m!='N':
                pl+=[tuple(map(int,m.split(',')))]        
        b.add_act(pl)
    elif msg[0] == "SCORE":
        break       

    sys.stdout.flush()
randomra
źródło
1

Irytacja, Bash

#!/bin/bash

declare -A avail
broken=
owned=

while read c p
    case "$c" in
        ACTIVATE|BROKEN) v=broken;;
        *) v=owned
    esac
    case "$c" in
        BEGIN)
            read b t n <<<"$p"
            list=$(
                eval "echo {0..$((n-1))},{0..$((n-1))}\$'\\n'" |
                shuf
            )
            for i in $list; do
                avail[$i]=1
            done;;
        DESTROY|ACTIVATE)
            for t in $(
                for i in ${!v}; do
                    [ "$i" != N ] &&
                    if [ "$c" = ACTIVATE ]; then
                        echo $(((${i%,*}+2)%n)),${i#*,}
                        echo $(((${i%,*}-2+n)%n)),${i#*,}
                    else
                        echo ${i%,*},$(((${i#*,}+1)%n))
                        echo ${i%,*},$(((${i#*,}-1+n)%n))
                    fi
                done |
                shuf
            ) $list; do
                [ "${avail[$t]}" ] && echo VERTEX $t && break
            done ||
            echo NONE;;
        BROKEN|OWNED)
            read x m $v <<<"$p";
            for i in $m ${!v}; do
                unset avail[$i]
            done;;
        SCORE)! :
    esac
do :;done

Próbowałem sprawić, by wynik wyglądał bardziej interesująco.

Uruchom z bash annoyance.sh.

jimmy23013
źródło
1
Twój bot drukuje wszystkie dane wejściowe do STDERR. To nie jest zabronione ani nic, tylko irytacja (gra słów zamierzona).
Zgarb
@Zgarb Przepraszamy, wkleiłem niewłaściwą wersję. Naprawiony.
jimmy23013
1

Middle Man

Widziałem, że niektóre boty budowane są z góry, a niektóre z dołu. To pierwszy (jak sądzę) początek w środku i praca w górę iw dół.

(nie jest testowany ze sterownikiem, więc jeśli to nie działa, daj mi znać.)

class Node

  def self.set_size s
    @@grid = Array.new(s,Array.new(s,0))
  end

  def initialize x,y
    @x=x
    @y=y
  end

  def offset dx,dy
    return Node.new @x+dx,@y+dy
  end

  def state
    return -1 if @x<0 || @y<0 || @x>=@@grid.length || @y>=@@grid.length
    @@grid[@x][@y]
  end

  def state= n
    return -1 if @x<0 || @y<0 || @x>=@@grid.length || @y>=@@grid.length
     @@grid[@x][@y]=n
  end

  def active?
    state > 0
  end

  def open?
    state == 0
  end
  attr_reader :x,:y

  def to_s
    "VERTEX #{@x},#{@y}"
  end


  def scan_down
    ans = nil
    [0,-1,1].each do|offset|
      n = Node.new @x+offset,@y-1
      ans = (ans||n) if n.open?
      ans = (n.scan_down||ans) if n.active?
    end
    return ans
  end

  def scan_up
    ans = nil
    [0,-1,1].each do|offset|
      n = Node.new @x+offset,@y+1
      ans = (ans||n) if n.open?
      ans = (n.scan_up||ans) if n.active?
    end
    return ans
  end

end

input = gets.split
input.shift

BotCount = input.shift.to_i
Turns = input.shift.to_i
GridSize = input.shift.to_i

Node.set_size GridSize

midRow = GridSize/2

toDestroy = (0...GridSize).map{|i|Node.new i,midRow}
toDestroy.reject!{|n| n.x==midRow}

chain = []
Turns.times do
  gets;
  toDestroy.each{|x|
    if x.active?
      toDestroy.push x.offset 0,1
      toDestroy.push x.offset 1,1
      toDestroy.push x.offset -1,1
    end
  }
  toDestroy.reject!{|x|!x.open?}
  puts toDestroy.sample
  input = gets.split
  input.shift;input.shift
  input.each{|str|
    a,b = str.split ','
    (Node.new a.to_i,b.to_i).state=1
  }
  gets;

  if chain.length == 0
    n = Node.new midRow,midRow
    until n.open?
      n = Node.new n.x+1,midRow
    end
    puts chain[0]=n
  elsif rand>0.5
    n=nil
    loop do
      h=chain[0]
      n = h.scan_down
     break if !n
      chain.shift
    end
    h.unshift n
    puts n
  else
    loop do
      h=chain[-1]
      n = h.scan_up
      h.pop if !n
      brake if n
    end
    chain.push n
    puts n
  end

  input = gets.split
  input.shift;input.shift
  input.each{|str|
    a,b = str.split ','
    (Node.new a,b).state=-1
  }

end
gets
exit
MegaTom
źródło
Dziękujemy za przesłanie! Niestety wyzwanie to było nieaktywne przez prawie pół roku i obecnie nie jestem w stanie uruchomić większości botów, ponieważ nie mam dostępu do komputera, na którym mógłbym zainstalować języki.
Zgarb
1
@Zgarb Rozumiem. może kiedyś odpowiem na wyzwanie w rozsądnym terminie ...
MegaTom