Costruisci un AI deterministico


11

Ecco un problema interessante che ho pensato l'altro giorno, che coinvolge bit di codice in competizione con altri bit di codice non solo in una proprietà del codice, ma giocando un gioco contro quegli altri bit di codice.

Il tuo compito è costruire un programma che prenda lo stato attuale di una Go board e determina quale mossa fare o passare.

Il tuo programma accetterà quanto segue come input:

  • 19 righe, ognuna con 19 caratteri, che rappresentano i pezzi attualmente sul tabellone. Un personaggio di 0rappresenta un quadrato vuoto, 1è nero ed 2è bianco.

  • Due numeri che rappresentano il numero di pezzi prigionieri di ogni giocatore (nero, poi bianco).

  • Un numero che rappresenta di chi si sposta (bianco o nero). Come sopra, 1è nero ed 2è bianco.

e genera uno dei seguenti:

  • Una coppia di coordinate che a brappresentano le coordinate su cui muoversi. 1 1è il quadrato in alto a sinistra, e il primo e il secondo numero rappresentano rispettivamente lo spostamento verso il basso e verso destra.

  • La stringa pass, che rappresenta una mossa da passare.

Ad esempio, il programma potrebbe ricevere il seguente input:

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

che rappresenta un gioco in cui sono state giocate solo poche mosse.

Quindi il programma potrebbe emettere 6 5, il che significa "metti una pietra nera sul punto 6 dall'alto e 5 ° da sinistra". Ciò catturerebbe la pietra bianca a 7 5. Lo stato del consiglio cambierebbe quindi in:

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

(Nota che sebbene sia stata catturata una pietra bianca, conta come un prigioniero per il nero.)

Il codice deve inoltre soddisfare le seguenti proprietà:

  • Se al tuo programma viene assegnato lo stesso stato di input, deve sempre produrre lo stesso output. Questo è il determinismo di Go AI. Non deve avere un componente casuale.

  • Il programma non deve richiedere più di circa 60 secondi per determinare quale mossa fare. Questa regola non sarà applicata rigorosamente a causa delle variazioni della potenza di calcolo, ma deve fare una mossa in un ragionevole lasso di tempo.

  • Il codice sorgente del programma non deve superare un totale di 1 megabyte (1.048.576 byte).

  • Il tuo programma deve sempre fare mosse legali. Il tuo programma non può fare una mossa dove esiste già una pietra, e non può posizionare un pezzo che provocherebbe la cattura di un gruppo di pietre. (Un'eccezione alle regole ai fini di questa sfida è che un programma è autorizzato a creare una posizione che era originariamente lì - poiché è data solo la posizione corrente di una tavola, non ci si può aspettare che memorizzi quali mosse sono state fatte prima.)

La tua sottomissione giocherà quindi in un torneo a tutto campo contro tutte le altre sottomissioni, in una partita di Go in cui lo stato del tabellone inizia come vuoto, e ogni programma a turno viene alimentato dalla posizione del tabellone e fa una mossa .

Ogni coppia di presentazioni giocherà due round - uno a turno con ogni giocatore nero. Poiché gli AI in questo problema sono completamente deterministici, due degli stessi AI che giocano insieme comporteranno sempre esattamente lo stesso gioco.

Le condizioni per una vittoria sono tali:

  • Se il tuo programma si gioca fino alla fine del gioco, per determinare il vincitore verranno utilizzate le regole cinesi per il punteggio di Go. Nessun komi verrà applicato.

  • Se il tuo programma gioca al punto da raggiungere uno stato precedente, causando così un ciclo infinito, i due programmi verranno dichiarati vincolati.

Il tuo invio verrà valutato in base al numero di punti ottenuti rispetto ad altri invii. Una vittoria vale 1 punto e un pareggio vale mezzo punto. La presentazione con il maggior numero di punti è il vincitore assoluto.


Questa è una sfida da re della collina, in cui chiunque può pubblicare una nuova voce in qualsiasi momento, e le classifiche saranno rivalutate periodicamente quando ciò accade.


7
Ok, aspettando tutti gli altri invii e poi scrivendo i miei per batterli, dovrebbe essere possibile poiché le soluzioni sono deterministiche.
Howard,

1
Sembra suonare in ko in modo tale che la posizione precedente sia ripetuta è consentita ma porta a un pareggio immediato (poiché provoca un loop). Interessante ...
FireFly

2
Sembra che il tuo problema sia troppo difficile e nessuno lavorerebbe abbastanza duramente per produrre una risposta degna (è davvero un sacco di lavoro). È un bel problema, ma andare è troppo difficile con cui lavorare.
Victor Stafusa,

1
Perché non usare una scheda più piccola? 9x9 è abbastanza comune per i giocatori principianti. Riduce drasticamente lo spazio di ricerca, ma non è così piccolo che è stato "battuto" dall'analisi (penso che il più grande che è stato completamente risolto sia 5x6).
Geobits

1
Come funziona l'input? argomenti stdin o della riga di comando?
Ypnypn,

Risposte:


7

Ecco il mio contributo per far decollare questa sfida. Codice Python:

print "pass"

Secondo le tue regole, giocare sempre "pass" è una strategia valida (anche se cattiva).


Il tuo codice perderà sempre contro chiunque abbia giocato contro di esso. Comunque, bella risposta alla base.
Joe Z.

1
@JoeZ. E dal suo aspetto ha vinto con esso: P
David Mulder,

4

Java: scegli un posto, qualsiasi posto

Scegli semplicemente i punti sul tabellone per verificarne la validità. Usa il PRNG, ma con un seme impostato quindi è deterministico. Utilizza diversi blocchi del ciclo PRNG a seconda di quanti turni sono passati.

Per ogni posizione candidata, verifica che sia una mossa valida (ma non che sia una mossa intelligente ). In caso contrario, passa al candidato successivo. Se non riesce a trovare una mossa valida dopo 1000 tentativi, passa.

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

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

Dalla lettura di Wikipedia, penso che questo batterà la soluzione attuale.


In effetti, vincerà di 361 punti in entrambi i casi.
Joe Z.

In realtà, dovrò riprenderlo, non segue le specifiche. L'intelligenza artificiale dovrebbe essere apolide. In realtà dovrebbe solo stampare una cosa dato lo stato della scheda e tu l'hai fatta stampare due ( 1 1e pass).
Joe Z.

@JoeZ. Aggiustato. Non avrei compilato comunque.
yayestechlab,

Questo verrà sempre stampato 1 1, in realtà, poiché il programma viene sempre eseguito di nuovo ogni volta che cambia la scheda.
Joe Z.

1

Giava

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

Scegli il primo spazio vuoto. Vince contro qualsiasi IA al momento della pubblicazione.


2
Questo non garantisce una mossa legale. Se il primo spazio disponibile non ha libertà, non può essere giocato. Ad esempio, se questa IA si gioca da sola: dopo la prima fila di pezzi alternati, 1 1verrebbe catturata dal bianco (ora vuoto) e non potrebbe essere giocata dal nero al turno successivo.
Geobits
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.