Bataille de routage de grille


22

REMARQUE: ce défi est actuellement mort, car je ne peux pas installer les langues nécessaires pour exécuter une correspondance. Si quelqu'un d'autre a le temps et l'intérêt de le faire, je ne m'y oppose pas.

Voir le bas du message pour un classement.

Il s'agit d'un défi semi-coopératif du roi de la colline, où les bots construisent des chemins à travers un graphique en grille à deux dimensions. Le bot qui contrôle les nœuds avec le plus de trafic est le gagnant. Cependant, il faut plus de ressources d'un bot pour réellement construire un chemin de connexion, donc les bots devront travailler ensemble - dans une certaine mesure.

Gameplay

Dans ce qui suit, N > 0soit le nombre de bots en jeu.

La grille

Le jeu se joue sur une grille entière bidimensionnelle de taille , dont la coordonnée en bas à gauche est à . Chaque coordonnée avec comporte des bords sortants vers les trois coordonnées , et au- dessus, où les -coordinates sont prises modulo . Cela signifie que la grille s'enroule sur les bords est et ouest. Chaque coordonnée inférieure est une source et chaque coordonnée supérieure est un puits .⌊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)

L'image suivante montre une 8 × 8grille.

Une grille 8x8.

Chaque sommet du graphique est inactif , actif ou cassé . Tous les sommets commencent inactifs et peuvent être activés par des bots, qui seront alors leurs propriétaires. De plus, les bots peuvent casser des sommets et ne peuvent pas être réparés.

Tournez l'ordre

Un tour consiste en une phase de destruction et une phase d'activation . Dans la phase de destruction, chaque bot peut casser un sommet inactif. Ce sommet est brisé à partir de ce moment et ne peut être activé par personne. Dans la phase d'activation, chaque bot peut activer un sommet inactif. À partir de ce moment, ils possèdent ce sommet, et il ne peut être réactivé par personne d'autre. Plusieurs robots peuvent posséder un seul sommet, s'ils l'activent tous au même tour. Dans chaque phase, les sélections de sommets sont effectuées simultanément.

Notation

Un tour dure exactement les tours. Après cela, le tour est marqué comme suit. À partir de chaque sommet source actif, nous effectuons fois une recherche aléatoire en profondeur d'abord le long des sommets actifs (ce qui signifie que les enfants de chaque sommet sont visités dans un ordre aléatoire). Si un chemin est trouvé de la source vers un puits, alors pour tous les sommets le long de ce chemin, chaque propriétaire du sommet obtient un point.N2N

Le jeu entier dure 100 tours, et le bot avec le plus de points au total est le gagnant. Je peux augmenter ce nombre, si la variance des scores est trop élevée.

Règles supplémentaires

  • Pas de problème avec le contrôleur ou d'autres soumissions.
  • Au plus une soumission par candidat.
  • Aucune ressource externe, à l'exception d'un fichier texte privé, nettoyé au début du jeu.
  • Ne concevez pas votre bot pour battre ou soutenir des adversaires spécifiques.
  • Fournissez des commandes pour compiler et exécuter votre bot. Tout compilateur / interprète librement disponible pour Debian Linux est acceptable.

Le controlle

Le contrôleur est écrit en Python 3 et se trouve dans GitHub . Voir le fichier README pour des instructions détaillées. Voici une API pour vous aider à démarrer:

  • Les bots commencent au début de chaque manche et persistent jusqu'à la fin de la manche. La communication avec le contrôleur via STDIN et STDOUT, à l'aide de messages terminés par une nouvelle ligne.
  • BEGIN [num-of-bots] [num-of-turns] [side-length] est entrée au début.
  • DESTROY [turn]est entré au début de chaque phase de destruction. Votre bot doit répondre soit VERTEX x,ypour choisir un sommet, soit NONE.
  • BROKEN [turn] [your-choice] [other-choices]est entré à la fin de chaque phase de destruction. L'ordre des autres bots est aléatoire au début de chaque partie, mais reste fixe pendant celui-ci. Les choix sont présentés comme x,you N.
  • ACTIVATE [turn]et OWNED [turn] [your-choice] [other-choices]sont les équivalents de ce qui précède pour la phase d'activation, et ont la même sémantique.
  • SCORE [your-score] [other-scores] est entré à la fin de la partie.
  • Votre bot a 1 seconde pour analyser les résultats d'une phase et choisir le sommet suivant, et 1 seconde pour quitter après avoir obtenu le score. Je vais tester les soumissions sur mon ordinateur portable relativement vieux, il est donc préférable de laisser une marge ici.

N'oubliez pas de vider votre tampon de sortie. Ne pas le faire peut bloquer le contrôleur dans certains environnements.

Classement

Mise à jour 13/03/2015

Peacemaker est opérationnel et Funnelweb a également reçu une mise à jour. Les scores ont bondi d'un ordre de grandeur. Le connecteur a dépassé la limite de temps dans deux jeux.

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

Le journal complet avec les graphiques artistiques ASCII se trouve dans le référentiel du contrôleur, dans graphical_log.txt.

Quelques observations:

  • Le connecteur peut très facilement être arrêté en cassant un seul sommet devant lui. Je soupçonne que l'agacement fait souvent cela. Cependant, cela n'a actuellement guère de sens car seul le connecteur peut concevoir un chemin.
  • La pastèque peut obtenir un score décent en se trouvant simplement sur un chemin de connexion (car le DFS est très susceptible d'utiliser ses sommets).
  • Explorer aime cultiver des vignes à partir des pastèques.
  • Le Funnelweb mis à jour obtient de très bons scores, car Connector se verrouille généralement dessus dans la moitié inférieure de la grille.
  • Les jeux deviennent assez longs, la manche moyenne prenant environ 25 secondes sur ma machine.

1
@Alex J'ai essayé de concevoir le défi pour que les robots suicidaires ne gâchent pas tout. Trois robots bien conçus devraient toujours être en mesure de construire un chemin valide, s'ils fonctionnent ensemble.
Zgarb

2
@Zgarb Suicide ne devrait pas le gâcher, mais quelques troll-bots travaillant ensemble pourraient probablement également bloquer tous les chemins, ruinant le jeu.
Geobits

2
@CarpetPython Les nœuds actifs ne peuvent pas être détruits.
Zgarb

1
Il semble que nous ne verrions probablement pas de jeux intéressants avec les joueurs et les règles actuels. Je vous suggère de modifier légèrement les règles pour créer des opportunités de jeux intéressants. Changer la taille de la grille à 1,5 * N ^ 2 au lieu de 2 * N ^ 2 devrait être bon et ne pas trop confondre les robots existants.
Logic Knight

1
@justhalf C'est vrai. Les jeux dans le journal ont en fait été joués avec la taille de grille encore plus réduite 4/3*N^2, et même là, les bots ont eu des problèmes pour former des chemins valides. Cependant, Connector a été temporairement disqualifié en raison d'une erreur, et maintenant qu'il a été corrigé, je m'attends à ce que les jeux soient plus intéressants. Je vais exécuter un autre lot ce soir.
Zgarb

Réponses:


7

Connecteur (Java)

Tente de créer un chemin à une position aléatoire. Puisqu'il ne peut pas créer seul un chemin, il recherche les cellules actives et les utilise. Le mérite revient à Geobits, à qui j'ai volé du code. De plus, cette soumission n'est pas encore terminée, car elle cesse simplement de faire quoi que ce soit dès que le chemin est créé.

Modifier: si un chemin est créé, Connector essaie de créer plusieurs chemins le long du chemin existant.

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 Désolé, j'ai créé un bogue lors de la correction de l'autre. Cela fonctionne maintenant
CommonGuy

@Manu, c'est bien que tu sois de retour dans le jeu. Il y a trop d'exploiteurs et pas assez de constructeurs. Avec Connector en cours d'exécution, les jeux peuvent devenir plus intéressants (plus d'un jeu sur 100 avec un score).
Logic Knight

Le connecteur a mis 28 secondes pour répondre à l'un des derniers jeux (voir le journal). On dirait qu'il a couru dans la pastèque et a eu du mal à décider où aller ensuite.
Zgarb

J'ai couru quelques jeux à nouveau avec l'amélioration Peacemaker, et connecteur jeté une erreur: java.lang.ArrayIndexOutOfBoundsException: -1 at Connector.findExtendingPathPoint(Connector.java:166).
Zgarb

7

Funnelweb, Python 2

version 1.2 - Meilleure jonction de code, nouvelle animation ajoutée

Nommé d'après l'une des araignées les moins amicales d'Australie. Ce bot construit d'abord un nid en forme d'entonnoir dans les rangées supérieures, puis attire d'autres bots dans des chemins de construction pour le trafic vers le nid.

Voici une nouvelle animation d'un jeu à 6 bots sur un tableau 4 / 3N ^ 2 montrant l'entonnoir et quelques bots plus simples:

bots6.gif

Le code Python de 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

L'araignée est exécutée avec python funnelweb.py.


Algorithme modifié et testé. Il devrait fonctionner maintenant.
Logic Knight

Fonctionne très bien maintenant!
Zgarb

6

Checkpoint, Java

Ce bot essaie de créer des points de contrôle afin que tout chemin valide passe par l'un de mes sommets. Puisqu'il y a N 2 tours et que la carte est 2N 2 de diamètre, je peux activer / casser chaque nœud sur une seule ligne horizontale (en supposant que je suis là en premier). Pour ce faire, en alternance ( xest cassé, oest le mien):

xoxoxoxoxoxox...

Si vous voulez faire un chemin, vous devez passer par mes points de contrôle :)

Maintenant, il peut y avoir plusieurs problèmes. Tout d'abord, ça ne va pas très bien du tout à moins qu'il y ait beaucoup de chemins. Comme il ne fait pas des chemins de production lui - même, il est vraiment totalement dépendante de l'existence d'certains concurrents. Même un couple de concurrents se combinant pour faire un seul chemin n'aidera pas beaucoup, car il ne marque qu'une place pour chaque chemin trouvé. Ce dont il a besoin pour briller, c'est probablement pas mal de bots qui font pas mal de chemins différents. Même alors, cela pourrait ne pas marquer très bien, mais c'était une idée que j'avais dans le chat, alors ...

Si l'un des espaces sur la ligne est déjà bloqué / revendiqué, je recherche simplement un endroit proche que je peux utiliser (de préférence sur la même xligne, juste décalé verticalement).


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

Pour compiler, c'est javac Checkpoint.java. Pour exécuter, java Checkpoint. Vous voudrez ajouter / modifier le chemin pour refléter où qu'il se trouve.


5

Pastèque, Java

Tente de dessiner des pastèques sur la grille.

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

Crée un goulot d'étranglement sur la deuxième ligne et active les nœuds sur le chemin derrière elle.

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

Si je n'ai pas bousillé, la configuration finale devrait ressembler à ceci:

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

Le commandement est Rscript FaucetBot.R.


5

Peacemaker, Java

Basé sur le code de Manu.

Peacemaker recherche les zones de conflit (c'est-à-dire la plupart des concentrations de vertex cassées ou actives) et active un vertex aléatoire à proximité.

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 Merci, j'aurais dû résoudre ce problème maintenant.
Thrax

Peacemaker fonctionne maintenant et est inclus dans le classement. Cependant, cela ne semble pas faire grand-chose, il reste donc probablement quelques bugs.
Zgarb

En fait, en regardant votre code, je pense que le problème est dans la whileboucle de la activateméthode. Vous arrêtez la recherche une fois que vous avez trouvé un sommet qui n'est pas le vôtre et qui n'est pas cassé - mais il pourrait appartenir à quelqu'un d'autre, vous ne pouvez donc pas l'activer.
Zgarb

@Zgarb J'ai mal lu les spécifications et j'ai pensé que plusieurs joueurs pouvaient activer le même sommet à tout moment. Je suppose que je dois juste changer ma recherche et rechercher uniquement le sommet inactif.
Thrax

2

Générateur aléatoire, Python 3

Il s'agit d'un exemple de bot stupide qui ne détruit jamais rien et essaie d'activer un sommet aléatoire à chaque tour. Notez qu'il n'a pas besoin de vérifier si le sommet est inactif; le contrôleur s'en charge.

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

Exécuter avec la commande

python3 random_builder.py

Vous devrez peut-être le remplacer python3en pythonfonction de votre installation Python. Pour ce faire, modifiez simplement le bots.txtfichier. J'ai mis à jour le contrôleur, et il n'est plus nécessaire de jouer avec les chemins de fichiers.


Depuis que vous utilisez python 3, au lieu de sys.stdout.flush()vous, vous pouvez simplement le faire flush=Truecomme argument print.
matsjoyce

@matsjoyce Merci, je ne le savais pas. Je modifierai la version du référentiel plus tard.
Zgarb

2

Explorer, Python 3

Stratégie d'activation:

Crée une carte thermique basée sur l'état de chaque nœud (actif / inactif / cassé) et choisit le nœud qui aurait la plus grande valeur de carte thermique attendue par personne s'il choisissait celui-ci.

Stratégie de destruction:

Ne détruit jamais rien car cela n'aide pas beaucoup le bot.

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

Agacement, 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

J'ai essayé de rendre le résultat plus intéressant.

Courez avec bash annoyance.sh.


1
Votre bot imprime toutes ses entrées dans STDERR. Ce n'est pas interdit ou quoi que ce soit, juste un ennui (jeu de mots voulu).
Zgarb

@Zgarb Désolé, j'ai collé la mauvaise version. Fixé.
jimmy23013

1

Homme du milieu

J'ai vu que certains bots étaient construits par le haut et certains par le bas. C'est le premier (je pense) à commencer au milieu et à monter et descendre.

(il n'est pas testé avec le contrôleur, donc s'il ne fonctionne pas, faites le moi savoir.)

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

Merci pour la soumission! Malheureusement, ce défi est en sommeil depuis près de six mois et je ne suis actuellement pas en mesure d'exécuter la plupart des bots, car je n'ai pas accès à un ordinateur où je pourrais installer les langues.
Zgarb

1
@Zgarb je comprends. peut-être qu'un jour je répondrai à un défi dans un délai raisonnable ...
MegaTom
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.