Częściowo obserwowalny Connect-4

8

Gra

Będziesz grał w (prawie) standardową grę Connect-4 . Niestety, jest to gra korespondencyjna i ktoś umieścił czarną taśmę w co drugim rzędzie, zaczynając od dołu, abyś nie widział żadnego ruchu przeciwnika w tych rzędach.

Wszelkie ruchy w już wypełnionych kolumnach będą liczone jako przejście twojej tury, a jeśli gra trwa dłużej niż 6 * 7tury, zostanie uznana za remis.

Specyfikacja wyzwania

Twój program powinien zostać zaimplementowany jako funkcja Python 3. Pierwszym argumentem jest „widok” planszy, reprezentujący znany stan planszy jako dwuwymiarową listę rzędów od dołu do góry, gdzie 1jest ruch pierwszego gracza, 2ruch drugiego gracza oraz 0pusta pozycja lub ukryta pozycja porusz się przez przeciwnika.

Drugim argumentem jest indeksowany numer tury 0, a jego parzystość mówi ci, którym jesteś graczem.

Ostatnim argumentem jest dowolny stan, inicjowany Nonena początku każdej gry, którego można użyć do zachowania stanu między kolejkami.

Powinieneś zwrócić 2-krotkę indeksu kolumny, w który chcesz zagrać, i nowy stan, który zostanie ci zwrócony w następnej turze.

Punktacja

Wygrana liczy się jako +1, remis jako 0, a przegrana jako -1. Twoim celem jest osiągnięcie jak najwyższego średniego wyniku w turnieju round-robin. Postaram się rozegrać tyle meczów, ile potrzeba, aby znaleźć wyraźnego zwycięzcę.

Zasady

Każdy zawodnik powinien mieć co najwyżej jednego konkurującego bota w tym samym czasie, ale poprawienie wpisu jest poprawne, jeśli wprowadzasz poprawki. Spróbuj ograniczyć bota do ~ 1 sekundy myślenia na turę.

Testowanie

Oto kod źródłowy kontrolera wraz z kilkoma niekonkurencyjnymi przykładowymi botami w celach informacyjnych:

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)

Happy KoTHing!

Wstępne wyniki

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 
użytkownik1502040
źródło
Czy możesz wyjaśnić, dlaczego sprawdzasz widok [-1] [j] == 0? Nie jestem do końca pewien, czy widzę, gdzie je wypełniłeś, a moja wiedza w języku Python wydaje się nieco zardzewiała.
Barbarian772,
@ Barbarian772 Sprawdzam, czy w tej kolumnie jest jeszcze miejsce. Zauważ, że jest 6 rzędów, więc górny rząd jest w pełni przestrzegany.
user1502040,
1
Nie należy liczyć umieszczania w już pełnych kolumnach jako przepustki. Wiele gier łączących 4 kończy się tylko jedną kolumną, która nie jest wypełniona, a jeśli jeden gracz przegra, grając w tej kolumnie, spowoduje to remis w grze, gdy w innym przypadku jest to wymuszona wygrana dla jednego gracza.
soktinpk
@soktinpk Czy to nie dodaje kolejnej warstwy strategii? Connect-4 jest w końcu rozwiązaną grą, więc współczynnik pominięcia tury może być wystarczającą zmianą reguły, że uczestnicy nie mogą po prostu użyć standardowych algorytmów.
mypetlion
1
Zero indeksowania od dołu, czy rzędy (0,2,4,6) czy (1,3,5) są klejone? Przydałaby się sztuka ASCII.
SIGSTACKFAULT,

Odpowiedzi:

6

Ten bot bierze wszystkie pewne wygrane i cofa się, aby zablokować rywali, po drugie zgadnij ich w pionie i poziomie lub wykonuj losowe ruchy.

import pprint, matematyka, kolekcje, kopiowanie
def zsani_bot_2 (zobacz, obróć, stan):
    if state == Brak: # pierwsza własna tura - zawsze dla środkowej
        state = (1, 2) if turn == 0 else (2, 1) # (my_symbol, twój symbol)
        #print (pprint.pformat (view) + 'Turn:' + str (turn) + 'Player:' + str (state [0]))
        zwróć 3, stan

    # zlokalizuj oczywiste punkty
    dla i w zakresie (1, 6): #pierwszy wiersz
        dla jw zakresie (len (widok [i])): # DO ZROBIENIA: Optymalizacja za pomocą zip. Idź teraz dla jasności
            jeśli widok [i] [j]! = 0 i widok [i-1] [j] == 0:
                widok [i-1] [j] = stan [1]
    punkty wroga = matematyka. podłoga (tura / 2)
    ++ punkty wroga, jeśli stan [0] == 2 pozostałe punkty wroga
    znane_punkty = suma ([i.count (stan [1]) dla i w widoku])
    missing_points = wrogi_punkt - znany_punkt

    #get pewnie wygrywa w dowolnym kierunku
    dla jw zakresie (0, 7): # każda kolumna
        dla i w zakresie (4, -1, -1):
            jeśli widok [i] [j]! = 0:
                break # znajdź najwyższy znany wypełniony punkt
        jeśli (nie brakuje punktów = i + 1 w {1, 3, 5}):
            view1 = copy.deepcopy (zobacz)
            próba = zastosowanie_sunięcia (widok1, stan [0], j)
            jeśli próba == WON:
               # print (pprint.pformat (widok) + „Turn:” + str (turn) + „Player:” + str (stan [0]) + „zwycięzca ruch”)
                zwróć j, stan

    #blokuj, czy wróg wygrywa w dowolnym kierunku
    dla jw zakresie (0, 7):
        dla i w zakresie (4, -1, -1):
            jeśli widok [i] [j]! = 0:
                break # znajdź najwyższy znany wypełniony punkt
        jeśli (nie brakuje punktów lub (i + 1 w {1, 3, 5})):
            view1 = copy.deepcopy (zobacz)
            próba = zastosowanie_sunięcia (widok1, stan [1], j)
            jeśli próba == WON:
              # print (pprint.pformat (view) + 'Turn:' + str (turn) + 'Player:' + str (state [0]) + 'save move')
                zwróć j, stan

    #blokuj ściany
    dla i w zakresie (0, 3): # nie można uzyskać 4 z rzędu, gdy kolumna jest pełna
        dla jw zakresie (0, 6):
            jeśli widok [i] [j]! = 0 i widok [i] [j] == widok [i + 1] [j] i widok [i + 2] [j] == widok [i + 3] [j ] == 0:
             # print (pprint.pformat (view) + 'Turn:' + str (turn) + 'Player:' + str (state [0]) + 'przeniesienie kolumny')
                zwróć j, stan

    #blokuj platformy, jeśli masz doskonałe informacje na temat wiersza poniżej i punktu zrzutu
    dla i w zakresie (0, 5):
        dla jw zakresie (0, 3):
            statystyki = kolekcje. licznik ([widok [i] [j], widok [i] [j + 1], widok [i] [j + 2], widok [i] [j + 3]])
            jeśli statystyki [0] == 2 i (statystyki [stan [0]] == 2 lub statystyki [stan [0]] == 2):
                dla kw zakresie (0, 3):
                    jeśli widok [i] [j + k] == 0:
                        złamać
                jeśli (i == 0 lub wyświetl [i-1] [j + k]! = 0) i (nie brakuje punktów_i lub i w {1, 3, 5}):
                    #print (pprint.pformat (widok) + „Turn:” + str (turn) + „Player:” + str (stan [0]) + „ruch platformy”)
                    zwróć j + k, stan
                jeszcze:
                    dla lw zakresie (k, 3):
                        jeśli widok [i] [j + l] == 0:
                            złamać
                        jeśli (i == 0 lub wyświetl [i-1] [j + l]! = 0) i (nie brakuje punktów_i lub i w {1, 3, 5}):
                     # print (pprint.pformat (view) + 'Turn:' + str (turn) + 'Player:' + str (state [0]) + 'move platform')
                            zwróć j + l, stan

    #fallback -> losowy
    podczas gdy prawda:
        j = random.randrange (0, 7)
        jeśli widok [-1] [j] == 0:
            #print (pprint.pformat (view) + 'Turn:' + str (turn) + 'Player:' + str (state [0]) + 'random move')
            zwróć j, stan

Dziękujemy za naprawienie gry run_game!

Dziennik zmian:

  • v2 dodaje blokowanie poziome - jeśli w rzędzie 4 znajdują się dwa puste miejsca i dwa miejsca wypełnione przez tego samego gracza, spróbuje on wypełnić jedno z nich, aby mieć trzy w rzędzie / zablokować rząd przeciwników, co, miejmy nadzieję, będzie być kapitalizowanym w następnych turach.
Syfer Polski
źródło
3
Witamy na stronie. Głosowałem za odrzuceniem zmiany na zmianę na kod, najlepiej zostawić komentarz, w ten sposób OP może zdecydować, co zrobić z kodem.
Ad Hoc Garf Hunter,
Nie mam wystarczającej reputacji, aby komentować główny post. Jak wycofać edycję?
Syfer Polski
Nie ma potrzeby wycofywania edycji (nie sądzę, że i tak możesz). W przyszłości komentarze będą wystarczające, ale skoro powiedziałeś to w swojej odpowiedzi, prawdopodobnie zobaczy to PO. Plus, myślę, że PO zobaczy, że zasugerowałeś i edytujesz, nawet jeśli został odrzucony.
Ad Hoc Garf Hunter,
Powodem, dla którego chciałbym wycofać edycję, jest to, że przegapiłem jeden wiersz w zmianach, bez którego edytowany kod całkowicie nie zadziała. Uwzględniłem to w propozycji edycji w swoim poście. Dziękuję za pomoc!
Syfer Polski
2

normalBot przyjmuje założenie, że plamy na środku są cenniejsze niż plamy na końcach. W związku z tym używa normalnego rozkładu wyśrodkowanego w środku, aby określić swoje wybory.

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
źródło
-1

Jest to oczywiście niekonkurencyjne, ale jednak bardzo przydatne do debugowania ... i zaskakująco zabawne, jeśli znasz swojego bota wystarczająco dobrze, aby oszukiwać: D, więc mam nadzieję zainspirować więcej zgłoszeń:

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

Siatka jest odwrócona (dolny rząd jest najwyższy). Aby otrzymać ogłoszenie o zwycięzcy, musisz załatać kontroler gier, dodając instrukcję drukowania przed zwróceniem wygranej:

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]] Tura: 0 Gracz: 1 Brakujące punkty: 0
Jaki jest twój ruch? (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]] Tura: 2 Gracz: 1 Brakujące punkty: 0
Jaki jest twój ruch? (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]] Tura: 4 Gracz: 1 Brakujące punkty: 1
Jaki jest twój ruch? (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]] Tura: 6 Gracz: 1 Brakujące punkty: 2
Jaki jest twój ruch? (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]] Tura: 6 Zwycięzca: 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]] Tura: 1 Gracz: 2 Brakujące punkty: 1
Jaki jest twój ruch? (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]] Tura: 3 Gracz: 2 Brakujące punkty: 2
Jaki jest twój ruch? (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]] Tura: 5 Gracz: 2 Brakujące punkty: 1
Jaki jest twój ruch? (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]] Tura: 7 Gracz: 2 Brakujące punkty: 2
Jaki jest twój ruch? (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]] Tura: 9 Gracz: 2 Brakujące punkty: 1
Jaki jest twój ruch? (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]] Tura: 11 Gracz: 2 Brakujące punkty: 1
Jaki jest twój ruch? (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]] Tura: 13 Gracz: 2 Brakujące punkty: 2
Jaki jest twój ruch? (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]] Tura: 15 Gracz: 2 Brakujące punkty: 1
Jaki jest twój ruch? (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]] Tura: 17 Gracz: 2 Brakujące punkty: 2
Jaki jest twój ruch? (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]] Tura: 19 Gracz: 2 Brakujące punkty: 0
Jaki jest twój ruch? (0-6) 
Jaki jest twój ruch? (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]] Tura: 21 Gracz: 2 Brakujące punkty: 1
Jaki jest twój ruch? (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]] Tura: 23 Gracz: 2 Brakujące punkty: 1
Jaki jest twój ruch? (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]] Tura: 25 Gracz: 2 Brakujące punkty: 2
Jaki jest twój ruch? (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]] Tura: 27 Gracz: 2 Brakujące punkty: 1
Jaki jest twój ruch? (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]] Tura: 29 Gracz: 2 Brakujące punkty: 0
Jaki jest twój ruch? (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]] Tura: 29 Zwycięzca: 2
Runda 1 z 1

Nazwa Losuje Przegrane Wygrywa Wynik
manual_bot: 0 0 2 1.000 zsani_bot_2: 0 2 0 -1.000

Syfer Polski
źródło