Construire une Go AI déterministe


11

Voici un problème intéressant auquel j'ai pensé l'autre jour, qui implique des bits de code en concurrence avec d'autres bits de code non seulement dans une propriété du code, mais en jouant à un jeu contre ces autres bits de code.

Votre tâche consiste à créer un programme qui prend l'état actuel d'une planche Go et détermine le mouvement à effectuer ou à passer.

Votre programme acceptera les éléments suivants en entrée:

  • 19 lignes, chacune avec 19 caractères, représentant les pièces actuellement sur le plateau Go. Un caractère 0représente un carré vide, 1est noir et 2blanc.

  • Deux nombres représentant le nombre de pièces de prisonnier que possède chaque joueur (noir, puis blanc).

  • Un nombre représentant à qui il revient de se déplacer (noir ou blanc). Comme ci-dessus, 1est noir et2 blanc.

et sortez l'un des éléments suivants:

  • Une paire de coordonnées a breprésentant les coordonnées auxquelles se déplacer. 1 1est le carré en haut à gauche, et les premier et deuxième nombres représentent respectivement le déplacement vers le bas et vers la droite.

  • La chaîne pass, qui représente un mouvement à passer.

Par exemple, le programme peut recevoir l'entrée suivante:

0000000000000000000
0000000000000000000
0000000000000000000
0001000000000002000
0000000000000000000
0000000000000000000
0001210000000000000
0000100000000000000
0000000000000000000
0000000000000000000
0000000000000000000
0000000000000000000
0000000000000000000
0000000000000000000
0000000000000000000
0002000000000001000
0000000000000000000
0000000000000000000
0000000000000000000
0 0 1

qui représente un jeu où seuls quelques coups ont été joués.

Ensuite, le programme pourrait sortir 6 5, ce qui signifie "mettre une pierre noire sur le point 6e du haut et 5e de la gauche". Cela capturerait la pierre blanche à 7 5. L'état du conseil changerait alors en:

0000000000000000000
0000000000000000000
0000000000000000000
0001000000000002000
0000000000000000000
0000100000000000000
0001010000000000000
0000100000000000000
0000000000000000000
0000000000000000000
0000000000000000000
0000000000000000000
0000000000000000000
0000000000000000000
0000000000000000000
0002000000000001000
0000000000000000000
0000000000000000000
0000000000000000000
1 0 2

(Notez que bien qu'une pierre blanche ait été capturée, elle compte comme prisonnier pour du noir.)

Votre code doit en outre satisfaire les propriétés suivantes:

  • Si votre programme reçoit le même état d'entrée, il doit toujours produire la même sortie. C'est le déterminisme du Go AI. Il ne doit pas avoir de composante aléatoire.

  • Votre programme ne doit pas prendre plus de 60 secondes environ pour déterminer quel mouvement effectuer. Cette règle ne sera pas strictement appliquée en raison des variations de la puissance de calcul, mais elle doit se déplacer dans un délai raisonnable.

  • Le code source de votre programme ne doit pas dépasser un total de 1 mégaoctet (1 048 576 octets).

  • Votre programme doit toujours faire des démarches légales. Votre programme ne peut pas faire un mouvement là où une pierre existe déjà, et ne peut pas placer un morceau qui entraînerait la capture d'un groupe de ses propres pierres. (Une exception aux règles aux fins de ce défi est qu'un programme est autorisé à créer un poste qui était à l'origine là-bas - parce qu'il n'est donné que la position actuelle d'un tableau, il ne peut pas être prévu de stocker les mouvements qui ont été effectués avant.)

Votre soumission jouera alors dans un tournoi tout jouer contre toutes les autres soumissions, dans une partie de Go où l'état du plateau commence comme vide, et chaque programme à tour de rôle est alimenté à la position du plateau et fait un mouvement .

Chaque paire de soumissions jouera deux tours - un tour avec chaque joueur étant noir. Étant donné que les IA de ce problème sont complètement déterministes, deux des mêmes IA jouant ensemble entraîneront toujours exactement le même jeu.

Les conditions pour gagner sont les suivantes:

  • Si votre programme se joue jusqu'à la fin de la partie, les règles de score chinois de Go seront utilisées pour déterminer le gagnant. Aucun komi ne sera appliqué.

  • Si votre programme est lu au point qu'un état antérieur est atteint, provoquant ainsi une boucle infinie, les deux programmes seront déclarés liés.

Votre soumission sera notée par le nombre de points qu'elle marque par rapport aux autres soumissions. Une victoire vaut 1 point et une égalité vaut un demi-point. La soumission avec le plus de points est le gagnant général.


Il s'agit d'un défi de taille, dans lequel n'importe qui peut publier une nouvelle inscription à tout moment, et le classement sera réévalué périodiquement lorsque cela se produira.


7
Ok, attendre toutes les autres soumissions et ensuite écrire les miennes pour les battre - devrait être possible car les solutions sont déterministes.
Howard

1
Il semble que jouer en ko de telle sorte que la position précédente soit répétée est autorisé mais conduit à un tirage immédiat (car il provoque une boucle). Intéressant ...
FireFly

2
Il semble que votre problème soit trop difficile et que personne ne travaillerait suffisamment pour produire une réponse valable (c'est vraiment beaucoup de travail). C'est un beau problème, mais il est trop difficile de travailler avec.
Victor Stafusa

1
Pourquoi ne pas utiliser une planche plus petite? 9x9 est assez commun pour les joueurs débutants. Cela réduit considérablement l'espace de recherche, mais il n'est pas si petit qu'il a été "battu" par l'analyse (je pense que le plus grand qui a été entièrement résolu est 5x6).
Geobits

1
Comment fonctionne la saisie? arguments stdin ou ligne de commande?
Ypnypn

Réponses:


7

Voici mon entrée pour lancer ce défi. Code Python:

print "pass"

Selon vos règles, toujours jouer "pass" est une stratégie valide (quoique mauvaise).


Votre code sera toujours perdu contre quiconque joue contre lui. Pourtant, belle réponse de base.
Joe Z.

1
@JoeZ. Et d'après son apparence, il a gagné avec: P
David Mulder

4

Java: choisissez un endroit, n'importe quel endroit

Choisit simplement les taches sur le tableau pour tester la validité. Il utilise le PRNG, mais avec une graine définie, c'est donc déterministe. Il utilise différents segments du cycle PRNG en fonction du nombre de tours passés.

Pour chaque poste candidat, il vérifie que c'est un coup valide (mais pas que c'est un coup intelligent ). Si ce n'est pas le cas, il passe au candidat suivant. S'il ne trouve pas de coup valide après 1000 essais, il passe.

import java.util.Random;
import java.util.Scanner;

public class GoNaive {

    int[][] board;
    boolean[] checked;
    int me;

    public static void main(String[] args) {
        new GoNaive().run();
    }

    void run(){
        int turns = init();
        Random rand = new Random(seed);

        for(int i=0;i<turns*tries;i++)
            rand.nextInt(size*size);

        for(int i=0;i<tries;i++){
            int pos = rand.nextInt(size*size);
            for(int c=0;c<size*size;c++)
                checked[c]=false;
            if(board[pos%size][pos/size] == 0)
                if(hasLiberties(pos, me)){
                    System.out.print((pos%size+1) + " " + (pos/size+1));
                    System.exit(0);
                }
        }
        System.out.print("pass");
    }

    boolean hasLiberties(int pos, int color){
        if(checked[pos])
            return false;
        checked[pos] = true;

        int x = pos%size, y=pos/size, n;

        if(x>0){
            n = board[x-1][y];
            if(n==0 || (n==me && hasLiberties(y*size+x-1, color)))
                return true;
        }
        if(size-x>1){
            n = board[x+1][y];
            if(n==0 || (n==me && hasLiberties(y*size+x+1, color)))
                return true;
        }
        if(y>0){
            n = board[x][y-1];
            if(n==0 || (n==me && hasLiberties((y-1)*size+x, color)))
                return true;
        }
        if(size-y>1){
            n = board[x][y+1];
            if(n==0 || (n==me && hasLiberties((y+1)*size+x, color)))
                return true;
        }
        return false;
    }

    int init(){
        int turns = 0;
        board = new int[size][size];
        checked = new boolean[size*size];
        turns = 0;
        Scanner s = new Scanner(System.in);
        String line;
        for(int i=0;i<size;i++){
            line = s.nextLine();
            for(int j=0;j<size;j++){
                board[j][i] = line.charAt(j)-48;
                if(board[j][i] > 0)
                    turns++;
            }
        }
        String[] tokens = s.nextLine().split(" ");
        turns += Integer.valueOf(tokens[0]);
        turns += Integer.valueOf(tokens[1]);
        me = Integer.valueOf(tokens[2]);
        s.close();
        return turns;
    }

    final static int size = 19;
    final static int seed = 0xdeadface;
    final static int tries = 1000;
}

2

Certains Scala:

package go;

class Go {
  def main(args : Array[String]) {
    readLine()
    readLine()
    readLine()
    readLine()
    readLine()
    readLine()
    readLine()
    readLine()
    readLine()
    readLine()
    readLine()
    readLine()
    readLine()
    readLine()
    readLine()
    readLine()
    readLine()
    readLine()
    readLine()
    System.out.printLn("1 1")
    readLine()
    readLine()
    readLine()
    readLine()
    readLine()
    readLine()
    readLine()
    readLine()
    readLine()
    readLine()
    readLine()
    readLine()
    readLine()
    readLine()
    readLine()
    readLine()
    readLine()
    readLine()
    readLine()
    System.out.printLn("pass")
  }
}

En lisant Wikipédia, je pense que cela battra la solution actuelle.


En fait, il gagnera 361 points dans les deux cas.
Joe Z.

En fait, je vais devoir reprendre cela, il ne suit pas les spécifications. L'IA est censée être apatride. Il est seulement censé imprimer une chose étant donné l'état de la carte, et vous l'avez fait imprimer deux ( 1 1et pass).
Joe Z.

@JoeZ. A corrigé. N'aurait pas compilé de toute façon.
yayestechlab

En fait, cela s'imprimera toujours1 1 , car le programme est toujours exécuté à chaque fois que la carte change.
Joe Z.

1

Java

public class Go {
  public static void main(String[] args) {
    Scanner s = new Scanner(System.in);
    for (int i = 0; i < 361;) {
      char c = s.nextChar();
      if (c != '\n') {
        if (c == '0') {
          System.out.println((i % 19 + 1) + " " + (i / 19 + 1));
          System.exit(0);
        }
        i++;
      }
    }
  }
}

Choisit le premier espace vide. Victoires contre l'une des IA au moment de la publication.


2
Cela ne garantit pas une décision légale. Si le premier espace disponible n'a aucune liberté, il ne peut pas être lu. Par exemple, si cette IA se jouait d'elle-même: après la première rangée de pièces alternées, elle 1 1serait capturée par des blancs (maintenant vides) et ne pourrait pas être jouée par des noirs au tour suivant.
Geobits
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.