Крестики, нет нолики

10

Все понимают, что Tic Tac Toe - решенная игра. Тем не менее, версия Misère only-X предоставляет интересную альтернативу.

В этой версии игры оба игрока играют X на доске и стараются не делать три подряд. Если вы хотите узнать больше об этом, у Numberphile есть хорошее видео об этой концепции.

Учитывая доску Misère Crosses, играйте оптимально.

Доска состоит из трех строк по три символа в каждой, которые являются Xили . Таким образом:

X X
X  
 XX

это действительная доска. Вы можете использовать это в любом удобном формате, если ваш вход и выход используют одинаковый формат. Форматы включают (но не ограничиваются ими): многострочную строку (с необязательным завершающим переводом строки); 2D массив символов, которые являются Xили ; 1D-сплющенный массив логических значений, представляющих, была ли проиграна каждая позиция.

Оптимальный ход - это тот, который гарантирует, что вы выиграете, продолжая играть оптимально, или продлевает ваш проигрыш как можно дольше и определяется следующими правилами:

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

[ПРИМЕЧАНИЕ: в одном случае доказано, что это неоптимально, но вы все равно должны использовать этот алгоритм.]

Вы можете предположить, что все ваши предыдущие ходы были оптимальными. Таким образом, первый пример платы не является допустимым вводом. Ход вашего оппонента может быть или не быть оптимальным.

Если игра закончилась (т. Е. Была сделана тройка подряд), поведение не определено.

Поскольку это , выигрывает самый короткий ответ в байтах!

Один из возможных путей, используя только оптимальные ходы, заключается в следующем:

[   ]  [   ]  [X  ]  [X  ]  [X  ]  [X  ]  [XX ]
[   ]->[ X ]->[ X ]->[ XX]->[ XX]->[ XX]->[ XX]
[   ]  [   ]  [   ]  [   ]  [ X ]  [XX ]  [XX ]

Вот возможные входные данные, исходящие от противника с использованием неоптимальных ходов:
(Обратите внимание, что только левые доски в этом списке являются действительными входными данными.)

[X  ]  [X  ]
[   ]->[   ]
[   ]  [  X]

[XX ]  [XX ]
[   ]->[   ]
[  X]  [ XX]

[XX ]  [XX ]
[X  ]->[X X]
[ XX]  [ XX]
CAD97
источник
Какие форматы ввода и вывода? Я предполагаю, что доска воспринимается как массив или строка? Однако это не дает информации о последнем шаге, поэтому мой следующий вопрос.
Уровень Река St
1
Стратегия «играть против последней игры оппонента» предполагает либо знание истории ходов вашего оппонента, либо то, что вы ранее следовали этой стратегии, т.е. не унаследовали доску, такую ​​как, .XX\nX..\nX..например. Должны ли мы рассмотреть вопрос о наследовании таких плат?
Уровень Река St
@LevelRiverSt Как написано: «Вы можете предположить, что все ваши предыдущие ходы были оптимальными», так что доска будет неверным вводом. Вы можете принимать входные данные в любом формате, который вам нравится, но многострочная строка, такая как ваш пример, будет иметь «значение по умолчанию»: я просто не хочу ограничивать кого-либо в необходимости разбора строки, когда логика перемещения является точкой соревнование.
CAD97

Ответы:

3

Ruby, Rev B 121 байт

Представление является анонимной функцией, минус f=. Показано в тестовой программе для иллюстрации использования.

f=->n{["~mK)\7","}uYwQO"][l=n%2].bytes{|t|9.times{|i|(m=n|1<<i)==n||8.times{|j|m/2*257>>j&255==126-t&&t+j%2!=119&&l=m}}}
l}

puts g=f[gets.to_i]
puts
[7,6,5,
 8,0,4,
 1,2,3].each{|i|print g>>i&1; puts if i/3==1}

2 байта сохраняются, делая центральный квадрат наименьшим значащим битом вместо самого старшего бита (удаляем /2вместо вместо %256.) Оставшиеся сбережения путем реорганизации таблицы допустимых ходов. Организация в виде центрального квадрата свободного / занятого вместо общего числа X позволяет провести более простой тест. Кроме того, теперь в массиве только 2 строки, поэтому %w{string1 string2}синтаксис отброшен в пользу ["string1","string2"]синтаксиса. Это позволяет \7включать непечатаемый символ , что, в свою очередь, позволяет использовать более простую кодировку: 126-tвместо (36-t)%120.

Ruby, Rev A 143 байта

->n{l=r=("%b"%n).sum%8
%w{$ %5 - I+Wy Q S#}[r].bytes{|t|9.times{|i|(m=n|1<<i)==n||8.times{|j|m%256*257>>j&255==(t-36)%120&&t+j%2!=43&&l=m}}}
l}

Это анонимная функция. Формат ввода / вывода был оставлен открытым, поэтому я выбрал 9-битное двоичное число. бит 512 представляет центр, а остальные биты закручиваются вокруг него (бит 1 считается углом).

Возможных входных данных намного больше, чем допустимых выходных, поэтому алгоритм должен попробовать все ходы и найти тот, который соответствует приемлемому выходному шаблону. Приемлемые шаблоны вывода для каждого числа X жестко закодированы.

Информация о центральном квадрате удаляется, а оставшиеся 8 бит умножаются на 257 для их дублирования. Этот шаблон затем поворачивается мимо приемлемых шаблонов путем смещения прав.

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

Учитывая стратегию «движения рыцарей», не имеет значения, повернута ли модель на 45 градусов или нет. Версия без гольфа следует стратегии перемещения коней и, следовательно, не нуждается в различении угловых квадратов и краевых граней: в любом случае следует избегать трех подряд.

Тем не менее, я обнаружил, что это не всегда лучшая стратегия, поскольку есть следующий трюк. Если ваш противник идет первым и занимает центр, он должен победить. Но на своем втором ходу он делает ошибку, позволяя вам сделать квадрат 2х2, вы должны взять его, так как это заставит его сделать три подряд. Это реализовано в версии для гольфа. В этом случае требуется немного дополнительного кода, чтобы различить три X в углу (заставить противника проиграть) и 3 X вдоль одного края (немедленное самоубийство).

Неуправляемый в тестовой программе

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

В версии для игры в гольф стол слегка модифицирован, [[0],[1,17],[9],[37,7,51,85],[45],[47,119]]чтобы реализовать немного другое поведение для случая r=3. Затем он сжимается в печатный ASCII (требующий декодирования (t-36)%120). В случае записи 7 таблицы требуется дополнительный бит логики, чтобы различать три X в углу и три X вдоль края:&&t+j%2!=43

f=->n{l=r=("%b"%n).sum%8                                      #convert input to text, take character checksum to count 1's(ASCII 49.) 
                                                              #0 is ASCII 48, so %8 removes unwanted checksum bloat of 48 per char.
                                                              #l must be initialised here for scoping reasons.
  [[0],[1,17],[9],[11,13,37,51,85],[45],[47,119]][r].each{|t| #according to r, find the list of acceptable perimeter bitmaps, and search for a solution.
    9.times{|i|(m=n|1<<i)==n||                                #OR 1<<i with input. if result == n, existing X overwritten, no good.
                                                              #ELSE new X is in vacant square, good. So.. 
      8.times{|j|m%256*257>>j&255==t&&l=m}}                   #%256 to strip off middle square. *257 to duplicate bitmap.
                                                              #rightshift, see if pattern matches t. If so, write to l
  }
l}                                                            #return l (the last acceptable solution found) as the answer.

#call function and pretty print output (not part of submission)
puts g=f[gets.to_i]
puts
[6,7,0,
 5,8,1,
4,3,2].each{|i|print g>>i&1; puts if i<3}

Вывод тестовой программы

Это то, что происходит, когда компьютер играет сам.

C: \ Users \ steve> ruby ​​tictac.rb
0
256

000
010
000

C: \ Users \ steve> ruby ​​tictac.rb
256
384

010
010
000

C: \ Users \ steve> ruby ​​tictac.rb
384
400

010
010
100

C: \ Users \ steve> ruby ​​tictac.rb
400
404

010
010
101

C: \ Users \ steve> ruby ​​tictac.rb
404
436

010
110
101

C: \ Users \ steve> ruby ​​tictac.rb
436
444

010
110
111

ИГРА АНАЛИЗ ИГРАЕТ ПЕРВЫЙ

Это на самом деле очень просто и линейно.

При первой игре средний квадрат всегда будет занятым первым.

г = 0

...  binary representation 0
.X.
...

г = 2

X..  binary representation 1001=9 
.XX
...

г = 4

X.. binary representation 101101=45
.XX
XX.

Есть только один способ (с точностью до симметрии), чтобы пять X, включая средний квадрат на доске, не были закончены. В среднем квадрате есть X, по одному на каждой диагонали (под углом 90 градусов друг к другу) и по одному на каждой горизонтальной / вертикальной центральной линии (под углом 90 градусов друг к другу). Поскольку весь край не может быть занят, вышеуказанное является единственным договоренность возможна. Другой игрок должен проиграть на следующем ходу.

ИГРОВОЙ АНАЛИЗ ИГРАЕТ ВТОРОЙ

Игра совсем другая, если другой игрок выберет средний квадрат.

г = 1

средняя площадь занята

.X. X..  binary representation 1 
.X. .X.
... ...

средняя площадь свободна

X.. .X. binary representation 10001=17
... ...
..X .X.

г = 3

Средняя клетка занята, если другой игрок играет рядом с вашим последним Икс. Игра в ход коня, как показано ниже, поддерживается в версии без гольфа.

XX. .XX binary representation 1011=11 
.X. XX. or mirror image 1101=13
X.. ...

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

XX. binary representation 111=7.           XXX
XX. Only to be used where j is odd.        .X.
... Even j would look like image to right. ...

Средняя клетка занята, если другой игрок играет на 90 или 135 градусов к вашему последнему Х (играйте с ходом рыцаря).

X.X .X. binary representation 100101=37 
.X. .XX
.X. X..

Средняя площадь бесплатно

X.X .X. XX. binary representations:
... X.X ...    1010101=85 (first two)
X.X .X. .XX and 110011=51 (last one)

г = 5

средняя площадь занята. По причинам, указанным выше в r = 4, есть четыре возможных хода, каждый из которых проигрывает. поддерживается только один: 101111 = 47.

средняя площадь свободна. Существует только одна возможная плата с точностью до симметрии, как изложено ниже. Другой игрок должен проиграть на следующем ходу, поэтому нет необходимости поддерживать r> 5.

XX. binary representation 1110111=119
X.X
.XX
Уровень реки St
источник
Это изумительный ответ! Я думал, что проверил все дела на оптимальное время, но, думаю, пропустил одно. Хотя для простоты спецификация останется прежней. (Действительно, это удивительно, спасибо вам за это, и это так хорошо объяснено! Я оставил ввод / вывод в проигрыше, чтобы люди могли сделать что-то удивительное, как это.)
CAD97
1
Спасибо, это был интересный вызов. Теперь мне удалось немного поиграть в гольф.
Уровень River St