Бефунге, 181 168 байт
# Checks if the given player has 3 in a row and returns 1 if so
def evaluate(board,turn):
for i, d in (0,1),(3,1),(6,1),(0,3),(1,3),(2,3),(0,4),(2,2):
if board[i]==b[i+d]==b[i+2*d]==turn: return 1
# Negamax recursive function
def negamax(board, turn, depth=8):
# Checks if player or opponent has won
if evaluate(board,turn): return 0,1
if evaluate(board,-turn): return 0,-1
# Checks if board is full
if all(board): return 0,0
# Goes through all possible moves in the board (available spots)
best_score = -2
for index in range(9):
if board[index]==0:
# Make the move, evaluate position, and unmake the move
board[index] = turn
score = -negamax(board, -turn, depth-1)[1]
board[index] = 0
# If score was better than previous best score, update best score and move
if score > best_score:
best_score, best_move = score, index
# Return best move and score after it has gone through all possibilities
return best_move, best_score
Позиции на доске пронумерованы от 1 до 9. По умолчанию вы получаете первый ход, но если вы хотите, чтобы компьютер делал первый ход, вы можете просто ввести 0 для своего первого хода. Когда вы сделаете ход, компьютер ответит числом, обозначающим ваш ход.
Нет никаких проверок, позволяющих убедиться, что вы не ввели правильный ход, а также нет проверок, позволяющих узнать, выиграл ли кто-нибудь или проиграл. Как только ходов больше не будет, программа просто переходит в бесконечный цикл.
Проверить это онлайн немного сложно, так как онлайн-переводчиков с интерактивным вводом нет. Однако, если вы заранее знаете, какие ходы собираетесь предпринять (что предполагает, что вы знаете, как компьютер отреагирует), вы можете протестировать TIO с заранее запрограммированными ходами.
Пользователь играет первым: Попробуйте онлайн!
Компьютер играет первым: Попробуйте онлайн!
Чтобы было легче видеть, что происходит, у меня также есть версия, которая выводит доску между ходами.
Пользователь играет первым: Попробуйте онлайн!
Компьютер играет первым: Попробуйте онлайн!
Обратите внимание, что вам придется дождаться истечения времени ожидания TIO, прежде чем вы сможете увидеть результаты.
Объяснение
Доска хранится в области памяти Befunge в виде плоского массива из 9 значений с индексами от 1 до 9. Это позволяет нам использовать нулевое смещение как особый случай «нет хода», когда мы хотим, чтобы компьютер играл первым. Ходы игрока сохраняются как 4, а перемещения компьютера — как 5. Для начала все позиции инициализируются как 32 (по умолчанию в памяти Befunge), поэтому всякий раз, когда мы получаем доступ к доске, мы модифицируем 8, поэтому мы возвращаемся либо 0, 4. или 5.
Учитывая такое расположение, если мы суммируем значения любых трех позиций на доске, мы знаем, что компьютер находится на расстоянии одного хода от победы, если сумма равна 10, игрок находится на расстоянии одного хода от победы, если сумма равна 8, а Позиции распределяются между компьютером и игроком (но одна позиция остается свободной), если общее количество равно 9.
Вся наша стратегия основана на этой концепции. У нас есть программа, которая принимает список троек, обозначающих наборы из трех позиций на доске, мы вычисляем сумму этих позиций, и если сумма равна определенной сумме, компьютер переходит к любой из позиций в наборе, которая свободна.
Основной список троек, которые мы проверяем, — это выигрышные комбинации (1/2/3, 1/5/9, 1/4/7 и т. д.). Сначала мы ищем в общей сложности 10 (компьютер вот-вот выиграет), а затем в общей сложности 8 (игрок вот-вот выиграет, и нам нужно заблокировать этот ход). Менее очевидно, что мы также проверяем общее количество 9 (если у игрока и компьютера есть одна из позиций, то хорошей стратегией для компьютера будет занять третью).
Перед этим последним сценарием еще один стратегический ход, который мы делаем, — это проверка всех угловых сетов (1/2/4, 2/3/6 и т. д.), а также двух противоположных угловых комбинаций (1/8/9 и 3). /7/8). Если сумма любой из этих комбинаций равна 8, т. е. игрок занял две позиции, то для компьютера будет хорошей стратегией занять оставшуюся свободную позицию.
Наконец, есть два особых хода. Во-первых, мы всегда стараемся занять центральную позицию перед любым другим ходом. Это достигается с помощью той же процедуры, что и все остальные наши ходы, просто передавая одну тройку 5/5/5 и целевую сумму 0. Кроме того, если все остальные тесты не смогли найти ход, мы пытаемся взять один из верхних углов в крайнем случае. Опять же, это просто достигается путем проверки троек 1/1/1 и 3/3/3 с целевой суммой 0.
Я не думаю, что это обязательно идеальная стратегия — могут быть игры, нарисованные компьютером, в которых потенциально можно было бы выиграть — но она достаточно хороша, чтобы никогда не проиграть матч. Я запустил тестовый сценарий, который пытался сыграть против компьютера все возможные ходы, и для каждой допустимой последовательности ходов компьютер либо выигрывал, либо играл вничью.