Connect-4 yang Dapat Diobservasi Sebagian

8

Permainan

Anda akan memainkan (hampir) permainan standar Connect-4 . Sayangnya, ini adalah permainan korespondensi dan seseorang telah menempatkan pita hitam di setiap baris kedua mulai dari bawah, sehingga Anda tidak dapat melihat gerakan lawan Anda di dalam baris ini.

Setiap gerakan dalam kolom yang sudah penuh akan dihitung sebagai melewati giliran Anda, dan jika permainan berjalan lebih lama dari 6 * 7putaran itu akan diputuskan sebagai undian.

Spesifikasi Tantangan

Program Anda harus diimplementasikan sebagai fungsi Python 3. Argumen pertama adalah 'tampilan' papan, yang mewakili status papan yang dikenal sebagai daftar 2D baris dari bawah ke atas di mana 1adalah gerakan oleh pemain pertama, 2gerakan oleh pemain kedua, dan 0posisi kosong atau tersembunyi bergerak oleh lawanmu.

Argumen kedua adalah nomor giliran yang diindeks 0, dan paritasnya memberi tahu Anda pemain mana Anda.

Argumen terakhir adalah status arbitrer, yang diinisialisasi Nonepada awal setiap pertandingan, yang dapat Anda gunakan untuk mempertahankan status di antara belokan.

Anda harus mengembalikan 2-tupel dari indeks kolom yang ingin Anda mainkan, dan status baru untuk dikembalikan kepada Anda giliran berikutnya.

Mencetak gol

Kemenangan dihitung sebagai +1, imbang sebagai 0, dan kalah sebagai -1. Tujuan Anda adalah untuk mencapai skor rata-rata tertinggi dalam turnamen round-robin. Saya akan mencoba menjalankan pertandingan sebanyak yang diperlukan untuk mengidentifikasi pemenang yang jelas.

Aturan

Pesaing mana pun harus memiliki paling banyak satu bot yang bersaing pada satu waktu, tetapi tidak masalah untuk memperbarui entri Anda jika Anda melakukan peningkatan. Silakan coba batasi bot Anda hingga ~ 1 detik dari waktu berfikir per giliran.

Pengujian

Berikut adalah kode sumber untuk pengontrol, bersama dengan beberapa bot contoh yang tidak bersaing untuk referensi:

import itertools
import random

def get_strides(board, i, j):
    yield ((i, k) for k in range(j + 1, 7))
    yield ((i, k) for k in range(j - 1, -1, -1))
    yield ((k, j) for k in range(i + 1, 6))
    yield ((k, j) for k in range(i - 1, -1, -1))
    directions = [(1, 1), (-1, -1), (1, -1), (-1, 1)]
    def diag(di, dj):
        i1 = i
        j1 = j
        while True:
            i1 += di
            if i1 < 0 or i1 >= 6:
                break
            j1 += dj
            if j1 < 0 or j1 >= 7:
                break
            yield (i1, j1)
    for d in directions:
        yield diag(*d)

DRAWN = 0
LOST = 1
WON = 2
UNDECIDED = 3

def get_outcome(board, i, j):
    if all(board[-1]):
        return DRAWN
    player = board[i][j]
    strides = get_strides(board, i, j)
    for _ in range(4):
        s0 = next(strides)
        s1 = next(strides)
        n = 1
        for s in (s0, s1):
            for i1, j1 in s:
                if board[i1][j1] == player:
                    n += 1
                    if n >= 4:
                        return WON
                else:
                    break
    return UNDECIDED

def apply_move(board, player, move):
    for i, row in enumerate(board):
        if board[i][move] == 0:
            board[i][move] = player
            outcome = get_outcome(board, i, move)
            return outcome
    if all(board[-1]):
        return DRAWN
    return UNDECIDED

def get_view(board, player):
    view = [list(row) for row in board]
    for i, row in enumerate(view):
        if i % 2:
            continue
        for j, x in enumerate(row):
            if x == 3 - player:
                row[j] = 0
    return view

def run_game(player1, player2):
    players = {1 : player1, 2 : player2}
    board = [[0] * 7 for _ in range(6)]
    states = {1 : None, 2 : None}
    for turn in range(6 * 7):
        p = (turn % 2) + 1
        player = players[p]
        view = get_view(board, p)
        move, state = player(view, turn, states[p])
        outcome = apply_move(board, p, move)
        if outcome == DRAWN:
            return DRAWN
        elif outcome == WON:
            return p
        else:
            states[p] = state
    return DRAWN

def get_score(counts):
    return (counts[WON] - counts[LOST]) / float(sum(counts))

def run_tournament(players, rounds=10000):
    counts = [[0] * 3 for _ in players]
    for r in range(rounds):
        for i, player1 in enumerate(players):
            for j, player2 in enumerate(players):
                if i == j:
                    continue
                outcome = run_game(player1, player2)
                if outcome == DRAWN:
                    for k in i, j:
                        counts[k][DRAWN] += 1
                else:
                    if outcome == 1:
                        w, l = i, j
                    else:
                        w, l = j, i
                    counts[w][WON] += 1
                    counts[l][LOST] += 1
        ranks = sorted(range(len(players)), key = lambda i: get_score(counts[i]), reverse=True)
        print("Round %d of %d\n" % (r + 1, rounds))
        rows = [("Name", "Draws", "Losses", "Wins", "Score")]
        for i in ranks:
            name = players[i].__name__
            score = get_score(counts[i])
            rows.append([name + ":"] + [str(n) for n in counts[i]] + ["%6.3f" % score])
        lengths = [max(len(s) for s in col) + 1 for col in zip(*rows)]
        for i, row in enumerate(rows):
            padding = ((n - len(s)) * ' ' for s, n in zip(row, lengths))
            print(''.join(s + p for s, p in zip(row, padding)))
            if i == 0:
                print()
        print()

def random_player(view, turn, state):
    return random.randrange(0, 7), state

def constant_player(view, turn, state):
    return 0, state

def better_random_player(view, turn, state):
    while True:
        j = random.randrange(0, 7)
        if view[-1][j] == 0:
            return j, state

def better_constant_player(view, turn, state):
    for j in range(7):
        if view[-1][j] == 0:
            return j, state

players = [random_player, constant_player, better_random_player, better_constant_player]

run_tournament(players)

Selamat KoTHing!

Hasil Sementara

Name                    Draws Losses Wins  Score  

zsani_bot:              40    5377   94583  0.892 
better_constant_player: 0     28665  71335  0.427 
constant_player:        3     53961  46036 -0.079 
normalBot:              38    64903  35059 -0.298 
better_random_player:   192   71447  28361 -0.431 
random_player:          199   75411  24390 -0.510 
pengguna1502040
sumber
Bisakah Anda menjelaskan mengapa Anda memeriksa tampilan [-1] [j] == 0? Saya tidak sepenuhnya yakin saya melihat di mana Anda mengisinya dan pengetahuan python saya tampaknya agak berkarat.
Barbarian772
@ Barbarian772 Saya sedang memeriksa apakah masih ada ruang di kolom itu. Perhatikan bahwa ada 6 baris sehingga baris paling atas terpantau sepenuhnya.
user1502040
1
Anda tidak harus menghitung penempatan di kolom yang sudah penuh sebagai izin. Banyak menghubungkan 4 pertandingan berakhir dengan hanya satu kolom tidak diisi dan jika satu pemain akan kalah dengan bermain di kolom itu, ini akan membuat pertandingan mengikat ketika dinyatakan menang paksa untuk satu pemain.
soktinpk
@ soktinpk Bukankah itu hanya menambah lapisan strategi? Bagaimanapun, Connect-4 adalah permainan yang dipecahkan, jadi turn skipping factor bisa jadi merupakan perubahan aturan yang cukup sehingga kontributor tidak bisa hanya menggunakan algoritma standar.
mypetlion
1
Pengindeksan nol dari bawah, apakah baris yang ditempel (0,2,4,6) atau (1,3,5)? Beberapa seni ASCII akan sangat membantu.
SIGSTACKFAULT

Jawaban:

6

Bot ini mengambil semua kemenangan pasti, dan jatuh kembali untuk memblokir saingan, tebak kedua secara vertikal dan horizontal atau membuat gerakan acak.

impor sidik jari, matematika, koleksi, salin
def zsani_bot_2 (lihat, putar, nyatakan):
    Jika status == Tidak ada: Giliran #first sendiri - selalu untuk untuk menengah
        state = (1, 2) jika belok == 0 lainnya (2, 1) # (my_symbol, simbol Anda)
        #print (pprint.pformat (view) + 'Turn:' + str (turn) + 'Player:' + str (state [0]))
        return 3, sebutkan

    #lokasi poin yang jelas
    for i in range (1, 6): #skip baris pertama
        for j in range (len (view [i])): #TODO: Optimalkan dengan zip. Pergi untuk kejelasan sekarang
            jika melihat [i] [j]! = 0 dan melihat [i-1] [j] == 0:
                lihat [i-1] [j] = keadaan [1]
    enemy_points = math.floor (belok / 2)
    ++ musuh_points jika menyatakan [0] == 2 lain musuh_points
    known_points = jumlah ([i.count (status [1]) untuk i dalam tampilan])
    missing_points = enemy_points - known_points

    #getarkan pasti menang ke segala arah
    untuk j dalam kisaran (0, 7): # setiap kolom
        untuk saya dalam kisaran (4, -1, -1):
            jika melihat [i] [j]! = 0:
                break #find titik diisi tertinggi yang diketahui
        if (not missing_points atau i +1 dalam {1, 3, 5}):
            view1 = copy.deepcopy (view)
            usaha = apply_move (view1, sebutkan [0], j)
            jika percobaan == MENANG:
               # print (pprint.pformat (view) + 'Putar:' + str (putar) + 'Pemain:' + str (status [0]) + 'langkah pemenang')
                return j, state

    #block yakin musuh menang ke segala arah
    untuk j dalam kisaran (0, 7):
        untuk saya dalam kisaran (4, -1, -1):
            jika melihat [i] [j]! = 0:
                break #find titik diisi tertinggi yang diketahui
        if (not missing_points atau (i +1 di {1, 3, 5}))):
            view1 = copy.deepcopy (view)
            usaha = apply_move (view1, sebutkan [1], j)
            jika percobaan == MENANG:
              # print (pprint.pformat (view) + 'Putar:' + str (putar) + 'Pemain:' + str (status [0]) + 'simpan langkah')
                return j, state

    #blok dinding
    for i in range (0, 3): #impossible mendapatkan 4 berturut-turut saat kolom penuh
        untuk j dalam kisaran (0, 6):
            jika melihat [i] [j]! = 0 dan melihat [i] [j] == lihat [i + 1] [j] dan melihat [i + 2] [j] == lihat [i + 3] [j ] == 0:
             # print (pprint.pformat (view) + 'Putar:' + str (putar) + 'Pemain:' + str (status [0]) + 'pemindahan kolom')
                return j, state

    #block platform jika memiliki informasi sempurna di baris di bawah dan drop point
    untuk saya dalam kisaran (0, 5):
        untuk j dalam kisaran (0, 3):
            stats = collections.Counter ([lihat [i] [j], lihat [i] [j + 1], lihat [i] [j + 2], lihat [i] [j + 3]])
            jika stats [0] == 2 dan (stats [state [0]] == 2 atau stats [state [0]] == 2):
                untuk k dalam kisaran (0, 3):
                    jika melihat [i] [j + k] == 0:
                        istirahat
                if (i == 0 atau lihat [i-1] [j + k]! = 0) dan (bukan missing_points atau i dalam {1, 3, 5}):
                    #print (pprint.pformat (view) + 'Turn:' + str (turn) + 'Player:' + str (state [0]) + 'platform move')
                    return j + k, sebutkan
                lain:
                    untuk l dalam kisaran (k, 3):
                        jika melihat [i] [j + l] == 0:
                            istirahat
                        if (i == 0 atau lihat [i-1] [j + l]! = 0) dan (bukan missing_points atau i dalam {1, 3, 5}):
                     # print (pprint.pformat (view) + 'Putar:' + str (putar) + 'Player:' + str (status [0]) + 'platform move')
                            return j + l, sebutkan

    #fallback -> acak
    sementara Benar:
        j = random.randrange (0, 7)
        jika tampilan [-1] [j] == 0:
            #print (pprint.pformat (view) + 'Turn:' + str (turn) + 'Player:' + str (status [0]) + 'move acak')
            return j, state

Terima kasih telah memperbaiki run_game!

Changelog:

  • v2 menambahkan pemblokiran horizontal - jika, dalam deretan 4, ada dua tempat kosong dan dua tempat diisi oleh pemain yang sama, itu akan berusaha untuk mengisi salah satu dari mereka untuk memiliki tiga berturut-turut / memblokir baris lawan, yang diharapkan akan dikapitalisasi pada dalam belokan berikut.
Syfer Polski
sumber
3
Selamat datang di situs ini. Saya memilih untuk menolak edit untuk mengubah kode, lebih baik dibiarkan sebagai komentar, dengan cara itu OP dapat memutuskan apa yang harus dilakukan dengan kode.
Ad Hoc Garf Hunter
Saya tidak memiliki reputasi yang cukup untuk mengomentari pos utama. Bagaimana cara saya menarik suntingan?
Syfer Polski
Tidak perlu menarik suntingan (saya kira Anda tidak bisa melakukannya). Di masa depan komentar akan cukup tetapi karena Anda telah mengatakannya dalam jawaban Anda, kemungkinan OP akan melihatnya. Ditambah lagi, saya pikir OP akan melihat bahwa Anda menyarankan dan mengedit meskipun telah ditolak.
Ad Hoc Garf Hunter
Alasan saya ingin menarik suntingan adalah karena saya melewatkan satu baris dalam perubahan, yang tanpanya kode yang diedit akan sepenuhnya gagal dijalankan. Saya sudah memasukkannya dalam sunting saran dalam posting saya. Terima kasih untuk bantuannya!
Syfer Polski
2

normalBot bermain dengan asumsi bahwa bintik-bintik di tengah lebih berharga daripada bintik-bintik di ujungnya. Dengan demikian, ia menggunakan distribusi normal yang berpusat di tengah untuk menentukan pilihannya.

def normalBot(view, turn, state):
    randomNumber = round(np.random.normal(3, 1.25))
    fullColumns = []
    for i in range(7):
        if view[-1][i] != 0:
            fullColumns.append(i)
    while (randomNumber > 6) or (randomNumber < 0) or (randomNumber in fullColumns):
        randomNumber = round(np.random.normal(3, 1.25))
    return randomNumber, state
Bob Cratchit
sumber
-1

Ini jelas non-bersaing, tapi tetap sangat berguna untuk debugging ... dan sangat menyenangkan, jika Anda tahu bot Anda cukup baik untuk menipu: D jadi saya memposting dengan harapan menginspirasi lebih banyak kiriman:

import math, pprint
def manual_bot(view, turn, state):
    if state == None:
        state = (1, 2) if turn == 0 else (2, 1) #(my_symbol, your symbol)

#locate obvious points
    for row in range (1, 6):
        for j in range(len(view[row])):
            if view[row][j] != 0 and view[row-1][j] == 0:
                view[row-1][j] = state[1]

#if you're second, the opponent has one more point than half the turns
    enemy_points = math.ceil(turn/2)
    known_points = sum([row.count(state[1]) for row in view])
    missing_points = enemy_points - known_points

    print(pprint.pformat(view) + ' Turn: ' + str(turn) + ' Player: ' + str(state[0]) + ' Missing points: ' + str(missing_points))
    while True:
        try:
            move = int(input("What is your move?(0-6) "))
        except ValueError:
            continue
        if move in {0, 1, 2, 3, 4, 5, 6}:
            return move, state

Kotak terbalik (baris paling bawah). Untuk mendapatkan pengumuman pemenang, Anda harus menambal pengontrol permainan, menambahkan pernyataan cetak sebelum mengembalikan kemenangan:

elif outcome == WON:
    print(pprint.pformat(board) + ' Turn: ' + str(turn) +' Winner: '+ str(p))
    return p

[[0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0]] Putar: 0 Pemain: 1 Poin yang hilang: 0
Apa langkahmu? (0-6) 3
[[0, 0, 0, 1, 0, 0, 0],
 [0, 0, 0, 2, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0]] Putar: 2 Pemain: 1 Poin yang hilang: 0
Apa langkahmu? (0-6) 2
[[0, 0, 1, 1, 0, 0, 0],
 [0, 0, 0, 2, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0]] Putar: 4 Pemain: 1 Poin yang hilang: 1
Apa langkahmu? (0-6) 4
[[0, 0, 1, 1, 1, 0, 0],
 [0, 0, 0, 2, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0]] Putar: 6 Pemain: 1 Poin yang hilang: 2
Apa langkahmu? (0-6) 1
[[2, 1, 1, 1, 1, 2, 0],
 [0, 0, 0, 2, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0]] Putar: 6 Pemenang: 1
[[0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0]] Putar: 1 Pemain: 2 Poin yang hilang: 1
Apa langkahmu? (0-6) 2
[[0, 0, 2, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0]] Putar: 3 Pemain: 2 Poin yang hilang: 2
Apa langkahmu? (0-6) 3
[[0, 0, 2, 1, 0, 0, 0],
 [0, 0, 1, 2, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0]] Putar: 5 Pemain: 2 Poin yang hilang: 1
Apa langkahmu? (0-6) 4
[[0, 0, 2, 1, 2, 0, 0],
 [0, 0, 1, 2, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0]] Putar: 7 Pemain: 2 Poin yang hilang: 2
Apa langkahmu? (0-6) 1
[[0, 2, 2, 1, 2, 0, 0],
 [0, 0, 1, 2, 0, 0, 0],
 [0, 0, 1, 0, 0, 0, 0],
 [0, 0, 1, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0]] Putar: 9 Pemain: 2 Poin yang hilang: 1
Apa langkahmu? (0-6) 2
[[0, 2, 2, 1, 2, 0, 0],
 [0, 0, 1, 2, 0, 0, 0],
 [0, 0, 1, 0, 0, 0, 0],
 [0, 0, 1, 0, 0, 0, 0],
 [0, 0, 2, 0, 0, 0, 0],
 [0, 0, 1, 0, 0, 0, 0]] Putar: 11 Pemain: 2 Poin yang hilang: 1
Apa langkahmu? (0-6) 4
[[0, 2, 2, 1, 2, 0, 0],
 [0, 0, 1, 2, 2, 0, 0],
 [0, 0, 1, 0, 0, 0, 0],
 [0, 0, 1, 0, 0, 0, 0],
 [0, 0, 2, 0, 0, 0, 0],
 [0, 0, 1, 0, 0, 0, 0]] Putar: 13 Pemain: 2 Poin yang hilang: 2
Apa langkahmu? (0-6) 4
[[0, 2, 2, 1, 2, 0, 0],
 [0, 1, 1, 2, 2, 0, 0],
 [0, 0, 1, 0, 1, 0, 0],
 [0, 0, 1, 0, 2, 0, 0],
 [0, 0, 2, 0, 0, 0, 0],
 [0, 0, 1, 0, 0, 0, 0]] Putar: 15 Pemain: 2 Poin yang hilang: 1
Apa langkahmu? (0-6) 3
[[0, 2, 2, 1, 2, 0, 0],
 [0, 1, 1, 2, 2, 0, 0],
 [0, 0, 1, 2, 1, 0, 0],
 [0, 0, 1, 0, 2, 0, 0],
 [0, 0, 2, 0, 0, 0, 0],
 [0, 0, 1, 0, 0, 0, 0]] Putar: 17 Pemain: 2 Poin yang hilang: 2
Apa langkahmu? (0-6) 5
[[0, 2, 2, 1, 2, 1, 1],
 [0, 1, 1, 2, 2, 2, 1],
 [0, 0, 1, 2, 1, 0, 0],
 [0, 0, 1, 0, 2, 0, 0],
 [0, 0, 2, 0, 0, 0, 0],
 [0, 0, 1, 0, 0, 0, 0]] Putar: 19 Pemain: 2 Poin yang hilang: 0
Apa langkahmu? (0-6) 
Apa langkahmu? (0-6) 6
[[0, 2, 2, 1, 2, 1, 1],
 [0, 1, 1, 2, 2, 2, 1],
 [0, 0, 1, 2, 1, 0, 2],
 [0, 0, 1, 0, 2, 0, 0],
 [0, 0, 2, 0, 0, 0, 0],
 [0, 0, 1, 0, 0, 0, 0]] Putar: 21 Pemain: 2 Poin yang hilang: 1
Apa langkahmu? (0-6) 1
[[0, 2, 2, 1, 2, 1, 1],
 [0, 1, 1, 2, 2, 2, 1],
 [0, 2, 1, 2, 1, 0, 2],
 [0, 1, 1, 0, 2, 0, 0],
 [0, 0, 2, 0, 0, 0, 0],
 [0, 0, 1, 0, 0, 0, 0]] Putaran: 23 Pemain: 2 Poin yang hilang: 1
Apa langkahmu? (0-6) 3
[[0, 2, 2, 1, 2, 1, 1],
 [0, 1, 1, 2, 2, 2, 1],
 [0, 2, 1, 2, 1, 0, 2],
 [0, 1, 1, 2, 2, 0, 0],
 [0, 0, 2, 0, 0, 0, 0],
 [0, 0, 1, 0, 0, 0, 0]] Putar: 25 Pemain: 2 Poin yang hilang: 2
Apa langkahmu? (0-6) 6
[[0, 2, 2, 1, 2, 1, 1],
 [0, 1, 1, 2, 2, 2, 1],
 [0, 2, 1, 2, 1, 0, 2],
 [0, 1, 1, 2, 2, 0, 2],
 [0, 0, 2, 1, 0, 0, 0],
 [0, 0, 1, 1, 0, 0, 0]] Putaran: 27 Pemain: 2 Poin yang hilang: 1
Apa langkahmu? (0-6) 5
[[1, 2, 2, 1, 2, 1, 1],
 [1, 1, 1, 2, 2, 2, 1],
 [0, 2, 1, 2, 1, 2, 2],
 [0, 1, 1, 2, 2, 0, 2],
 [0, 0, 2, 1, 0, 0, 0],
 [0, 0, 1, 1, 0, 0, 0]] Putar: 29 Pemain: 2 Poin yang hilang: 0
Apa langkahmu? (0-6) 5
[[1, 2, 2, 1, 2, 1, 1],
 [1, 1, 1, 2, 2, 2, 1],
 [0, 2, 1, 2, 1, 2, 2],
 [0, 1, 1, 2, 2, 2, 2],
 [0, 0, 2, 1, 0, 0, 0],
 [0, 0, 1, 1, 0, 0, 0]] Putar: 29 Pemenang: 2
Babak 1 dari 1

Name Draws Losses Menangkan Skor
manual_bot: 0 0 2 1.000 zsani_bot_2: 0 2 0 -1.000

Syfer Polski
sumber