¡Juguemos Meta tic-tac-toe!

38

¡Vamos a jugar un juego de Meta tic-tac-toe!

Este es un torneo del de Meta tic-tac-toe. Las reglas de Meta tic-tac-toe son las siguientes:

  1. Se aplican todas las reglas regulares de tic-tac-toe.

  2. Hay nueve tableros dispuestos para hacer un tablero maestro. Al igual que:

    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 
    

    el tablero 0 se refiere al tablero superior izquierdo, el tablero 1 se refiere al tablero medio superior ... así

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

    Si digo tablero 3, mosaico 4, eso significa el mosaico central del tablero en el centro a la izquierda.

  3. Solo puedes moverte en una de las tablas más pequeñas.

  4. Si gana uno de los tableros más pequeños, ese tablero completo cuenta como su ficha.

  5. Si uno de los tableros se llena antes de que cualquiera de los bots lo haya ganado, cuenta como una ficha de nadie.

  6. ¡Quien gane el tablero maestro gana!

Sin embargo, hay un giro importante. Digamos que voy en el tablero 7, mosaico 2. Eso significa que en tu turno, solo puedes ir en el tablero 2. Luego digamos que vas en el tablero 2, mosaico 5. Ahora en mi turno, solo puedo ir en el tablero 5. Digamos que el tablero 1 está lleno. (No quedan más lugares, o uno de nosotros ya ha ganado el tablero 1) Ahora si voy en el tablero 5, mosaico 1, puedes ir a cualquiera de los tableros que quieras.

Estas reglas pueden considerarse como:

  1. Debes jugar en el tablero correspondiente a la posición que jugó el jugador anterior.
    • Si X juega en el tablero 2, ficha 5; O debes jugar en el tablero 5
  2. Si el tablero objetivo está lleno (empate) o ya tiene un vencedor, el siguiente movimiento no tiene restricciones.
  3. No se puede jugar con un tablero con un ganador , incluso en un movimiento sin restricciones.

Si esto es un poco confuso, puede probarlo en línea aquí. (asegúrese de cambiar de "las primeras fichas ganadas" a "3 fichas seguidas")

Ahora aquí están las reglas del desafío.

  1. Debes escribir un bot que juegue este juego.

  2. Bot 1 es Xs, y llega primero. Se llamará con estos argumentos de línea de comando (sin las cosas entre paréntesis):

    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)
    

    El primer personaje representa quién es el bot. En este caso, el bot 1 juega como X. Las siguientes 9 líneas se refieren a los 9 tableros. La undécima línea se refiere a la placa maestra. El "xx" es el último movimiento. Ahora, bot1 debe imprimir dos números entre 0 y 8. El número 1 es el tablero en el que se está moviendo su bot, y el número 2 es el mosaico en dicho tablero. El controlador realizará un seguimiento de este movimiento. Digamos que el bot 1 imprime 38. Ahora el tablero se verá así:

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

    y bot2 se llamará con estos argumentos:

    O
    ---------
    --------- 
    --------- 
    --------X 
    --------- 
    --------- 
    --------- 
    --------- 
    --------- 
    ---------
    38
    
  3. Ahora el bot 2 debe moverse en el tablero 8 (porque bot1 colocó una x en la casilla 3). Digamos que bot2 imprime 84. Ahora el tablero se ve así.

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

    ahora se llamará bot1 con estos argumentos:

    X
    ---------
    --------- 
    --------- 
    --------X 
    --------- 
    --------- 
    --------- 
    --------- 
    ----0---- 
    ---------
    84
    
  4. Ahora bot1 debe moverse en el tablero 4. Sin embargo, bot1 es un pequeño bot travieso y decide moverse en el tablero 3. Imprime '30'. El tablero no cambia en absoluto. El bot maestro realiza un seguimiento de esto. Ahora se llamará a bot2 con estos argumentos:

    O
    ---------
    --------- 
    --------- 
    --------X 
    --------- 
    --------- 
    --------- 
    --------- 
    ----0---- 
    ---------
    xx
    
  5. Ahora el bot 2 puede ir a donde quiera (excepto 38 y 84, por supuesto). Esto continúa hasta que alguien gana 3 de los tableros maestros en una fila. Luego, hay un segundo enfrentamiento donde bot2 es X y llega primero.

  6. Esto se repite hasta que cada robot haya jugado todos los demás bot.

Tanteo

La puntuación funciona así:

El ganador de cada partido obtiene 100 + number of open spotspuntos. De esa manera, es más valioso si su bot gana rápidamente. Cada vez que tu bot realiza un movimiento no válido, pierde 1 punto. Si después de 250 rondas, ninguno de los bots ha ganado, cada bot pierde 10 puntos y pasamos a la siguiente ronda.


Todo se colocará en un directorio que contiene

  1. El controlador bot. Este es un programa C ++ que he escrito. Puede consultar el código fuente del bot del controlador aquí. Avíseme si ve algo que no está bien con el controlador.

  2. Un archivo de texto llamado instructions.txtEste archivo se verá así:

    [Total number of bots that are competing]
    
    [bot1Name] [bot1 command to run]
    [bot2Name] [bot2 command to run]
    ...
    
  3. Una carpeta para cada bot. Esta carpeta contendrá su programa (ya sea un script o un binario) y UN archivo de texto llamado data.txtque su bot puede leer y escribir lo que quiera.

Especificaciones técnicas y aclaraciones de reglas.

  • Cualquier bot que intente leer / escribir algo desde cualquier lugar que no esté dentro de su carpeta será expulsado del juego.

  • Su programa debe poder ejecutarse en un macbook con Yosemite. Los lenguajes actualmente compatibles son python (2.7.9 y 3.4.2), C / C ++, Objective-C, perl, ruby, bash, PHP, Java, C #, javascript y Haskell. Hay muchos más, pero estos son solo los que puedo pensar en este momento. Agregaré más a medida que pase el tiempo. Si desea competir en un idioma específico, envíeme un mensaje o comentario, y lo agregaré a la lista si es posible.

  • Si se gana un tablero, pero todavía hay espacio, aún no puede moverse a uno de los espacios abiertos.

  • Tenga en cuenta que el directorio de trabajo de su envío será el directorio que contiene el controlador y todos los demás bots, NO el directorio que contiene su bot.

  • Publique junto con el código de bot del controlador el comando correcto para compilar (si corresponde) y ejecutar su bot. La mayor parte de esto se hará desde el terminal OS X, que es bastante similar a un terminal de Linux.

  • Los bots deben completarse en menos de un segundo. Desafortunadamente, no soy lo suficientemente competente como para agregar un temporizador al bot del controlador. Sin embargo, cronometraré manualmente los bots.


Resultados!

Bueno, estaba en lo cierto. Olvidé hacer que el bot del controlador verifique si el masterBoard está lleno. Si el masterBoard está lleno, CADA movimiento no es válido, pero continúa llamando a los bots, lo que probablemente es la razón por la que hubo tantos movimientos no válidos. Lo tengo arreglado ahora. Aquí están los resultados oficiales con la versión más actual de todos los 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.

¡Prof Bot es el campeón reinante! Al menos, por ahora.

DJMcMayhem
fuente
Por otro lado, ¿alguna vez has mirado Fire and Ice (una versión pbem en gamerz.net )? Hay algunos elementos de tic-tac-toe ... aunque esto también me recordó al escriba .
9 me gusta y 40 vistas. ¡Estoy impresionado!
Loovjo
55
Es posible que desee poner un límite al tiempo de respuesta de los bots, o los bots pueden tomar 3 minutos por movimiento mientras busca todos los movimientos futuros posibles.
Logic Knight
1
He agregado algunas aclaraciones de reglas al bit sobre el próximo movimiento. Me preocupa el formato de datos y una de las reglas. Regla 5 de la primera sección: "Si uno de los tableros se llena, cuenta como mosaico de nadie". ¿Está lleno sin un ganador? es decir, si alguien gana el mosaico anteriormente, y se llena, ¿no es nadie el mosaico? Además, si los bots no tienen estado (parecen estar) con el estado pasado, ¿cómo se XXX000---transmite el ganador de un tablero que se transmite? ¿O es que 'nadie lo consigue a pesar de que O lo haya ganado primero'?
@MichaelT el ganador del tablero pasa a la línea 11. Editaré esta parte para que quede un poco más clara, sin embargo, su edición es incorrecta. "Si se gana un tablero, pero todavía hay espacio, aún no se puede mover a uno de los espacios abiertos".
DJMcMayhem

Respuestas:

5

Python 2.7, profundidad

Una implementación de poda alfa-beta sin nada demasiado elegante. Intenta ordenar movimientos de una manera menos ingenua para maximizar las eliminaciones alfa-beta. Probablemente intentaré acelerarlo, pero sinceramente, no sé qué tan competitivo puede ser Python si se trata de una cuestión de velocidad.

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 ejecutarlo, simplemente puede hacerlo python Depth.py <input>, aunque sugeriría usarlo pypyya que lo acelera notablemente.

Además, no sé qué tan rápido es su sistema, pero puede modificar el primer argumento al DepthPlayerfinal para que sea más alto si aún puede ejecutarse en el tiempo especificado (en mi sistema completó casi todas las cosas muy rápidamente con una profundidad de 7 u 8, pero hubo algunos casos que estaban cerca o por encima de un segundo, así que lo configuré en 6 para que sea seguro).

KSab
fuente
Python sys.argvno devuelve una cadena separada de nueva línea. Da una lista de cadenas en este formato: ['Depth.py', 'X', '---------', '---------', ...]lo arreglé editando las dos últimas líneas para esto command = '\n'.join(sys.argv[1:]) print DepthPlayer(6).run(command), espero que no te importe.
DJMcMayhem
@DJMcMayhem Oh, gracias, no probé esa última línea.
KSab
2

Java, ingenuo

Si es posible, gana. De lo contrario, evita que un oponente gane.

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
fuente
Tendrás que perdonarme por ser un novato de Java, pero ¿cómo ejecuto esto desde el directorio principal? Tengo Naive.classen un directorio nombrado naiveBotdentro del directorio principal.
DJMcMayhem
@DJMcMayhem No tengo acceso a una Mac, pero en Windows, solo puede ejecutar el java Naive <args>comando, suponiendo que las variables de entorno incluyan el puntero a C:\Program Files\Java\jdk1.8.0\bin. Espero que esto ayude.
Ypnypn
Muy bien, lo resolveré.
DJMcMayhem
@DJMcMayhem Si aún no lo resolvió, es java -classpath naiveBot Naive;)
CommonGuy
@Ypnypn Si legalAnywherees cierto, su envío falla porque intenta usar tableros que ya ha ganado un jugador.
CommonGuy
2

Python 2, MiddleBot

A MiddleBot le gusta el medio. Antes de que se gane el juego central (4), intentará agarrar el cuadrado central de tantos juegos como sea posible, forzando al oponente a regresar al juego del medio una y otra vez.
Una vez hecho esto, intenta ganar cualquier juego que pueda, o simplemente llena el primer espacio disponible si no (creo que es necesario trabajar en su juego tardío)

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 ejecutarlo, python MiddleBot.py <input>parece que felizmente corre menos de un segundo para mí, así que espero que también lo haga para ti

LogicianWithAHat
fuente
Todo funciona bien, pero para su información, se bloquea cuando el último movimiento es 'xx', que ocurre al principio y cada vez que un bot realiza un movimiento no válido.
DJMcMayhem
¡Uy! Debería arreglarse ahora. Olvidé probar el caso 'xx' en esa iteración, ¡lo siento!
LogicianWithAHat
También realizó una edición - se estrellaría si un directorio había sido llenado sin un ganador y se le pidió que jugar allí
LogicianWithAHat
0

También podría tirar mi propio bot en la mezcla.

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)

A este bot no le importa dónde se mueve, siempre que sea un movimiento válido. Selecciona aleatoriamente todos los movimientos válidos y realiza un promedio de 0movimientos no válidos.

DJMcMayhem
fuente