決定論的なGo AIを構築する


11

先日考えた興味深い問題があります。それは、コードが持っているプロパティだけでなく、他のコードとゲームをプレイすることによって、他のコードと競合するコードを含んでいます。

あなたのタスクは、Goボードの現在の状態を取得し、実行または通過する動きを決定するプログラムを構築することです。

プログラムは、入力として次を受け入れます。

  • 19行。各行に19文字があり、現在Goボード上にあるピースを表します。の文字は0、空の正方形を表し、1黒で、2白です。

  • 各プレイヤーが持っている囚人の駒の数を表す2つの数字(黒、次に白)。

  • 移動する順番を表す1つの数字(黒または白)。上記のように、1黒であり、2白です。

次のいずれかを出力します。

  • a b移動する座標を表す座標のペア。1 1は左上の正方形で、最初と2番目の数字はそれぞれ下と右への移動を表します。

  • pass渡す動きを表す文字列。

たとえば、プログラムは次の入力を受け取る場合があります。

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

これは、わずかな動きしかプレイされていないゲームを表します。

次に、プログラムはを出力します6 5。これは、「上から6番目、左から5番目のポイントに黒い石を置く」ことを意味します。これは、で白い石をキャプチャし7 5ます。ボードの状態は次のように変わります。

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

(白い石が捕獲されたが、黒の囚人としてカウントされることに注意してください。)

コードはさらに次のプロパティを満たしている必要があります。

  • プログラムに同じ入力状態が与えられている場合、常に同じ出力を生成する必要があります。これがGo AIの決定論です。ランダムなコンポーネントを含めることはできません。

  • あなたのプログラムは、どのような動きをするかを決定するのに約60秒以上かかることはありません。このルールは、計算能力のばらつきにより厳密には適用されませんが、妥当な時間内に移行する必要があります。

  • プログラムのソースコードは、合計1メガバイト(1,048,576バイト)を超えてはなりません。

  • あなたのプログラムは常に合法的な動きをしなければなりません。あなたのプログラムは、石がすでに存在する場所に移動することはできず、自分の石のグループが捕獲される結果となるようなピースを配置することはできません。(このチャレンジの目的のための規則の1つの例外は、プログラムが元々そこにあった位置を作成することを許可されていることです-それはボードの現在の位置だけが与えられるため、どの動きが行われたかを保存することは期待できません前。)

その後、ボードの状態が空になり、各プログラムが順番にボードの位置を与えられて移動するGoのゲームで、あなたの提出物は他のすべての提出物に対してオールプレイオールトーナメントでプレイされます。

提出の各ペアは2ラウンドをプレイします-各プレイヤーが黒である1ラウンド。この問題のAIは完全に決定論的であるため、2つの同じAIが一緒にプレイすると、常にまったく同じゲームがプレイされます。

勝利の条件は次のとおりです。

  • プログラムがゲームの最後までプレイされる場合、Goの中国のスコアリングルールを使用して勝者が決定されます。コミは適用されません。

  • プログラムが以前の状態に到達するまで再生され、無限ループが発生する場合、2つのプログラムは結合されていると宣言されます。

あなたの提出物は、他の提出物に対して何点獲得したかによって得点されます。勝利は1ポイントの価値があり、同点は0.5ポイントの価値があります。最も多くのポイントを持つ提出物が全体的な勝者です。


これはキングオブザヒルチャレンジです。誰でもいつでも新しいエントリを投稿でき、この状況が発生すると定期的に順位が再評価されます。


7
わかりました、他のすべての提出物を待って、次にそれらを打つために私自身を書いてください-ソリューションが決定論的であるので、可能性があります。
ハワード

1
前の位置が繰り返されるようなkoでのプレイは許可されているように見えますが、すぐにドローになります(ループが発生するため)。興味深い...
ホタル

2
あなたの問題は非常に困難で、価値のある答えを出すために誰も一生懸命に働かないように見えます(本当に多くの仕事です)。これはいい問題ですが、行くのは難しいです。
ビクターStafusa 14年

1
小さいボードを使用しないのはなぜですか?9x9は初心者プレイヤーにとっては十分に一般的です。サーチスペースを劇的に削減しますが、分析によってまだ「beatられた」ほど小さくはありません(完全に解決された最大のものは5x6であると思います)。
ジオビット14年

1
入力はどのように機能しますか?stdinまたはコマンドライン引数?
Ypnypn

回答:


7

このチャレンジを実現するための私のエントリーはこちらです。Pythonコード:

print "pass"

あなたのルールによれば、常に「パス」をプレイすることは有効な(しかし悪い)戦略です。


あなたのコードは、それに対して何らかの遊びをする誰に対しても常に失われます。それでも、良いベースケースの答え。
ジョーZ.

1
@JoeZ。そして、それのルックスから彼はそれで勝ちました:P
デビッドモルダー14

4

Java:スポットを選びます

単にボード上のスポットを選択して、有効性をテストします。これはPRNGを使用しますが、シードが設定されているため、確定的です。通過したターン数に応じて、PRNGサイクルの異なるチャンクを使用します。

候補者の位置ごとに、それが有効な移動であることを確認します(ただし、スマートな移動ではありません)。そうでない場合は、次の候補に進みます。1000回試行しても有効な移動が見つからない場合、合格します。

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

いくつかの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")
  }
}

ウィキペディアを読むと、これは現在の解決策に勝ると思います。


実際、どちらの場合も361ポイント勝ちます。
ジョーZ. 14

実際、私はそれを取り戻さなければなりません、それは仕様に従っていません。AIはステートレスであることになっています。実際には、ボードの状態を考慮して1つのものだけを印刷することになっており、2つ(1 1およびpass)を印刷しました。
ジョーZ. 14

@JoeZ。それを修正しました。とにかくコンパイルしなかっただろう。
yayestechlab

ボードが変更されるたびにプログラムが常に新しく実行されるため、実際には常に印刷1 1されます。
ジョー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++;
      }
    }
  }
}

最初の空のスペースを選択します。投稿時点でAIのいずれかに対して勝ちます。


2
これは合法的な動きを保証するものではありません。最初に使用可能なスペースに自由がない場合、再生できません。たとえば、このAIが自分自身をプレイした場合:交互のピースの最初の行の後、1 1白(現在は空)でキャプチャされ、次のターンで黒でプレイできません。
ジオビット
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.