Vamos jogar Meta jogo da velha!

38

Vamos jogar um jogo de Meta jogo da velha!

Este é um torneio do do Meta jogo da velha. As regras do Meta jogo da velha são as seguintes:

  1. Todas as regras regulares do jogo da velha aplicam-se.

  2. Existem nove quadros organizados para formar um quadro principal. Igual a:

    0|1|2 || 0|1|2 || 0|1|2 
    ----- || ----- || ----- 
    3|4|5 || 3|4|5 || 3|4|5 
    ----- || ----- || ----- 
    6|7|8 || 6|7|8 || 6|7|8 
    ========================
    0|1|2 || 0|1|2 || 0|1|2 
    ----- || ----- || ----- 
    3|4|5 || 3|4|5 || 3|4|5 
    ----- || ----- || ----- 
    6|7|8 || 6|7|8 || 6|7|8 
    ========================
    0|1|2 || 0|1|2 || 0|1|2 
    ----- || ----- || ----- 
    3|4|5 || 3|4|5 || 3|4|5 
    ----- || ----- || ----- 
    6|7|8 || 6|7|8 || 6|7|8 
    

    placa 0 refere-se à placa superior esquerda, placa 1 refere-se à placa intermediária superior ... assim

    0|1|2
    -----
    3|4|5
    -----
    6|7|8
    

    Se eu disser o quadro 3, lado a lado 4, significa o lado central do quadro no meio esquerdo.

  3. Você só pode mover-se em uma das placas menores.

  4. Se você ganhar uma das tábuas menores, essa tábua inteira será considerada como sua peça.

  5. Se um dos quadros for preenchido antes que um dos bot o vença, será considerado como ninguém.

  6. Quem vencer o quadro principal vence!

No entanto, há uma reviravolta importante. Digamos que eu entre no tabuleiro 7, bloco 2. Isso significa que, no seu turno, você pode entrar no tabuleiro 2. Então, digamos que você entre no tabuleiro 2, bloco 5. Agora, no meu turno, posso participar do tabuleiro 5. Digamos que o quadro 1 esteja cheio. (Não há mais vagas sobrando, ou um de nós já venceu o quadro 1) Agora, se eu entrar no quadro 5, lado 1, você poderá entrar em qualquer um dos quadros que desejar.

Essas regras podem ser consideradas como:

  1. Você deve jogar no tabuleiro correspondente à posição jogada pelo jogador anterior.
    • Se X jogar no tabuleiro 2, peça 5; O deve jogar no tabuleiro 5
  2. Se o quadro alvo estiver cheio (um empate) ou já tiver um vencedor, a próxima jogada é irrestrita.
  3. Um tabuleiro com um vencedor não pode ser jogado, mesmo em uma jogada sem restrições.

Se isso é um pouco confuso, você pode tentar online aqui.(certifique-se de alternar de "vitórias no primeiro bloco" para "3 blocos seguidos")

Agora, aqui estão as regras do desafio.

  1. Você deve escrever um bot que jogue este jogo.

  2. O bot 1 é Xs e é o primeiro. Ele será chamado com estes argumentos de linha de comando (sem os itens entre parênteses):

    X         (whose turn)
    --------- (board 0)
    --------- (board 1)
    --------- (board 2)
    --------- (board 3)
    --------- (board 4)
    --------- (board 5)
    --------- (board 6)
    --------- (board 7)
    --------- (board 8)
    --------- (master board)
    xx        (last move)
    

    O primeiro caractere representa quem é o bot. Nesse caso, o bot 1 é reproduzido como X. As próximas 9 linhas referem-se às 9 placas. A 11ª linha refere-se ao quadro principal. O "xx" é a última jogada. Agora, o bot1 deve imprimir dois números entre 0 e 8. O número 1 é o quadro em que seu bot está se movendo e o número 2 é o bloco no referido quadro. O controlador acompanhará esse movimento. Digamos que o bot 1 imprima 38. Agora o quadro ficará assim:

     | |  ||  | |  ||  | |  
    ----- || ----- || ----- 
     | |  ||  | |  ||  | |  
    ----- || ----- || ----- 
     | |  ||  | |  ||  | |  
    ==========================
     | |  ||  | |  ||  | |  
    ----- || ----- || ----- 
     | |  ||  | |  ||  | |  
    ----- || ----- || ----- 
     | |X ||  | |  ||  | |  
    ==========================
     | |  ||  | |  ||  | |  
    ----- || ----- || ----- 
     | |  ||  | |  ||  | |  
    ----- || ----- || ----- 
     | |  ||  | |  ||  | |  
    

    e bot2 serão chamados com estes argumentos:

    O
    ---------
    --------- 
    --------- 
    --------X 
    --------- 
    --------- 
    --------- 
    --------- 
    --------- 
    ---------
    38
    
  3. Agora o bot 2 deve se mover no quadro 8 (porque o bot1 colocou um x no bloco 3). Digamos que bot2 imprime 84. Agora o quadro se parece com isso.

     | |  ||  | |  ||  | |  
    ----- || ----- || ----- 
     | |  ||  | |  ||  | |  
    ----- || ----- || ----- 
     | |  ||  | |  ||  | |  
    ==========================
     | |  ||  | |  ||  | |  
    ----- || ----- || ----- 
     | |  ||  | |  ||  | |  
    ----- || ----- || ----- 
     | |X ||  | |  ||  | |  
    ==========================
     | |  ||  | |  ||  | |  
    ----- || ----- || ----- 
     | |  ||  | |  ||  |O|  
    ----- || ----- || ----- 
     | |  ||  | |  ||  | |  
    

    agora o bot1 será chamado com estes argumentos:

    X
    ---------
    --------- 
    --------- 
    --------X 
    --------- 
    --------- 
    --------- 
    --------- 
    ----0---- 
    ---------
    84
    
  4. Agora o bot1 deve se mover no quadro 4. No entanto, o bot1 é um bot pequeno e impertinente e decide se mover no quadro 3. Imprime '30'. O conselho não muda nada. O bot principal acompanha isso. Agora o bot2 será chamado com estes argumentos:

    O
    ---------
    --------- 
    --------- 
    --------X 
    --------- 
    --------- 
    --------- 
    --------- 
    ----0---- 
    ---------
    xx
    
  5. Agora, o bot 2 pode ir aonde quiser (exceto 38 e 84, é claro). Isso continua até que alguém vença três das placas-mãe seguidas. Depois, há uma segunda luta em que bot2 é X e começa primeiro.

  6. Isso se repete até que cada bot jogue todos os outros bot.

Pontuação

A pontuação funciona assim:

O vencedor de cada partida ganha 100 + number of open spotspontos. Dessa forma, é mais valioso se o seu bot vencer rapidamente. Toda vez que seu bot faz um movimento inválido, ele perde 1 ponto. Se, após 250 rodadas, nenhum bot vencer, cada bot perde 10 pontos e passamos à próxima rodada.


Tudo será colocado em um diretório que contém

  1. O bot do controlador. Este é um programa C ++ que eu escrevi. Você pode olhar o código-fonte bot do controlador aqui. Informe-me se vir algo que não está certo no controlador.

  2. Um arquivo de texto chamado instructions.txtEste arquivo será mais ou menos assim:

    [Total number of bots that are competing]
    
    [bot1Name] [bot1 command to run]
    [bot2Name] [bot2 command to run]
    ...
    
  3. Uma pasta para cada bot. Esta pasta conterá seu programa (seja um script ou um binário) e um arquivo de texto chamado data.txtque seu bot possa ler e gravar o que quiser.

Especificações técnicas e esclarecimentos de regras

  • Qualquer bot que tentar ler / escrever algo de qualquer lugar que não esteja dentro da pasta será expulso do jogo.

  • Seu programa deve ser capaz de executar em um macbook executando o Yosemite. As linguagens atualmente suportadas são python (2.7.9 e 3.4.2), C / C ++, objetivo-C, perl, ruby, bash, PHP, Java, C #, javascript e Haskell. Há muito mais, mas estes são apenas os que consigo pensar agora. Adicionarei mais conforme o tempo passa. Se você quiser competir em um idioma específico, envie uma mensagem ou comentário e eu o adicionarei à lista, se possível.

  • Se um tabuleiro for vencido, mas ainda houver espaço, você ainda não poderá entrar em um dos espaços abertos.

  • Observe que o diretório de trabalho do seu envio será o diretório que contém o controlador e todos os outros bots, NÃO o diretório que contém o seu bot.

  • Por favor, publique junto com o código do seu controlador o comando correto para compilar (se aplicável) e executar seu bot. A maior parte disso será feita no terminal OS X, que é bastante semelhante a um terminal linux.

  • Os robôs devem ser concluídos em menos de um segundo. Infelizmente, não sou suficientemente competente para adicionar um timer ao bot do controlador. No entanto, cronometrarei manualmente os bots.


Resultados!

Bem, eu estava certa. Eu esqueci de fazer o bot do controlador verificar se o masterBoard está cheio. Se o masterBoard estiver cheio, TODOS os movimentos são inválidos, mas continuam a chamar os bots, e é provavelmente por isso que houve tantos movimentos inválidos. Eu o consertei agora. Aqui estão os resultados oficiais com a versão mais atual de todos os bots.

Bot 1, goodRandBot, has 1 wins and made 0 illegal moves, for a total of 133 points.
Bot 2, naiveBot, has 3 wins and made 48 illegal moves, for a total of 361 points.
Bot 3, depthBot, has 5 wins and made 0 illegal moves, for a total of 664 points.
Bot 4, middleBot, has 1 wins and made 20 illegal moves, for a total of 114 points.

With 4 bots, This program took 477.471 seconds to finish.

Depth Bot é o atual campeão! Pelo menos por enquanto.

DJMcMayhem
fonte
Como um aparte, você já olhou para Fire and Ice (uma versão pbem em gamerz.net ) - existem alguns elementos do jogo da velha ... embora isso também me lembrasse o escriba .
9 curtidas e 40 visualizações. Estou impressionado!
Loovjo
5
Você pode colocar um limite no tempo de resposta dos bots, ou os bots podem demorar 3 minutos por movimento enquanto pesquisam todos os movimentos futuros possíveis.
Logic Knight
1
Adicionei alguns esclarecimentos sobre o próximo passo. Eu tenho uma preocupação com o formato dos dados e uma das regras. Regra 5 da primeira seção: "Se uma das placas for preenchida, ela conta como ninguém no bloco". Isso é preenchido sem um vencedor? ou seja, se alguém ganhar o ladrilho anteriormente e ele ficar preenchido, é ninguém ladrilhar? Além disso, se os bots são apátridas (parecem) com o estado passado, como é XXX000---transmitido o vencedor de uma placa ? ou é que 'ninguém consegue, apesar de O ter vencido primeiro'?
@MichaelT, o vencedor do conselho é passado na 11ª linha. Editarei esta parte para torná-la um pouco mais clara, mas sua edição está incorreta. "Se um tabuleiro for vencido, mas ainda houver espaço, você ainda não poderá entrar em um dos lugares abertos".
DJMcMayhem

Respostas:

5

Python 2.7, profundidade

Uma implementação de poda alfa-beta sem nada demais. Ele tenta ordenar movimentos de uma maneira menos ingênua para maximizar as eliminações alfa-beta. Provavelmente tentarei acelerar, mas sinceramente não sei o quanto o Python pode ser competitivo se se trata de uma questão de velocidade.

class DepthPlayer:
    def __init__(self,depth):
        self.depth = depth

    def score(self,master,subs,last_move):
        total = 0
        for x in range(3):
            for y in range(3):
                c = master[3*y+x]
                if c == 0:
                    total += sum(subs[3*y+x])
                else:
                    total += c*10
                    for (dx,dy) in [(1,-1),(1,0),(0,1),(1,1)]:
                        if x+dx<=2 and 0<=y+dy<=2 and master[3*(y+dy)+(x+dx)] == c:
                            total += c*10
        if last_move is None or master[last_move[1]] != 0 or 0 not in subs[last_move[1]]:
            total += 5
        return total

    def winner(self,board):
        for y in range(3):
            row = board[3*y:3*y+3]
            if 0!=row[0]==row[1]==row[2]:
                return row[0]
        for x in range(3):
            col = board[x:9:3]
            if 0!=col[0]==col[1]==col[2]:
                return col[0]
        if 0!=board[0]==board[4]==board[8]:
            return board[0]
        if 0!=board[2]==board[4]==board[6]:
            return board[2]

        return 0

    def parse(self,input):
        lines = input.split('\n')
        team = lines[0]
        subs_str = lines[1:10]
        master_str = lines[10]
        last_move_str = lines[11]

        master = [1 if c==team else 0 if c=='-' else -1 for c in master_str]
        subs = [[1 if c==team else 0 if c=='-' else -1 for c in sub_str] for sub_str in subs_str]
        if last_move_str == 'xx':
            last_move = None

        else:
            last_move = [int(c) for c in last_move_str]
        return master,subs,last_move

    def alphabeta(self, master,subs,last_move, depth, alpha, beta, player):
        if depth == 0:
            return self.score(master,subs,last_move),None
        w = self.winner(master)
        if w != 0:
            return w*1000,None

        if player:
            v = -10000
            best = None
            for n_master,n_subs,n_last_move in self.all_moves(master,subs,last_move,1):
                nv,_ = self.alphabeta(n_master,n_subs,n_last_move, depth-1, alpha, beta, False)
                if nv>v:
                    v = nv
                    best = n_last_move
                alpha = max(alpha, v)
                if beta <= alpha:
                    break
            return v,best
        else:
            v = 10000
            best = None
            for n_master,n_subs,n_last_move in self.all_moves(master,subs,last_move,-1):
                nv,nb = self.alphabeta(n_master,n_subs,n_last_move, depth-1, alpha, beta, True)
                if nv<v:
                    v = nv
                    best = n_last_move
                beta = min(beta, v)
                if beta <= alpha:
                    break
            return v,best

    def make_move(self,master,subs,move,player):
        n_subs = [sub[:] for sub in subs]
        n_master = master[:]
        n_subs[move[0]][move[1]] = player
        if n_master[move[0]] == 0:
            n_master[move[0]] = self.winner(n_subs[move[0]])
        return n_master,n_subs,move

    def sub_moves(self,board):
        first = []
        second = []
        third = []
        for i in range(9):
            if board[i] != 0:
                continue
            y,x = divmod(i,3)
            c=-2
            if   x==0 and 0!=board[i+1]==board[i+2]>c: c=board[i+1]
            elif x==1 and 0!=board[i-1]==board[i+1]>c: c=board[i-1]
            elif x==2 and 0!=board[i-2]==board[i-1]>c: c=board[i-2]
            if   y==0 and 0!=board[i+3]==board[i+6]>c: c=board[i+3]
            elif y==1 and 0!=board[i-3]==board[i+3]>c: c=board[i-3]
            elif y==2 and 0!=board[i-6]==board[i-3]>c: c=board[i-6]
            if i in [0,4,8] and 0!=board[(i+4)%12]==board[(i+4)%12]>c: c=board[i-6]
            if i in [2,4,6] and 0!=board[6 if i==2 else i-2]==board[2 if i==6 else i+2]>c: c=board[i-6]

            if c==-2:   third.append(i)
            elif c==-1: second.append(i)
            else:       third.append(i)
        return first+second+third

    def all_moves(self,master,subs,last_move,player):
        if last_move is not None and master[last_move[1]]==0 and 0 in subs[last_move[1]]:
            for i in self.sub_moves(subs[last_move[1]]):
                yield self.make_move(master,subs,[last_move[1],i],player)

        else:
            for j in range(9):
                if master[j]==0 and 0 in subs[j]:
                    for i in self.sub_moves(subs[j]):
                        yield self.make_move(master,subs,[j,i],player)

    def move(self,master,subs,last_move):
        return self.alphabeta(master,subs,last_move, self.depth, -10000, 10000, True)[1]

    def run(self,input):
        result = self.move(*self.parse(input))
        if result:
            return str(result[0])+str(result[1])

def print_board(subs,player):
    string = ""
    for row in range(9):
        for sub in subs[row/3*3:row/3*3+3]:
            for c in sub[row%3*3:row%3*3+3]:
                string += "-XO"[c*(1 if player=='X' else -1)]
            string += ' '
        if row%3 == 2:
            string += '\n'
        string += '\n'
    print string

def to_string(master,subs,last_move,player):
    string = player+'\n'
    for sub in subs:
        for c in sub:
            string += "-XO"[c*(1 if player=='O' else -1)]
        string += '\n'
    for c in master:
        string += "-XO"[c*(1 if player=='O' else -1)]
    string += '\n'+str(last_move[0])+str(last_move[1])
    return string


import sys
command = '\n'.join(sys.argv[1:])
print DepthPlayer(8).run(command)

Para executá-lo, você pode simplesmente fazê-lo python Depth.py <input>, embora eu sugira o uso, pypypois ele acelera notavelmente.

Além disso, não sei o quão rápido é o seu sistema, mas você pode modificar o primeiro argumento DepthPlayerno final para que seja mais alto se ele ainda puder ser executado no tempo especificado (no meu sistema, ele concluiu quase todas as coisas muito rapidamente com uma profundidade de 7 ou 8, mas houve alguns casos que estavam próximos ou acima de um segundo, então eu o configurei em 6 para ser seguro).

KSab
fonte
python sys.argvnão retorna uma string separada por nova linha. Ele fornece uma lista de seqüências de caracteres neste formato: ['Depth.py', 'X', '---------', '---------', ...]Eu a corrigi editando as duas últimas linhas, command = '\n'.join(sys.argv[1:]) print DepthPlayer(6).run(command)espero que você não se importe.
DJMcMayhem
@DJMcMayhem Oh, obrigado, eu não testei a última linha.
KSab #
2

Java, ingênuo

Se possível, vence. Caso contrário, impede que um oponente vença.

import java.util.Arrays;

public class Naive {

    public static void main(String[] args) {

        char[][] board = new char[9][9];
        for (int i = 0; i < 9; i++) {
            board[i] = args[i + 1].toCharArray();
        }
        char[] metaBox = args[10].toCharArray();

        char a = args[0].charAt(0),
                b = (char) ('X' + 'O' - a);

        int legalBox = args[11].charAt(1) - '0';
        boolean legalAnywhere = legalBox == 'x' - '0';
        if (!legalAnywhere) {
            if (wins(board[legalBox], 'X') || wins(board[legalBox], 'O')) {
                legalAnywhere = true;
            }
        }
        a:
        if (!legalAnywhere) {
            for (int i = 0; i < 9; i++) {
                if (board[legalBox][i] == '-') {
                    break a;
                }
            }
            legalAnywhere = true;
        }

        if (legalAnywhere) {
            chooseMove(board, metaBox, a, b);
        } else {
            chooseMove(board, metaBox, a, b, legalBox);
        }
    }

    static boolean canWinWith(char[] box, char c) {
        for (int i = 0; i < 9; i++) {
            if (wins(box, i, c)) {
                return true;
            }
        }
        return false;
    }

    static boolean wins(char[] box, int move, char c) {
        char[] copy = Arrays.copyOf(box, 9);
        copy[move] = c;
        return wins(copy, c);
    }

    static boolean wins(char[] box, char c) {
        return (box[0] == c && box[1] == c && box[2] == c)
               || (box[3] == c && box[4] == c && box[5] == c)
               || (box[6] == c && box[7] == c && box[8] == c)
               || (box[0] == c && box[3] == c && box[6] == c)
               || (box[1] == c && box[4] == c && box[7] == c)
               || (box[2] == c && box[5] == c && box[8] == c)
               || (box[0] == c && box[4] == c && box[8] == c)
               || (box[2] == c && box[4] == c && box[6] == c);
    }

    static void endWith(int box, int i) {
        System.out.println("" + box + i);
        System.exit(0);
    }

    private static void chooseMove(char[][] board, char[] metaBox, char a, char b, int legalBox) {
        for (int i = 0; i < 9; i++) {
            if (wins(board[legalBox], i, a) && board[legalBox][i] == '-') {
                endWith(legalBox, i);
            }
        }
        for (int i = 0; i < 9; i++) {
            if (wins(board[legalBox], i, b) && board[legalBox][i] == '-') {
                endWith(legalBox, i);
            }
        }
        for (int i = 0; i < 9; i++) {
            if (board[legalBox][i] == '-') {
                if (!canWinWith(board[i], b)) {
                    endWith(legalBox, i);
                }
            }
        }
        for (int i = 0; i < 9; i++) {
            if (board[legalBox][i] == '-') {
                endWith(legalBox, i);
            }
        }
        throw new RuntimeException("No move chosen!");
    }

    private static void chooseMove(char[][] board, char[] metaBox, char a, char b) {
        for (int box = 0; box < 9; box++) {
            for (int i = 0; i < 9; i++) {
                if (wins(board[box], i, a) && board[box][i] == '-') {
                    endWith(box, i);
                }
            }
        }
        for (int box = 0; box < 9; box++) {
            for (int i = 0; i < 9; i++) {
                if (wins(board[box], i, b) && board[box][i] == '-') {
                    endWith(box, i);
                }
            }
        }
        for (int box = 0; box < 9; box++) {
            for (int i = 0; i < 9; i++) {
                if (board[box][i] == '-') {
                    if (!canWinWith(board[i], b)) {
                        endWith(box, i);
                    }
                }
            }
        }
        for (int box = 0; box < 9; box++) {
            for (int i = 0; i < 9; i++) {
                if (board[box][i] == '-') {
                    endWith(box, i);
                }
            }
        }
        throw new RuntimeException("No move chosen!");
    }
}
Ypnypn
fonte
Você terá que me perdoar por ser um java noob, mas como eu executo isso no diretório pai? Eu tenho Naive.classem um diretório nomeado naiveBotdentro do diretório principal.
DJMcMayhem
@DJMcMayhem Eu não tenho acesso a um Mac, mas no Windows, você pode simplesmente executar o java Naive <args>comando, assumindo que as variáveis ​​de ambiente incluem o ponteiro para C:\Program Files\Java\jdk1.8.0\bin. Eu espero que isso ajude.
Ypnypn
Tudo bem, eu vou descobrir.
DJMcMayhem
@DJMcMayhem Se você ainda não descobriu, é java -classpath naiveBot Naive;);
CommonGuy 13/15
@Ypnypn Se legalAnywherefor verdade, seu envio falhará porque você tenta usar os painéis que já foram ganhos por um jogador.
CommonGuy
2

Python 2, MiddleBot

MiddleBot gosta do meio. Antes que o jogo central (4) seja vencido, ele tentará agarrar o quadrado central de tantos jogos quanto possível, forçando o oponente de volta ao jogo do meio repetidas vezes.
Uma vez feito isso, ele tenta ganhar todos os jogos que conseguir, ou apenas preenche o primeiro espaço disponível, se não (precisa trabalhar no seu jogo final, eu acho)

from random import randint
import sys
command_in = '\n'.join(sys.argv[1:])
class MiddleBot:

    def scan_in(self,the_game):

        lines = the_game.split('\n')
        self.us = lines[0]
        if self.us == 'X':
            self.them = 'O'
        else:
            self.them = 'X'
        self.games = lines[1:10]
        self.metagame = lines[10]
        self.last_move = lines[11]

        try:
            self.sub_board = int(self.last_move[1])
        except ValueError:
            self.sub_board = self.last_move[1]

    def empty(self,game,target):
        if self.games[int(game)][int(target)] == '-':
            self.emptycell = 1
        else: self.emptycell = 0

    def empty_fill(self,game):
        #checks for next empty space, fills it
        for k in xrange(0,8):
            self.empty(game,k)
            if self.emptycell == 1:
                self.first_empty_space = k
                break
            if self.emptycell == 0:
                game = randint(0,8)
                self.first_empty_space = 4


    def aim_for_target(self,game,target):
        if self.games[int(game)][int(target)] == '-':
            self.move = `game` + `target`
        else:
            self.empty_fill(game)
            self.move = `game` + `self.first_empty_space`


    #define all win conditions        
    win = [0]*8            
    win[0] = [0,1,2]
    win[1] = [3,4,5]
    win[2] = [6,7,8]
    win[3] = [0,3,6]
    win[4] = [1,4,7]
    win[5] = [2,5,8]
    win[6] = [0,4,8]
    win[7] = [2,4,6]

    #check if current board state is one move away from 'us' winning
    def aim_for_win(self,game):
            for k in xrange(0,len(self.win)):
                if self.games[self.sub_board][self.win[k][0]] == self.games[self.sub_board][self.win[k][1]] == self.us:
                    self.empty(self.sub_board,self.win[k][2])
                    if self.emptycell == 1:
                        self.move = `self.sub_board`+`self.win[k][2]`
                    else:
                        self.empty_fill(self.sub_board)
                        self.move = `self.sub_board`,`self.first_empty_space`
                elif self.games[self.sub_board][self.win[k][0]] == self.games[self.sub_board][self.win[k][2]] == self.us:
                    self.empty(self.sub_board,self.win[k][1])
                    if self.emptycell == 1:
                        self.move = `self.sub_board`+`self.win[k][1]`
                    else:
                        self.empty_fill(self.sub_board)
                        self.move = `self.sub_board`+`self.first_empty_space`
                elif self.games[self.sub_board][self.win[k][1]] == self.games[self.sub_board][self.win[k][2]] == self.us:
                    self.empty(self.sub_board,self.win[k][0])
                    if self.emptycell == 1:
                        self.move = `self.sub_board`+`self.win[k][0]`
                    else:
                        self.empty_fill(self.sub_board)
                        self.move = `self.sub_board`+`self.first_empty_space`
                else:
                    self.empty_fill(self.sub_board)
                    self.move = `self.sub_board`+`self.first_empty_space`


    def play(self):
        #If the middle board is not won, aim for the middle square of each board
        if self.metagame[4] == '-':
            if self.sub_board == 4 or self.sub_board == 'x':
                self.aim_for_target(4,4)
            else:
                self.aim_for_target(self.sub_board,4)
        else:
            #once the middle board is won, pretty much plays randomly, aiming to win if it can, otherwise just filling the first empty space in each subgame
            played = 0
            if self.sub_board == 'x':
                self.sub_board = randint(0,8)
            while played == 0:
                if self.metagame[int(self.sub_board)] == '-':
                    self.aim_for_win(self.sub_board)
                    played = 1
                else:
                    self.sub_board = randint(0,8)
        return self.move

    def run(self,game_board):
        self.scan_in(game_board)
        self.play()
        return self.move

print MiddleBot().run(command_in)      

Para executá-lo, python MiddleBot.py <input>ele parece feliz em menos de um segundo para mim, então espero que seja para você também

LogicianWithAHat
fonte
Tudo corre bem, mas, para sua informação, ele trava quando o último movimento é 'xx', o que acontece no início e toda vez que um bot faz um movimento inválido.
DJMcMayhem
Opa! Deve ser corrigido agora. Esqueceu de testar o caso 'xx' nessa iteração, desculpe!
LogicWithAHat
Também fez uma edição - que iria falhar se uma placa tinha sido preenchido sem um vencedor e foi convidado a jogar lá
LogicianWithAHat
0

É melhor jogar meu próprio bot na mistura.

python 2, goodRandomBot

import sys
from random import choice

args = sys.argv
if len(args) < 13:
    print ("I can't work with this!\n")
    sys.exit()

whoAmI = args[1];
masterBoard = list(args[11])
board = []
for i in range(2, 11):
    board.append(list(args[i]))

oppMove = args[12]

def findAllValidMoves(board, masterBoard):
    validMoves = []
    for row in range(9):
        if masterBoard[row] != '-':
            continue
        for col in range(len(board[row])):
            if board[row][col] == '-':
                validMoves.append(str(row) + str(col))
    return validMoves

validMoves = []
if oppMove == "xx" or masterBoard[int(oppMove[1])] != "-":
    validMoves = findAllValidMoves(board, masterBoard)    

else:
    row = int(oppMove[1])
    for col in range(len(board[row])):
        if board[row][col] == '-' and masterBoard[row] == "-":
            validMoves.append(str(row) + str(col))

if (validMoves == []):
    validMoves = findAllValidMoves(board, masterBoard)

print choice(validMoves)

Este bot não se importa para onde se move, desde que seja uma jogada válida. Seleciona aleatoriamente todos os movimentos válidos e faz uma média de 0movimentos inválidos.

DJMcMayhem
fonte