Noktalar ve Kutular için en hızlı oyuncu


16

Zorluk klasik kalem ve kağıt oyunu Noktalar ve Kutular için bir çözücü yazmaktır . Kodunuzun iki tamsayı alması mve ngiriş olarak kartın boyutunu belirlemesi gerekir.

Boş bir nokta ızgarasından başlayarak, oyuncular sırayla birleşerek bitişik olmayan iki bitişik nokta arasına tek bir yatay veya dikey çizgi ekler. 1 × 1 kutunun dördüncü tarafını tamamlayan oyuncu bir puan kazanır ve bir tur daha alır. (Noktalar genellikle kutuya, bir baş harf gibi oynatıcının tanımlayıcı bir işareti yerleştirilerek kaydedilir). Başka satır yerleştirilemediğinde oyun sona erer. Oyunun galibi en çok puanı alan oyuncudur.

resim açıklamasını buraya girin

Ya varsayabiliriz n = mya n = m - 1ve men az 2'dir.

Meydan okuma, solvebir dakikadan kısa sürede mümkün olan en büyük Nokta ve Kutular oyunudur. Bir oyunun boyutu basitçe n*m. Kodunuzun çıktısı win, drawya loseda her iki oyuncunun da en iyi şekilde oynadığı varsayılarak ilk oyuncu için sonuç olmalıdır.

Kolayca kurulabilen ve ücretsiz araçlar kullanılarak kodunuz ubuntu'da derlenebilir / çalıştırılabilir olmalıdır. Lütfen skorunuzu 1 dakika içinde bilgisayarınızda çözebileceğiniz en büyük alan olarak bildirin. Daha sonra bilgisayarımdaki kodu test edip sıralı girişler tablosu yapacağım.

Beraberlik durumunda, kazanan bir dakikadan kısa sürede çözebileceği en büyük boyutlu tahtadaki en hızlı kod olacaktır.


Kodun çıktısı sadece kazanmak ya da kaybetmekle kalmaz, aynı zamanda gerçek puanla da daha iyi olur. Bu, akıl sağlığının doğruluğunu kontrol eder.


2
Minimax kullanmak zorunda mıyız?
qwr

@qwr Aklınızda başka hangi seçeneğin olduğunu bana söyleyebilir misiniz?

Bekle, bu oyunda sadece ızgara boyutuna göre tahmin edilebilir bir kazanan var mı?
Charles

@Charles Evet eğer her iki oyuncu da en iyi şekilde oynarsa.

1
@PeterTaylor Sanırım iki puan alıyorsunuz ama sadece bir ekstra dönüş.

Yanıtlar:


15

0.084 saniyede C99 - 3x3 kartı

Düzenleme: Kodumu refactored ve sonuçları üzerinde biraz daha derin analiz yaptım.

Diğer Düzenlemeler: Simetrilerle budama eklendi. Bu 4 algoritma konfigürasyonu yapar: simetrili veya simetrik olmayan X, alfa-beta budama ile veya olmadan

En Uzak Düzenlemeler: Karma tablo kullanarak not ekleme, sonunda imkansızı gerçekleştirme: 3x3'lük bir tahta çözme!

Birincil özellikler:

  • Alfa-beta budama ile minimax'ın doğrudan uygulanması
  • çok az bellek yönetimi (geçerli hareketlerin dll'sini korur; ağaç aramada dal başına O (1) güncelleme)
  • simetrilerle budama ile ikinci dosya. Halen dal başına O (1) güncellemeleri gerçekleştirmektedir (teknik olarak O (S), burada S simetri sayısıdır. Bu kare tahtalar için 7 ve kare olmayan tahtalar için 3'tür)
  • üçüncü ve dördüncü dosyalar not ekler. Hashtable'ın boyutu ( #define HASHTABLE_BITWIDTH) üzerinde kontrolünüz var . Bu boyut duvar sayısından büyük veya ona eşit olduğunda, çarpışma ve O (1) güncellemelerini garanti etmez. Daha küçük hashtable'lar daha fazla çarpışma yaşar ve biraz daha yavaş olur.
  • -DDEBUGçıktılar için derleme

Potansiyel iyileştirmeler:

  • ilk düzenlemede sabitlenen küçük bellek sızıntısını düzelt
  • Alfa / beta budama 2. düzenlemeye eklendi
  • erik simetriler 3 düzenlemek ilave (simetri olduğunu not değil o ayrı optimizasyon kalabilmeleri için memoization tarafından ele.)
  • memoization 4 düzenlemek katma
  • şu anda hafızada her duvar için bir gösterge biti kullanılmaktadır. 3x4 kartında 31 duvar vardır, bu nedenle bu yöntem zaman kısıtlamalarından bağımsız olarak 4x4 kartlarını işleyemedi. iyileştirme, X'in en az duvar sayısı kadar büyük olduğu X-bit tamsayılarını taklit etmek olacaktır.

kod

Organizasyon eksikliğinden dolayı, dosya sayısı kontrolden çıktı. Tüm kodlar bu Github Deposuna taşındı . Not düzenlemesine, bir makefile ve test komut dosyası ekledim.

Sonuçlar

Yürütme sürelerinin günlük grafiğini çizme

Karmaşıklık Hakkında Notlar

Noktalara ve kutulara kaba kuvvet yaklaşımları karmaşıklıkta çok hızlı bir şekilde patlar .

RSatırlar ve Csütunlar içeren bir tahta düşünün . Orada R*Ckareler, R*(C+1)dikey duvarlar ve C*(R+1)yatay duvarlar. Bu toplam W = 2*R*C + R + C.

Lembik bizden oyunu minimax ile çözmemizi istediğinden, oyun ağacının yapraklarına geçmemiz gerekiyor. Şimdilik budamayı görmezden gelelim, çünkü önemli olan büyüklük emirleri.

Wİlk hamle için seçenekler var . Bunların her biri için, bir sonraki oyuncu herhangi oynayabilir W-1vb duvarların da .. Bu da bize bir arama-yer verir SS = W * (W-1) * (W-2) * ... * 1ya SS = W!. Faktörler çok büyük, ama bu sadece başlangıç. SS, arama alanındaki yaprak düğümlerinin sayısıdır . Analizimizle daha ilgili olan, alınması gereken toplam karar sayısıdır (yani ağaçtaki şube sayısı B). İlk dal tabakasının Wseçenekleri vardır. Bunların her biri için bir sonraki seviye W-1vb.

B = W + W*(W-1) + W*(W-1)*(W-2) + ... + W!

B = SUM W!/(W-k)!
  k=0..W-1

Bazı küçük masa boyutlarına bakalım:

Board Size  Walls  Leaves (SS)      Branches (B)
---------------------------------------------------
1x1         04     24               64
1x2         07     5040             13699
2x2         12     479001600        1302061344
2x3         17     355687428096000  966858672404689

Bu sayılar saçma hale geliyor. En azından kaba kuvvet kodunun neden 2x3'lük bir tahtada sonsuza kadar durduğunu açıklıyorlar. 2x3'lük bir tahtanın arama alanı 2x2'den 742560 kat daha büyüktür . 2x2'nin tamamlanması 20 saniye alırsa, muhafazakar bir ekstrapolasyon 2x3 için 100 günden fazla yürütme süresini tahmin eder . Açıkçası budamak gerekir.

Budama Analizi

Alfa-beta algoritmasını kullanarak çok basit budama ekleyerek başladım. Temel olarak, ideal bir rakibin mevcut fırsatlarına asla vermeyeceğini araştırmayı durdurur. "Hey bak - rakibim her kareyi almama izin verirse çok kazanıyorum!" Diye düşündü.

Diğer anlamları: düzenlemek I ayrıca budama dayalı simetrik panoları ekledi. Bir güne kadar not ekleme yaklaşımını kullanmıyorum, sadece bir gün not ekleme ve bu analizi ayrı tutmak istemem durumunda. Bunun yerine şu şekilde çalışır: Çoğu çizginin ızgara üzerinde başka bir yerde "simetrik bir çifti" vardır. En fazla 7 simetri vardır (yatay, dikey, 180 rotasyon, 90 rotasyon, 270 rotasyon, diyagonal ve diğer diyagonal). Tüm 7 kare tahtalar için geçerlidir, ancak son 4 kare olmayan tahtalar için geçerli değildir. Her duvar, bu simetrilerin her biri için "çifti" ne işaret eder. Eğer bir dönüşe girerken, tahta yatay olarak simetrikse, her yatay çiftten sadece birinin oynanması gerekir.

düzenlemek edit Memoization! Her duvar, bir gösterge biti olarak rahatça ayarladığım benzersiz bir kimlik alır; nth duvarın kimliği var 1 << n. O zaman, bir tahtanın karması, oynanan tüm duvarların sadece VEYA'sıdır. Bu, her dalda O (1) zamanda güncellenir. Hashtable'ın boyutu a olarak ayarlanır #define. Tüm testler 2 ^ 12 boyutunda yapıldı, çünkü neden olmasın? Hashtabı indeksleyen bitlerden daha fazla duvar olduğunda (bu durumda 12 bit), en az anlamlı olan 12 maskelenir ve endeks olarak kullanılır. Çarpışmalar, her hashtable dizininde bağlantılı bir listeyle işlenir. Aşağıdaki tablo, karma boyutun performansı nasıl etkilediğine dair hızlı ve kirli analizimdir. Sonsuz RAM'li bir bilgisayarda, masanın boyutunu her zaman duvar sayısına ayarlayacağız. 3x4'lük bir tahta 2 ~ 31 uzunluğunda bir hashtable'a sahip olacaktır. Ne yazık ki bu lüksümüz yok.

Hashtable Boyutunun Etkileri

Tamam, budamaya geri dön .. Ağacın üstündeki aramayı durdurarak, yapraklara inmeden çok zaman kazanabiliriz . 'Budama Faktörü', ziyaret etmek zorunda olduğumuz tüm şubelerin kesiridir. Kaba kuvvet 1 budama faktörüne sahiptir. Ne kadar küçükse o kadar iyidir.

Alınan dalların log grafiği

Budama faktörlerinin log grafiği


23'ler C gibi hızlı bir dil için dikkat çekici derecede yavaş görünüyor.
qwr

Alfa beta'dan az miktarda budama ile kaba kuvvet. O 23s şüpheli olduğunu kabul ediyorum, ama ben, başka deyişle .. tutarsız olacağını benim kodda onun bir gizemi bir neden görmüyorum
wrongu

1
girdi, soru tarafından belirtildiği gibi biçimlendirilir. rows columnstahta boyutunu belirten iki boşlukla ayrılmış tamsayı
wrongu

1
@Lembik Yapacak bir şey kaldığını sanmıyorum. Bu çılgın projeyi bitirdim!
Haziran'da wrongu

1
Bence cevabınız özel bir yeri hak ediyor. Ben baktı ve 3 x 3 daha önce çözülmüş en büyük sorun boyutu ve kodunuz neredeyse anında. 3 ile 4 veya 4 ile 4'ü çözebilirseniz, sonucu wiki sayfasına ekleyebilir ve ünlü olabilirsiniz :)

4

Python - 29 saniyede 2x2

Bulmacalar çapraz gönderme . Özellikle optimize edilmemiştir, ancak diğer katılımcılar için yararlı bir başlangıç ​​noktası olabilir.

from collections import defaultdict

VERTICAL, HORIZONTAL = 0, 1

#represents a single line segment that can be drawn on the board.
class Line(object):
    def __init__(self, x, y, orientation):
        self.x = x
        self.y = y
        self.orientation = orientation
    def __hash__(self):
        return hash((self.x, self.y, self.orientation))
    def __eq__(self, other):
        if not isinstance(other, Line): return False
        return self.x == other.x and self.y == other.y and self.orientation == other.orientation
    def __repr__(self):
        return "Line({}, {}, {})".format(self.x, self.y, "HORIZONTAL" if self.orientation == HORIZONTAL else "VERTICAL")

class State(object):
    def __init__(self, width, height):
        self.width = width
        self.height = height
        self.whose_turn = 0
        self.scores = {0:0, 1:0}
        self.lines = set()
    def copy(self):
        ret = State(self.width, self.height)
        ret.whose_turn = self.whose_turn
        ret.scores = self.scores.copy()
        ret.lines = self.lines.copy()
        return ret
    #iterate through all lines that can be placed on a blank board.
    def iter_all_lines(self):
        #horizontal lines
        for x in range(self.width):
            for y in range(self.height+1):
                yield Line(x, y, HORIZONTAL)
        #vertical lines
        for x in range(self.width+1):
            for y in range(self.height):
                yield Line(x, y, VERTICAL)
    #iterate through all lines that can be placed on this board, 
    #that haven't already been placed.
    def iter_available_lines(self):
        for line in self.iter_all_lines():
            if line not in self.lines:
                yield line

    #returns the number of points that would be earned by a player placing the line.
    def value(self, line):
        assert line not in self.lines
        all_placed = lambda seq: all(l in self.lines for l in seq)
        if line.orientation == HORIZONTAL:
            #lines composing the box above the line
            lines_above = [
                Line(line.x,   line.y+1, HORIZONTAL), #top
                Line(line.x,   line.y,   VERTICAL),   #left
                Line(line.x+1, line.y,   VERTICAL),   #right
            ]
            #lines composing the box below the line
            lines_below = [
                Line(line.x,   line.y-1, HORIZONTAL), #bottom
                Line(line.x,   line.y-1, VERTICAL),   #left
                Line(line.x+1, line.y-1, VERTICAL),   #right
            ]
            return all_placed(lines_above) + all_placed(lines_below)
        else:
            #lines composing the box to the left of the line
            lines_left = [
                Line(line.x-1, line.y+1, HORIZONTAL), #top
                Line(line.x-1, line.y,   HORIZONTAL), #bottom
                Line(line.x-1, line.y,   VERTICAL),   #left
            ]
            #lines composing the box to the right of the line
            lines_right = [
                Line(line.x,   line.y+1, HORIZONTAL), #top
                Line(line.x,   line.y,   HORIZONTAL), #bottom
                Line(line.x+1, line.y,   VERTICAL),   #right
            ]
            return all_placed(lines_left) + all_placed(lines_right)

    def is_game_over(self):
        #the game is over when no more moves can be made.
        return len(list(self.iter_available_lines())) == 0

    #iterates through all possible moves the current player could make.
    #Because scoring a point lets a player go again, a move can consist of a collection of multiple lines.
    def possible_moves(self):
        for line in self.iter_available_lines():
            if self.value(line) > 0:
                #this line would give us an extra turn.
                #so we create a hypothetical future state with this line already placed, and see what other moves can be made.
                future = self.copy()
                future.lines.add(line)
                if future.is_game_over(): 
                    yield [line]
                else:
                    for future_move in future.possible_moves():
                        yield [line] + future_move
            else:
                yield [line]

    def make_move(self, move):
        for line in move:
            self.scores[self.whose_turn] += self.value(line)
            self.lines.add(line)
        self.whose_turn = 1 - self.whose_turn

    def tuple(self):
        return (tuple(self.lines), tuple(self.scores.items()), self.whose_turn)
    def __hash__(self):
        return hash(self.tuple())
    def __eq__(self, other):
        if not isinstance(other, State): return False
        return self.tuple() == other.tuple()

#function decorator which memorizes previously calculated values.
def memoized(fn):
    answers = {}
    def mem_fn(*args):
        if args not in answers:
            answers[args] = fn(*args)
        return answers[args]
    return mem_fn

#finds the best possible move for the current player.
#returns a (move, value) tuple.
@memoized
def get_best_move(state):
    cur_player = state.whose_turn
    next_player = 1 - state.whose_turn
    if state.is_game_over():
        return (None, state.scores[cur_player] - state.scores[next_player])
    best_move = None
    best_score = float("inf")
    #choose the move that gives our opponent the lowest score
    for move in state.possible_moves():
        future = state.copy()
        future.make_move(move)
        _, score = get_best_move(future)
        if score < best_score:
            best_move = move
            best_score = score
    return [best_move, -best_score]

n = 2
m = 2
s = State(n,m)
best_move, relative_value = get_best_move(s)
if relative_value > 0:
    print("win")
elif relative_value == 0:
    print("draw")
else:
    print("lose")

Pypy kullanılarak 18 saniyeye kadar hızlandırılabilir.

2

Javascript - 20ms'de 1x2 kartı

Çevrimiçi demo burada (uyarı - tam arama derinliğinde 1x2'den büyükse çok yavaş ): https://dl.dropboxusercontent.com/u/141246873/minimax/index.html

Hız için değil, orijinal kazanma kriterleri (kod golf) için geliştirilmiştir.

Windows 7'de Google Chrome V35'te test edildi.

//first row is a horizontal edges and second is vertical
var gameEdges = [
    [false, false],
    [false, false, false],
    [false, false]
]

//track all possible moves and score outcome
var moves = []

function minimax(edges, isPlayersTurn, prevScore, depth) {

    if (depth <= 0) {
        return [prevScore, 0, 0];
    }
    else {

        var pointValue = 1;
        if (!isPlayersTurn)
            pointValue = -1;

        var moves = [];

        //get all possible moves and scores
        for (var i in edges) {
            for (var j in edges[i]) {
                //if edge is available then its a possible move
                if (!edges[i][j]) {

                    //if it would result in game over, add it to the scores array, otherwise, try the next move
                    //clone the array
                    var newEdges = [];
                    for (var k in edges)
                        newEdges.push(edges[k].slice(0));
                    //update state
                    newEdges[i][j] = true;
                    //if closing this edge would result in a complete square, get another move and get a point
                    //square could be formed above, below, right or left and could get two squares at the same time

                    var currentScore = prevScore;
                    //vertical edge
                    if (i % 2 !== 0) {//i === 1
                        if (newEdges[i] && newEdges[i][j - 1] && newEdges[i - 1] && newEdges[i - 1][j - 1] && newEdges[parseInt(i) + 1] && newEdges[parseInt(i) + 1][j - 1])
                            currentScore += pointValue;
                        if (newEdges[i] && newEdges[i][parseInt(j) + 1] && newEdges[i - 1] && newEdges[i - 1][j] && newEdges[parseInt(i) + 1] && newEdges[parseInt(i) + 1][j])
                            currentScore += pointValue;
                    } else {//horizontal
                        if (newEdges[i - 2] && newEdges[i - 2][j] && newEdges[i - 1][j] && newEdges[i - 1][parseInt(j) + 1])
                            currentScore += pointValue;
                        if (newEdges[parseInt(i) + 2] && newEdges[parseInt(i) + 2][j] && newEdges[parseInt(i) + 1][j] && newEdges[parseInt(i) + 1][parseInt(j) + 1])
                            currentScore += pointValue;
                    }

                    //leaf case - if all edges are taken then there are no more moves to evaluate
                    if (newEdges.every(function (arr) { return arr.every(Boolean) })) {
                        moves.push([currentScore, i, j]);
                        console.log("reached end case with possible score of " + currentScore);
                    }
                    else {
                        if ((isPlayersTurn && currentScore > prevScore) || (!isPlayersTurn && currentScore < prevScore)) {
                            //gained a point so get another turn
                            var newMove = minimax(newEdges, isPlayersTurn, currentScore, depth - 1);

                            moves.push([newMove[0], i, j]);
                        } else {
                            //didnt gain a point - opponents turn
                            var newMove = minimax(newEdges, !isPlayersTurn, currentScore, depth - 1);

                            moves.push([newMove[0], i, j]);
                        }
                    }



                }


            }

        }//end for each move

        var bestMove = moves[0];
        if (isPlayersTurn) {
            for (var i in moves) {
                if (moves[i][0] > bestMove[0])
                    bestMove = moves[i];
            }
        }
        else {
            for (var i in moves) {
                if (moves[i][0] < bestMove[0])
                    bestMove = moves[i];
            }
        }
        return bestMove;
    }
}

var player1Turn = true;
var squares = [[0,0],[0,0]]//change to "A" or "B" if square won by any of the players
var lastMove = null;

function output(text) {
    document.getElementById("content").innerHTML += text;
}

function clear() {
    document.getElementById("content").innerHTML = "";
}

function render() {
    var width = 3;
    if (document.getElementById('txtWidth').value)
        width = parseInt(document.getElementById('txtWidth').value);
    if (width < 2)
        width = 2;

    clear();
    //need to highlight the last move taken and show who has won each square
    for (var i in gameEdges) {
        for (var j in gameEdges[i]) {
            if (i % 2 === 0) {
                if(j === "0")
                    output("*");
                if (gameEdges[i][j] && lastMove[1] == i && lastMove[2] == j)
                    output(" <b>-</b> ");
                else if (gameEdges[i][j])
                    output(" - ");
                else
                    output("&nbsp;&nbsp;&nbsp;");
                output("*");
            }
            else {
                if (gameEdges[i][j] && lastMove[1] == i && lastMove[2] == j)
                    output("<b>|</b>");
                else if (gameEdges[i][j])
                    output("|");
                else
                    output("&nbsp;");

                if (j <= width - 2) {
                    if (squares[Math.floor(i / 2)][j] === 0)
                        output("&nbsp;&nbsp;&nbsp;&nbsp;");
                    else
                        output("&nbsp;" + squares[Math.floor(i / 2)][j] + "&nbsp;");
                }
            }
        }
        output("<br />");

    }
}

function nextMove(playFullGame) {
    var startTime = new Date().getTime();
    if (!gameEdges.every(function (arr) { return arr.every(Boolean) })) {

        var depth = 100;
        if (document.getElementById('txtDepth').value)
            depth = parseInt(document.getElementById('txtDepth').value);

        if (depth < 1)
            depth = 1;

        var move = minimax(gameEdges, true, 0, depth);
        gameEdges[move[1]][move[2]] = true;
        lastMove = move;

        //if a square was taken, need to update squares and whose turn it is

        var i = move[1];
        var j = move[2];
        var wonSquare = false;
        if (i % 2 !== 0) {//i === 1
            if (gameEdges[i] && gameEdges[i][j - 1] && gameEdges[i - 1] && gameEdges[i - 1][j - 1] && gameEdges[parseInt(i) + 1] && gameEdges[parseInt(i) + 1][j - 1]) {
                squares[Math.floor(i / 2)][j - 1] = player1Turn ? "A" : "B";
                wonSquare = true;
            }
            if (gameEdges[i] && gameEdges[i][parseInt(j) + 1] && gameEdges[i - 1] && gameEdges[i - 1][j] && gameEdges[parseInt(i) + 1] && gameEdges[parseInt(i) + 1][j]) {
                squares[Math.floor(i / 2)][j] = player1Turn ? "A" : "B";
                wonSquare = true;
            }
        } else {//horizontal
            if (gameEdges[i - 2] && gameEdges[i - 2][j] && gameEdges[i - 1] && gameEdges[i - 1][j] && gameEdges[i - 1] && gameEdges[i - 1][parseInt(j) + 1]) {
                squares[Math.floor((i - 1) / 2)][j] = player1Turn ? "A" : "B";
                wonSquare = true;
            }
            if (gameEdges[i + 2] && gameEdges[parseInt(i) + 2][j] && gameEdges[parseInt(i) + 1] && gameEdges[parseInt(i) + 1][j] && gameEdges[parseInt(i) + 1] && gameEdges[parseInt(i) + 1][parseInt(j) + 1]) {
                squares[Math.floor(i / 2)][j] = player1Turn ? "A" : "B";
                wonSquare = true;
            }
        }

        //didnt win a square so its the next players turn
        if (!wonSquare)
            player1Turn = !player1Turn;

        render();

        if (playFullGame) {
            nextMove(playFullGame);
        }
    }

    var endTime = new Date().getTime();
    var executionTime = endTime - startTime;
    document.getElementById("executionTime").innerHTML = 'Execution time: ' + executionTime;
}

function initGame() {

    var width = 3;
    var height = 2;

    if (document.getElementById('txtWidth').value)
        width = document.getElementById('txtWidth').value;
    if (document.getElementById('txtHeight').value)
        height = document.getElementById('txtHeight').value;

    if (width < 2)
        width = 2;
    if (height < 2)
        height = 2;

    var depth = 100;
    if (document.getElementById('txtDepth').value)
        depth = parseInt(document.getElementById('txtDepth').value);

    if (depth < 1)
        depth = 1;

    if (width > 2 && height > 2 && !document.getElementById('txtDepth').value)
        alert("Warning. Your system may become unresponsive. A smaller grid or search depth is highly recommended.");

    gameEdges = [];
    for (var i = 0; i < height; i++) {
        if (i == 0) {
            gameEdges.push([]);
            for (var j = 0; j < (width - 1) ; j++) {
                gameEdges[i].push(false);
            }
        }
        else {
            gameEdges.push([]);
            for (var j = 0; j < width; j++) {
                gameEdges[(i * 2) - 1].push(false);
            }
            gameEdges.push([]);
            for (var j = 0; j < (width - 1) ; j++) {
                gameEdges[i*2].push(false);
            }
        }
    }

    player1Turn = true;

    squares = [];
    for (var i = 0; i < (height - 1) ; i++) {
        squares.push([]);
        for (var j = 0; j < (width - 1); j++) {
            squares[i].push(0);
        }
    }

    lastMove = null;

    render();
}

document.addEventListener('DOMContentLoaded', initGame, false);

Demo gerçekten harika! Arama derinliğini artırdıkça kazanan ileri geri değiştiğinden 3 x 3 gerçekten ilginçtir. Kontrol edebilir miyim, minimax'ınız bir tur boyunca yarıda durur mu? Demek istediğim, birisi kare alırsa, her zaman sıranın sonuna kadar uzanır mı?

2x2, 3'e 3'tür. Kodunuzun tam olarak 20ms'de çözeceğinden emin misiniz?

"Birisi bir kare alırsa, her zaman sıranın sonuna kadar uzanır mı?" - Eğer oyuncu bir kareyi alırsa, yine de bir sonraki dönüşe geçer, ancak bir sonraki dönüş aynı oyuncu içindir, yani bir kareyi tamamlamak için ekstra bir dönüş alırlar. "2x2, 3 nokta 3'tür" - Hata! Bu durumda puanım 1x1.
rdans
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.