KoTH: Gomoku (Arka arkaya beş tane)


10

Gomoku veya Arka arkaya Beş, siyah ve beyaz taşlarla ızgarada iki oyuncu tarafından oynanan bir masa oyunudur . Kim arka arkaya 5 taşı koyabilirse (yatay, dikey veya çapraz) oyunu kazanır.15x155

kurallar

Bu KoTH'de Swap2 kuralını oynayacağız, yani bir oyun iki aşamadan oluşuyor: İlk aşamada iki oyuncu kimin ilk gittiğini / kimin siyah oynadığını belirler, daha sonra oyuncu ile başlayan her turda bir taş yerleştirecekler siyahı seçti.

İlk aşama

Oyuncular A & B olsun ve A oyunu açacaktır:

  • Bir gemide siyah iki ve bir beyaz taş yerleştirir
  • B aşağıdaki üç hareketten birini seçebilir:
    • B oyuncusu siyah oynamaya karar verir: başlangıç ​​aşaması bitti
    • Oyuncu B beyaz bir taş koymaya karar verir ve beyaz oynar: ilk aşama bitti
    • Oyuncu B bir siyah ve bir beyaz taş oynamaya karar verir: A rengi seçer

Oyun aşaması

Siyah oynar çalar, bu orada oyun için artık serbest alanlardır (bu durumda bu bir kravat) ya da bir oyuncu oynamak için yönetir kadar gider ile başlayan gemide onların renk, her oyuncu yerleri tek taş a taşları (bu durumda oyuncu kazanır).5

Bir sıra yatay, dikey veya diyagonal anlamına gelir. Kazanmak bir kazanımdır - oyuncunun birden fazla sıra atmayı başarabilmesi önemli değildir.

KoTH oyun kuralları

  • her oyuncu birbirlerine karşı iki kez oynar:
    • başlangıçta kimin önce gittiğine rastgele karar verilecek
    • sonraki oyunda en son oynayan oyuncu ilk önce
  • bir kazanma 2 puan, bir kravat 1 ve bir kayıp 0
  • amaç mümkün olduğunca çok puan almak

Botunuz

Bu zorluğun olabildiğince çok dil için erişilebilir olmasını sağlamak için giriş / çıkış stdin / stdout (satır tabanlı) aracılığıyla olacaktır . Hakim programı, botunuzun stdin'ine bir satır yazdırarak programınızı yönlendirir ve botunuz stdout'a bir satır yazdırır .

Bir EXITmesaj aldıktan sonra , hakimin işlemi öldürmesi için dosyalara yazmayı bitirmeniz için yarım saniye verilecektir.

Rastgelelik

Turnuvaları doğrulanabilir kılmak için yargıç tohumlanmış randomizasyon kullanır ve botunuz da aynı nedenle yapmalıdır. Bot, kullanması gereken komut satırı argümanı ile bir tohum verilecek, lütfen bir sonraki bölüme bakın.

Argümanlar

Bot, iki komut satırı argümanı alır:

  1. rakibin adı
  2. rastgelelik için tohum

Kullanıcı durumu

Programınız her oyun için her zaman yeni başlatılacağından, saklamak istediğiniz bilgileri saklamak için dosyaları kullanmanız gerekir. Geçerli dizininizde herhangi bir dosyayı okuyabilir / yazabilir veya alt klasörler oluşturabilir / kaldırabilirsiniz. Herhangi bir üst dizindeki herhangi bir dosyaya erişim izniniz yok!

Giriş / Çıkış formatı

BOARD((X,Y),COLOR)XY[0,15)COLOR"B""W"

SPXY(X,Y)[0,15)|

İlk aşamada üç farklı mesaj türü vardır:

Prompt (judge) -> Answer (bot)
"A" SP "[]"  -> XY XY XY
"B" SP BOARD -> "B" | "W" SP XY | XY XY
"C" SP BOARD -> "B" | "W"
  • İlk mesaj üç tuple ister, ilk ikisi siyah taşların pozisyonları, üçüncüsü ise beyaz taşların pozisyonları olacaktır.
  • İkinci mesaj şunlardan birini ister:
    • "B" -> siyah seç
    • "W" SP XY -> beyazı seç ve üzerine beyaz bir taş koy XY
    • XY XY -> iki taş yerleştirin (ilk siyah ve ikinci beyaz)
  • Sonuncusu sadece hangi rengi oynamak istediğinizi sorar

Bundan sonra normal oyun başlayacak ve mesajlar çok daha basit olacak

N BOARD -> XY

N0XY


Bir cevap beklemeyen bir ek mesaj var

"EXIT" SP NAME | "EXIT TIE"

NAMEkazanılan botun adı nerede . Oyun kazanan kimse ve taş yerleştirmek için daha fazla boş alan olmadığı için oyun sona ererse, ikinci mesaj gönderilecektir (bu, botunuzun adlandırılamayacağı anlamına gelir TIE).

Biçimlendirme

Bottan gelen mesajların herhangi bir boşluk olmadan kodu çözülebileceğinden, tüm boşluklar yok sayılır (örn. (0 , 0) (0,12)İle aynı şekilde işlenir (0,0)(0,12)). Hâkimden gelen mesajlar sadece farklı bölümleri ayırmak için bir boşluk içerir (yani yukarıda belirtildiği gibi SP) ve boşluklar üzerinde çizgiyi bölmenize izin verir.

Herhangi bir geçersiz yanıt, o turun kaybedilmesine neden olur (yine de bir EXITmesaj alırsınız), kurallara bakın.

Misal

İşte gerçek mesajlara bazı örnekler:

A []
B [((0,0),"B"),((0,1),"W"),((14,14),"B")]
1 [((0,0),"B"),((0,1),"W"),((1,0),"B"),((1,1),"W"),((14,14),"B")]

Hakim

Sen yargıç programını bulabilirsiniz burada : basitçe yeni bir klasör oluşturmak için bir bot eklemek için bots, klasörün orada dosyalarınızı yerleştirmek ve bir dosya eklemek metaiçeren ad , komutu , argümanlar ve bir bayrak 0/1 (etkin / devre dışı stderr'yi ) her ayrı bir satırda.

Bir turnuva yapmak için koşun ./gomokuve tek bir bot koşusunda hata ayıklayın ./gomoku -d BOT.

Not: Hakimi nasıl kuracağınız ve kullanacağınız hakkında daha fazla bilgiyi Github deposunda bulabilirsiniz. Üç örnek bot da vardır ( Haskell , Python ve JavaScript ).

kurallar

  • her bot değişikliğinde * turnuva tekrar oynanacak ve en çok puana sahip oyuncu kazanacak (tie-breaker ilk başvuru)
  • ortak bir strateji oynamadıkları sürece birden fazla bot gönderebilirsiniz
  • dizininizin dışındaki dosyalara dokunmanıza izin verilmez (örn. diğer oynatıcının dosyalarını değiştirme)
  • botunuz çökerse veya geçersiz bir yanıt gönderirse, mevcut oyun sona erer ve o raundu kaybedersiniz
  • Hâkim (şu anda) tur başına bir zaman sınırı uygulamazken, tüm başvuruları test etmek mümkün olmadığından harcanan zamanı düşük tutmanız tavsiye edilir **
  • Yargıç programındaki hataları kötüye kullanmak boşluk deliği sayılır

* Botunuzu doğrudan botsdizinde ayrı olarak göndermek (ve potansiyel olarak değiştirmek util.sh) için Github'ı kullanmanız önerilir !

** Bu size bildirilecek bir sorun haline gelirse, 500 ms'nin altında bir şey söyleyebilirim (bu çok!) Şimdilik iyi olmalı.

Sohbet

Sorularınız varsa veya bu Koth hakkında konuşmak istiyorsanız, katılmak için çekinmeyin Sohbet !



Boşluklara sahip olmak, örneklerinizde bir meta boşluk karakterini aklıma getiriyor. Bazı örnekler daha iyi olurdu.
Veskah

@Veskah: Bağlantılı üç örnek bot var, mesajlar için birkaç örnek ekleyeceğim.
ბიმო

@Veskah: Bazı örnekler eklendi. Btw. hangi formatta olduklarını görmek ve geçerli bir yanıtın ne olduğunu test etmek için örnek bir botta hata ayıklamayı da deneyebilirsiniz.
ბიმო

Push izinleri vermedin, bu yüzden
Kaito Kid

Yanıtlar:


3

KaitoBot

MiniMax ilkelerinin çok kaba bir uygulamasını kullanır. Aramanın derinliği de çok düşüktür, çünkü aksi halde çok uzun sürer.

Daha sonra geliştirmek için düzenlenebilir.

Ayrıca mümkünse Siyah oynamaya çalışır, çünkü Wikipedia Siyah'ın bir avantajı olduğunu söylüyor.

Gomoku'yu hiç oynamadım, bu yüzden daha iyi bir fikir olmaması için ilk üç taşı rastgele kurdum.

const readline = require('readline');
const readLine = readline.createInterface({ input: process.stdin });

var debug = true;
var myColor = '';
var opponentColor = '';
var board = [];
var seed = parseInt(process.argv[3]);

function random(min, max) {
    changeSeed();
    var x = Math.sin(seed) * 10000;
    var decimal = x - Math.floor(x);
    var chosen = Math.floor(min + (decimal * (max - min)));
    return chosen;
}

function changeSeed() {
    var x = Math.sin(seed++) * 10000;
    var decimal = x - Math.floor(x);
    seed = Math.floor(100 + (decimal * 9000000));
}

function KaitoBot(ln) {
    var ws = ln.split(' ');

    if (ws[0] === 'A') {
        // Let's play randomly, we don't care.
        var nums = [];
        nums[0] = [ random(0, 15), random(0, 15) ];
        nums[1] = [ random(0, 15), random(0, 15) ];
        nums[2] = [ random(0, 15), random(0, 15) ];
        while (nums[1][0] == nums[0][0] && nums[1][1] == nums[0][1])
        {
            nums[1] = [ random(0, 15), random(0, 15) ];
        }
        while ((nums[2][0] == nums[0][0] && nums[2][1] == nums[0][1]) || (nums[2][0] == nums[1][0] && nums[2][1] == nums[1][1]))
        {
            nums[2] = [ random(0, 15), random(0, 15) ];
        }
        console.log('(' + nums[0][0] + ',' + nums[0][1] + ') (' + nums[1][0] + ',' + nums[1][1] + ') (' + nums[2][0] + ',' + nums[2][1] + ')');
    }
    else if (ws[0] === 'B') {
        // we're second to play, let's just pick black
        myColor = 'B';
        opponentColor = 'W';
        console.log('B');
    }
    else if (ws[0] === 'C') {
        // the other player chose to play 2 stones more, we need to pick..
        // I would prefer playing Black
        myColor = 'B';
        opponentColor = 'W';
        console.log('B');
    }
    else if (ws[0] === 'EXIT') {
        process.exit();
    }
    else {
        board = [];
        var json = JSON.parse(ws[1].replace(/\(\(/g,'{"xy":[')
                .replace(/"\)/g,'"}')
                .replace(/\),/g,'],"colour":'));
        // loop over all XYs and make a board object I can use
        for (var x = 0; x < 15; x++) {
            var newRow = []
            for (var y = 0; y < 15; y++) {
                var contains = false;
                json.forEach(j => {
                    if (j.xy[0] == x && j.xy[1] == y) {
                        contains = true;
                        newRow[newRow.length] = j.colour;
                    }
                });
                if (!contains) {
                    newRow[newRow.length] = ' ';
                }
            }
            board[board.length] = newRow;
        }
        // If we never picked Black, I assume we're White
        if (myColor == '') {
            myColor = 'W';
            opponentColor = 'B';
        }
        var bestMoves = ChooseMove(board, myColor, opponentColor);
        var chosenMove = bestMoves[random(0, bestMoves.length)];
        console.log('(' + chosenMove.X + ',' + chosenMove.Y + ')');
    }
}

function IsSquareRelevant(board, x, y) {
    return (board[x][y] == ' ' && 
        ((x > 0 && board[x - 1][y] != ' ') 
        || (x < 14 && board[x + 1][y] != ' ') 
        || (y > 0 && board[x][y - 1] != ' ') 
        || (y < 14 && board[x][y + 1] != ' ')
        || (x > 0 && y > 0 && board[x - 1][y - 1] != ' ') 
        || (x < 14 && y < 14 && board[x + 1][y + 1] != ' ') 
        || (y > 0 && x < 14 && board[x + 1][y - 1] != ' ') 
        || (y < 14 && x > 0 && board[x - 1][y + 1] != ' ')));
}

function ChooseMove(board, colorMe, colorOpponent) {
    var possibleMoves = [];
    for (var x = 0; x < 15; x++) {
        for (var y = 0; y < 15; y++) {
            if (IsSquareRelevant(board, x, y)) {
                possibleMoves[possibleMoves.length] = {X:x, Y:y};
            }
        }
    }
    var bestValue = -9999;
    var bestMoves = [possibleMoves[0]];
    for (var k in possibleMoves) {
        var changedBoard = JSON.parse(JSON.stringify(board));
        changedBoard[possibleMoves[k].X][possibleMoves[k].Y] = colorMe;
        var value = analyseBoard(changedBoard, colorMe, colorOpponent, colorOpponent, 2);
        if (value > bestValue) {
            bestValue = value;
            bestMoves = [possibleMoves[k]];
        } else if (value == bestValue) {
            bestMoves[bestMoves.length] = possibleMoves[k];
        }
    }
    return bestMoves;
}

function analyseBoard(board, color, opponent, nextToPlay, depth) {
    var tBoard = board[0].map((x,i) => board.map(x => x[i]));
    var score = 0.0;
    for (var x = 0; x < board.length; x++) {
        var inARow = 0;
        var tInARow = 0;
        var opponentInARow = 0;
        var tOpponentInARow = 0;
        var inADiago1 = 0;
        var opponentInADiago1 = 0;
        var inADiago2 = 0;
        var opponentInADiago2 = 0;

        for (var y = 0; y < board.length; y++) {
            if (board[x][y] == color) {
                inARow++;
                score += Math.pow(2, inARow);
            } else {
                inARow = 0;
            }
            if (board[x][y] == opponent) {
                opponentInARow++;
                score -= Math.pow(2, opponentInARow);
            } else {
                opponentInARow = 0;
            }
            if (tBoard[x][y] == color) {
                tInARow++;
                score += Math.pow(2, tInARow);
            } else {
                tInARow = 0;
            }
            if (tBoard[x][y] == opponent) {
                tOpponentInARow++;
                score -= Math.pow(2, tOpponentInARow);
            } else {
                tOpponentInARow = 0;
            }

            var xy = (y + x) % 15;
            var xy2 = (x - y + 15) % 15;
            if (xy == 0) {
                inADiago1 = 0;
                opponentInADiago1 = 0;
            }
            if (xy2 == 0) {
                inADiago2 = 0;
                opponentInADiago2 = 0;
            }

            if (board[xy][y] == color) {
                inADiago1++;
                score += Math.pow(2, inADiago1);
            } else {
                inADiago1 = 0;
            }
            if (board[xy][y] == opponent) {
                opponentInADiago1++;
                score -= Math.pow(2, opponentInADiago1);
            } else {
                opponentInADiago1 = 0;
            }
            if (board[xy2][y] == color) {
                inADiago2++;
                score += Math.pow(2, inADiago2);
            } else {
                inADiago2 = 0;
            }
            if (board[xy2][y] == opponent) {
                opponentInADiago2++;
                score -= Math.pow(2, opponentInADiago2);
            } else {
                opponentInADiago2 = 0;
            }


            if (inARow == 5 || tInARow == 5) {
                return 999999999.0;
            } else if (opponentInARow == 5 || tOpponentInARow == 5) {
                return -99999999.0;
            }
            if (inADiago1 == 5 || inADiago2 == 5) {
                return 999999999.0;
            } else if (opponentInADiago1 == 5 || opponentInADiago2 == 5) {
                return -99999999.0;
            }
        }
    }

    if (depth > 0) {
        var bestMoveValue = 999999999;
        var nextNextToPlay = color;
        if (nextToPlay == color) {
            nextNextToPlay = opponent;
            bestMoveValue = -999999999;
        }
        for (var x = 0; x < board.length; x++) {
            for (var y = 0; y < board.length; y++) {
                if (IsSquareRelevant(board, x, y)) {
                    var changedBoard = JSON.parse(JSON.stringify(board));
                    changedBoard[x][y] = nextToPlay;
                    var NextMoveValue = (analyseBoard(changedBoard, color, opponent, nextNextToPlay, depth - 1) * 0.1);

                    if (nextToPlay == color) {
                        if (NextMoveValue > bestMoveValue) {
                            bestMoveValue = NextMoveValue;
                        }
                    } else {
                        if (NextMoveValue < bestMoveValue) {
                            bestMoveValue = NextMoveValue;
                        }
                    }
                }
            }
        }
        score += bestMoveValue * 0.1;
    }
    return score;
}

readLine.on('line', (ln) => {

    KaitoBot(ln);

});

DÜZENLEMELER: Tohum dinamik olarak değiştirildi çünkü aksi takdirde tohumlar 2 ^ 52 javascript aşıldığında artım doğru şekilde işlenemedi

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.