Grid-Routing Battle


22

ПРИМЕЧАНИЕ. В настоящее время этот вызов не решен, поскольку я не могу установить языки, необходимые для запуска матча. Если у кого-то есть время и интерес, чтобы сделать это, я не против.

Смотрите в нижней части поста для лидеров.

Это полу-кооперативный вызов царя холма, где боты строят пути через двумерный сеточный граф. Бот, который контролирует узлы с наибольшим количеством трафика, является победителем. Однако для создания соединительного пути требуется больше, чем один бот, поэтому ботам придется работать вместе - в некоторой степени.

Игровой процесс

Далее, пусть N > 0будет количество ботов в игре.

Сетки

Игра ведется на двумерной целочисленной сетке размером , нижняя левая координата которой равна . Каждая координата с имеют исходящие ребра к трем координатам , и над ним, где -координаты берутся по модулю . Это означает, что сетка оборачивается на восточном и западном краях. Каждая нижняя координата является источником , а каждая верхняя координата является стоком .⌊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)

На следующем рисунке показана 8 × 8сетка.

Сетка 8х8.

Каждая вершина графа является неактивной , активной или сломанной . Все вершины начинаются неактивно и могут быть активированы ботами, которые затем станут их владельцами. Кроме того, боты могут ломать вершины, и они не могут быть восстановлены.

Заказ поворота

Поворот состоит из фазы разрушения и фазы активации . На этапе уничтожения каждый бот может сломать одну неактивную вершину. С тех пор эта вершина нарушается и может быть никем не активирована. На этапе активации каждый бот может активировать одну неактивную вершину. С тех пор они владеют этой вершиной, и никто другой не может ее активировать. У нескольких ботов может быть одна вершина, если они все активируют ее в один ход. На каждом этапе выбор вершин выполняется одновременно.

счет

Один раунд длится ровно по очереди. После этого раунд засчитывается следующим образом. Из каждой активной исходной вершины мы выполняем случайный поиск в глубину вдоль активных вершин (это означает, что дочерние элементы каждой вершины посещаются в случайном порядке). Если путь найден от источника до некоторого стока, то для всех вершин вдоль этого пути каждый владелец вершины получает одну точку.N2N

Вся игра длится 100 раундов, и бот с наибольшим количеством очков является победителем. Я могу увеличить это число, если дисперсия оценок слишком высока.

Дополнительные правила

  • Нет возиться с контроллером или другими представлениями.
  • Максимум одно представление на одного участника.
  • Никакие внешние ресурсы, кроме одного личного текстового файла, очищены в начале игры.
  • Не заставляйте своего бота побеждать или поддерживать конкретных противников.
  • Предоставьте команды для компиляции и запуска вашего бота. Любой компилятор / интерпретатор, который свободно доступен для Debian Linux, является приемлемым.

Контроллер

Контроллер написан на Python 3 и может быть найден в GitHub . Смотрите файл README для получения подробных инструкций. Вот API для начала:

  • Боты запускаются в начале каждого раунда и сохраняются до конца раунда. Связь с контроллером осуществляется через STDIN и STDOUT, используя завершающие строки сообщения.
  • BEGIN [num-of-bots] [num-of-turns] [side-length] ввод в начале.
  • DESTROY [turn]вводится в начале каждой фазы уничтожения. Ваш бот должен ответить либо VERTEX x,yвыбрать вершину, либо NONE.
  • BROKEN [turn] [your-choice] [other-choices]вводится в конце каждой фазы разрушения. Порядок других ботов рандомизируется в начале каждой игры, но остается неизменным в течение этого времени. Выбор представлен как x,yили N.
  • ACTIVATE [turn]и OWNED [turn] [your-choice] [other-choices]являются эквивалентами вышеуказанного для фазы активации и имеют одинаковую семантику.
  • SCORE [your-score] [other-scores] ввод в конце игры.
  • У вашего бота есть 1 секунда, чтобы проанализировать результаты фазы и выбрать следующую вершину, и 1 секунда, чтобы выйти после получения оценки. Я протестирую материалы на своем относительно старом ноутбуке, поэтому лучше оставить здесь запас.

Пожалуйста, не забудьте очистить ваш выходной буфер. Невыполнение этого требования может привести к зависанию контроллера в некоторых средах.

Leaderboard

Обновлено 13.03.2015

Peacemaker запущен и работает, и Funnelweb также получил обновление. Баллы подскочили на порядок. Коннектор превысил ограничение по времени в двух играх.

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

Полный журнал с графикой ASCII можно найти в репозитории контроллера, в graphical_log.txt.

Некоторые наблюдения:

  • Соединитель можно очень легко остановить, разбив одну вершину перед ним. Я подозреваю, что раздражение часто делает это. Однако в настоящее время это не имеет большого смысла, поскольку только Connector может создать путь.
  • Арбуз может получить приличный счет, просто оказавшись на соединительном пути (так как DFS, скорее всего, будет использовать его вершины).
  • Исследователь любит выращивать виноградные лозы из арбузов.
  • Обновленный Funnelweb получает действительно хорошие оценки, поскольку Connector обычно фиксирует его в нижней половине сетки.
  • Игры становятся довольно длинными, средний раунд занимает около 25 секунд на моей машине.

1
@ Алекс Я пытался придумать задачу, чтобы боты-самоубийцы не все испортили. Три хорошо спроектированных бота всегда должны быть в состоянии построить правильный путь, если они работают вместе.
Згарб

2
@Zgarb Самоубийство не должно испортить это, но пара тролл-ботов, работающих вместе, вероятно, также могли бы заблокировать все пути, разрушая игру.
Geobits

2
@CarpetPython Активные узлы не могут быть уничтожены.
Згарб

1
Кажется, мы вряд ли увидим какие-либо интересные игры с действующими игроками и правилами. Я предлагаю вам немного изменить правила, чтобы создать возможности для интересных игр. Изменение размера сетки на 1,5 * N ^ 2 вместо 2 * N ^ 2 должно быть хорошим и не должно слишком сильно сбивать с толку существующих роботов.
Рыцарь логики

1
@justhalf Это правда. Игры в журнале фактически игрались с дальнейшим уменьшением размера сетки 4/3*N^2, и даже там у ботов были проблемы с формированием правильных путей. Однако, Connector был временно дисквалифицирован из-за ошибки, и теперь, когда это было исправлено, я ожидаю, что игры будут более интересными. Сегодня вечером я запущу еще одну партию.
Згарб

Ответы:


7

Соединитель (Java)

Пытается создать путь в случайной позиции. Поскольку он не может создать путь сам по себе, он ищет активные ячейки и использует их. Кредит идет на Geobits, у которого я украл некоторый код Кроме того, эта отправка еще не закончена, так как она просто перестает делать что-либо, как только будет построен путь.

Редактировать: если путь построен, Connector пытается создать несколько путей вдоль существующего.

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

@Zgarb Извините, я создал ошибку, исправляя другую. Это работает сейчас
CommonGuy

@ Ману, хорошо, что ты вернулся в игру. Слишком много эксплуататоров и недостаточно строителей. Когда работает Connector, игры могут стать более интересными (более 1 игры на 100 с оценкой).
Логика Найт

Соединителю потребовалось 28 секунд, чтобы ответить в одной из последних игр (см. Журнал). Похоже, он столкнулся с Арбузом и с трудом решал, куда идти дальше.
Згарб

Я провел несколько игр снова с улучшенной Миротворец, и разъем бросил ошибку: java.lang.ArrayIndexOutOfBoundsException: -1 at Connector.findExtendingPathPoint(Connector.java:166).
Згарб

7

Воронка, Python 2

версия 1.2 - улучшено объединение кода, добавлена ​​новая анимация

Назван в честь одного из менее дружелюбных пауков Австралии. Этот бот сначала строит гнездо в форме воронки в верхних рядах, а затем заманивает других ботов на пути строительства к гнезду.

Вот новая анимация игры с 6 ботами на доске 4 / 3N ^ 2, показывающая воронку и несколько простых ботов:

bots6.gif

Python-код 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

Паук бежит с python funnelweb.py.


Изменили алгоритм и протестировали его. Это должно бежать сейчас.
Логика Рыцарь

Отлично работает сейчас!
Згарб

6

Контрольная точка, Java

Этот бот пытается создать контрольные точки, чтобы любой допустимый путь проходил через одну из моих вершин. Так как есть N 2 поворотов и доска имеет 2N 2 в поперечнике, я могу активировать / разбить каждый узел на одной горизонтальной линии (при условии, что я там первый). Сделайте это по-другому ( xсломано, oэто мое):

xoxoxoxoxoxox...

Если вы хотите проложить путь, вам нужно пройти через мои контрольные точки :)

Теперь есть несколько проблем, с которыми это может столкнуться. Во-первых, это не будет очень хорошо, если есть много путей. Поскольку он сам не делает никаких продуктивных путей, он действительно полностью зависит от присутствия некоторых конкурентов. Даже пара конкурентов, объединившихся в единый путь, мало чем поможет, так как он получает только одно место за каждый найденный путь. То, что ему нужно, чтобы сиять, - это, вероятно, немало ботов, делающих немало разных путей. Даже тогда это может быть не очень высоко, но у меня была идея в чате, так что ...

Если одно из пробелов на линии уже заблокировано / заявлено, я просто ищу ближайший участок, который могу использовать (желательно на той же xстроке, только смещенный вертикально).


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

Чтобы скомпилировать, это javac Checkpoint.java. Бежать java Checkpoint. Вы хотите добавить / изменить путь, чтобы отразить, где бы он ни находился.


5

Арбуз, Ява

Попытки нарисовать арбузы на сетке.

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

5

FaucetBot (в R)

Создает узкое место во второй строке и активирует узлы на пути позади него.

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")
    }

Если я не облажался, окончательная конфигурация должна выглядеть примерно так:

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

Команда есть Rscript FaucetBot.R.


5

Миротворец, Ява

Основано на коде Ману.

Миротворец ищет конфликтные зоны (т. Е. Большинство BROKEN или ACTIVE концентрации вершин) и активирует случайную вершину поблизости.

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

@ Zgarb Спасибо, я должен был решить эту проблему сейчас.
Thrax

Миротворец работает сейчас и входит в таблицу лидеров. Тем не менее, похоже, что это мало что делает, поэтому, вероятно, еще остались некоторые ошибки.
Згарб

На самом деле, глядя на ваш код, я думаю, что проблема в whileцикле activateметода. Вы прекращаете поиск, когда находите вершину, которая не принадлежит вам и не нарушена - но она может принадлежать кому-то другому, поэтому вы не можете ее активировать.
Згарб

@Zgarb Я неправильно понял спецификации и подумал, что несколько игроков могут активировать одну и ту же вершину в любое время. Я думаю, мне просто нужно изменить свой поиск и искать только неактивную вершину.
Thrax

2

Случайный строитель, Python 3

Это глупый пример бота, который никогда ничего не разрушает и пытается активировать случайную вершину каждый ход. Обратите внимание, что не нужно проверять, не активна ли вершина; контроллер позаботится об этом.

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

Запустить с командой

python3 random_builder.py

Возможно, вам придется заменить python3в pythonзависимости от вашей установки Python. Для этого просто отредактируйте bots.txtфайл. Я обновил контроллер, и больше не нужно связываться с путями к файлам.


Поскольку вы используете Python 3, вместо sys.stdout.flush()вас можно просто сделать flush=Trueв качестве аргумента print.
matsjoyce

@matsjoyce Спасибо, я этого не знал. Я отредактирую версию репозитория позже.
Згарб

2

Explorer, Python 3

Стратегия активации:

Создает тепловую карту на основе состояния каждого узла (активный / неактивный / неисправный) и выбирает узел, который будет иметь наибольшее ожидаемое значение тепловой карты на человека, если он выберет этот.

Стратегия уничтожения:

Никогда ничего не разрушает, так как это мало помогает боту.

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

1

Раздражение, Баш

#!/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

Постарался, чтобы результат выглядел интереснее.

Беги с bash annoyance.sh.


1
Ваш бот печатает все свои данные в STDERR. Это не запрещено или что-либо, только раздражение (каламбур).
Згарб

@Zgarb Извините, я вставил не ту версию. Исправлена.
jimmy23013

1

Средний человек

Я видел, что некоторые боты построены сверху, а некоторые снизу. Это первое (я думаю) начало в середине и работа вверх и вниз.

(он не тестируется с контроллером, поэтому, если он не работает, дайте мне знать.)

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

Спасибо за представление! К сожалению, эта проблема была бездействующей в течение почти полугода, и в настоящее время я не могу запустить большинство ботов, поскольку у меня нет доступа к компьютеру, на котором я мог бы установить языки.
Згарб

1
@ Згарб, я понимаю. может быть, когда-нибудь я отвечу на вызов в разумные сроки ...
MegaTom
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.