Давайте играть в мета-крестики-нолики!

38

Давайте играть в игру мета-крестики-нолики!

Это турнир по методу крестики-нолики в стиле « . Правила Meta Tic-Tac-Toe следующие:

  1. Все обычные правила крестики-нолики применяются.

  2. Для создания одной мастер-доски предусмотрено девять досок. Вот так:

    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 
    

    доска 0 относится к верхней левой доске, доска 1 относится к верхней средней доске ... вот так

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

    Если я скажу «доска 3», тайл 4, это означает центральную плитку доски в середине слева.

  3. Вам разрешено двигаться только на одной из небольших досок.

  4. Если вы выиграете одну из небольших досок, вся эта доска считается вашей плиткой.

  5. Если одна из досок заполняется до того, как один из ботов ее выиграл, она считается плиткой nobody.

  6. Тот, кто выигрывает мастерскую доску, побеждает!

Тем не менее, есть важный поворот. Скажем, я иду на доске 7, клетка 2. Это означает, что на вашем ходу вы можете идти только на доске 2. Затем, допустим, вы идете на доску 2, клетку 5. Теперь на моем ходу я могу пойти только на доске 5. Допустим, доска 1 заполнена. (Осталось больше мест, или один из нас уже выиграл 1-ю доску). Теперь, если я перейду на 5-ю доску, тайл 1, вы можете перейти на любую доску, какую захотите.

Эти правила можно рассматривать как:

  1. Вы должны играть на доске, соответствующей позиции, сыгранной предыдущим игроком.
    • Если Х играет на доске 2, тайл 5; О должен играть на доске 5
  2. Если целевая доска полна (ничья) или уже имеет победителя, следующий ход будет неограниченным.
  3. Доска с победителем не может быть разыграна, даже на ход без ограничений.

Если это немного сбивает с толку, вы можете попробовать это онлайн здесь. (не забудьте переключиться с «первых выигрышей плитки» на «3 плитки подряд»)

Теперь вот правила вызова.

  1. Вы должны написать бота, который играет в эту игру.

  2. Бот 1 - это Х, и он должен идти первым. Он будет вызываться с этими аргументами командной строки (без содержимого в скобках):

    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)
    

    Первый символ представляет, кто бот. В этом случае бот 1 играет за X. Следующие 9 строк относятся к 9 доскам. 11-я строка относится к основной доске. «Хх» - последний ход. Теперь бот1 должен напечатать два числа от 0 до 8. Номер 1 - доска, на которой движется ваш бот, а номер 2 - фишка на этой доске. Контроллер будет отслеживать этот ход. Допустим, бот 1 печатает 38. Теперь доска будет выглядеть так:

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

    и bot2 будет вызываться с этими аргументами:

    O
    ---------
    --------- 
    --------- 
    --------X 
    --------- 
    --------- 
    --------- 
    --------- 
    --------- 
    ---------
    38
    
  3. Теперь бот 2 должен двигаться на доске 8 (потому что бот1 поместил х в клетку 3). Допустим, бот2 печатает 84. Теперь доска выглядит следующим образом.

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

    теперь bot1 будет вызываться с этими аргументами:

    X
    ---------
    --------- 
    --------- 
    --------X 
    --------- 
    --------- 
    --------- 
    --------- 
    ----0---- 
    ---------
    84
    
  4. Теперь бот1 должен переместиться на доску 4. Однако бот1 - это непослушный маленький бот, и он решает переместиться на доску 3. Он печатает «30». Плата не меняется вообще. Мастер бот отслеживает это. Теперь bot2 будет вызываться с этими аргументами:

    O
    ---------
    --------- 
    --------- 
    --------X 
    --------- 
    --------- 
    --------- 
    --------- 
    ----0---- 
    ---------
    xx
    
  5. Теперь бот 2 может ходить куда угодно (кроме 38 и 84, конечно). Это продолжается до тех пор, пока кто-то не выиграет 3 из главных досок подряд. Затем идет второй матч, в котором bot2 - это X и он идет первым.

  6. Это повторяется до тех пор, пока каждый бот не сыграет с каждым другим ботом.

счет

Скоринг работает так:

Победитель каждого матча получает 100 + number of open spotsочки. Таким образом, более ценно, если ваш бот быстро побеждает. Каждый раз, когда ваш бот делает неверный ход, он теряет 1 очко. Если после 250 раундов ни один бот не выиграл, каждый бот теряет 10 очков, и мы переходим к следующему раунду.


Все будет помещено в каталог, который содержит

  1. Контроллер бот. Это программа на C ++, которую я написал. Вы можете посмотреть исходный код бота контроллера здесь. Пожалуйста, дайте мне знать, если вы видите что-то не так с контроллером.

  2. Текстовый файл с именем instructions.txtЭтот файл будет выглядеть примерно так:

    [Total number of bots that are competing]
    
    [bot1Name] [bot1 command to run]
    [bot2Name] [bot2 command to run]
    ...
    
  3. Папка для каждого бота. Эта папка будет содержать вашу программу (будь то скрипт или бинарный файл) и ОДИН текстовый файл с именем, data.txtкоторый ваш бот может читать и писать, что хочет.

Технические характеристики и разъяснения правил

  • Любой бот, который пытается что-то прочитать / написать из любой точки, находящейся вне его папки, будет исключен из игры.

  • Ваша программа должна быть в состоянии работать на MacBook под управлением Yosemite. В настоящее время поддерживаются следующие языки: Python (2.7.9 и 3.4.2), C / C ++, target-C, perl, ruby, bash, PHP, Java, C #, javascript и Haskell. Их намного больше, но это только те, о которых я могу думать прямо сейчас. Я добавлю больше со временем. Если вы хотите соревноваться на определенном языке, напишите мне или оставьте комментарий, и я добавлю его в список, если это возможно.

  • Если доска выиграна, но все еще есть место, вы все равно не сможете перейти в одно из открытых мест.

  • Обратите внимание, что рабочим каталогом вашего представления будет каталог, содержащий контроллер и все остальные боты, а НЕ каталог, содержащий ваш бот.

  • Пожалуйста, напишите вместе с кодом вашего контроллера бота правильную команду для компиляции (если применимо) и запуска вашего бота. Большая часть этого будет сделана из терминала OS X, который довольно похож на терминал Linux.

  • Боты должны завершить менее чем за секунду. К сожалению, я не достаточно компетентен, чтобы добавить таймер в бот контроллера. Тем не менее, я буду вручную время ботов.


Полученные результаты!

Ну, я был прав. Я забыл проверить бот контроллера, чтобы проверить, заполнен ли masterBoard. Если masterBoard заполнен, то КАЖДЫЙ ход недействителен, но он продолжает вызывать ботов, поэтому, вероятно, было так много недействительных ходов. Я исправил это сейчас. Вот официальные результаты с самой последней версией всех ботов.

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 - действующий чемпион! По крайней мере, прямо сейчас.

DJMcMayhem
источник
Кроме того, вы когда-нибудь смотрели на Fire and Ice (версия pbem на gamerz.net ) - там есть некоторые элементы крестики-нолики ... хотя это также напоминало мне писца .
9 лайков и 40 просмотров. Я впечатлен!
Loovjo
5
Вы можете захотеть ограничить время ответа ботов, или боты могут занять 3 минуты на ход при поиске всех возможных будущих ходов.
Логика Найт
1
Я добавил некоторые пояснения к правилу о следующем шаге. Меня беспокоит формат данных и одно из правил. Правило 5 из первого раздела: «Если одна из досок становится заполненной, она считается плиткой ничей». Заполнено ли это без победителя? то есть, если кто-то выиграл плитку ранее, и она стала заполненной, то это плитка ничей? Кроме того, если боты не имеют состояния (они кажутся) с переданным состоянием, как передается победитель XXX000---получаемой доски ? или это «никто не получает это, несмотря на то, что О выиграл это первым»?
@MichaelT победитель доски попадает на 11-ю строчку. Я отредактирую эту часть, чтобы сделать ее немного более понятной, однако ваше редактирование неверно. «Если доска выиграна, но все еще есть место, вы все равно не сможете выйти на одно из открытых мест».
DJMcMayhem

Ответы:

5

Python 2.7, глубина

Реализация обрезки альфа-бета без каких-либо излишеств. Он пытается упорядочить ходы менее наивным способом, чтобы максимизировать устранение альфа-бета. Я, вероятно, постараюсь ускорить его, но, честно говоря, я не знаю, насколько конкурентоспособным может быть Python, если говорить о скорости.

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)

Чтобы запустить его, вы можете просто сделать python Depth.py <input>, хотя я бы посоветовал использовать pypyего, так как он заметно ускоряется.

Также я не знаю, насколько быстро работает ваша система, но вы можете изменить первый аргумент DepthPlayerв самом конце, чтобы он был выше, если он все еще может работать в указанное время (в моей системе он завершил почти все вещи очень быстро с глубиной 7 или 8, но было несколько случаев, которые были около или выше секунды, поэтому я установил его на 6, чтобы быть в безопасности).

KSab
источник
Python sys.argvне возвращает разделенную строкой строку. Он дает список строк в этом формате: ['Depth.py', 'X', '---------', '---------', ...]я исправил это, отредактировав последние две строки, command = '\n'.join(sys.argv[1:]) print DepthPlayer(6).run(command)надеюсь, вы не против.
DJMcMayhem
@DJMcMayhem О, спасибо, я не проверял эту последнюю строку.
KSab
2

Ява, Наивный

Если возможно, это выигрывает. В противном случае это препятствует победе противника.

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
источник
Вы должны простить меня за то, что я Java-нуб, но как мне запустить это из родительского каталога? У меня есть Naive.classв каталоге с именем naiveBotвнутри основного каталога.
DJMcMayhem
@DJMcMayhem У меня нет доступа к Mac, но в Windows вы можете просто запустить java Naive <args>команду, предполагая, что переменные окружения содержат указатель на C:\Program Files\Java\jdk1.8.0\bin. Надеюсь, это поможет.
Ypnypn
Хорошо, я разберусь.
DJMcMayhem
@DJMcMayhem Если вы еще не поняли, это java -classpath naiveBot Naive;)
CommonGuy
@Ypnypn Если legalAnywhereэто правда, ваша подача не удалась, потому что вы пытаетесь использовать доски, которые уже выиграл игрок.
CommonGuy
2

Python 2, MiddleBot

MiddleBot любит середину. До того, как центральная игра (4) выиграна, он попытается захватить центральную клетку как можно большего количества игр, заставляя противника снова и снова возвращаться в среднюю игру.
Как только это сделано, он пытается выиграть все игры, которые может, или просто заполняет первое доступное пространство, если нет (думаю, нужно работать над его поздней игрой)

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)      

Чтобы запустить его, мне python MiddleBot.py <input>кажется, что он с радостью бежит за секунду, так что, надеюсь, это будет и для вас.

LogicianWithAHat
источник
Все работает нормально, но, к вашему сведению, происходит сбой, когда последний ход - «хх», что происходит в начале, и каждый раз, когда бот делает неверный ход.
DJMcMayhem
К сожалению! Должно быть исправлено сейчас. Забыл проверить случай 'xx' в этой итерации, извините!
LogicianWithAHat
Также внес изменения - он вылетел бы, если бы доска была заполнена без победителя, и ее попросили сыграть там
LogicianWithAHat
0

С таким же успехом можно добавить своего собственного бота.

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)

Этому боту все равно, куда он движется, если он действителен. Выбирает случайным образом из всех допустимых ходов и делает среднее число 0недействительных ходов.

DJMcMayhem
источник