Оптимальные игры в Tic Tac Torus

31

Это испытание про игру Tic Tac Toe, но в нее играют на торе.

Как играть

Чтобы создать необходимую игровую доску, вы начинаете с обычной игровой доски Tic Tac Toe. Сначала сложите его в цилиндр, соединив левый и правый край. Затем сложите его в тор, соединяя верхний и нижний края. Вот простая визуализация такой игровой доски с несколькими сыгранными ходами (навыки Sick Paint!).

Tic Tac Torus

Правила Tic Tac Toe на торе те же, что и у Tic Tac Toe. Каждый игрок размещает чередующиеся буквы X и Os. Первый с 3 одинаковыми символами в строке, столбце или по диагонали выигрывает.

Поскольку тор довольно сложно визуализировать, мы просто проецируем доску обратно на бумагу. Теперь мы можем играть в эту игру как обычный Tic Tac Toe. Разница лишь в том, что вы также можете выиграть с 3 одинаковыми символами в ломаной диагонали. Например, Игрок 1 (X) выигрывает следующую доску. Это легко увидеть, немного изменив вид тора.

Игрок 1 (X) выигрывает из-за 3 X в одной ломаной диагонали

Если вам интересно, вы можете сыграть в Tic Tac Toe на Torus Games в Torus . Есть версия для Windows, Mac и Android.

Оптимальные игры

В этом вызове были заинтересованы в оптимальных играх. Оптимальная игра - это игра, в которой оба игрока играют оптимальную стратегию. На регулярной доске Tic Tac Toe оптимальные игры всегда заканчиваются вничью. Увлекательно на торусной доске всегда побеждает первый игрок. На самом деле игра на доске тора никогда не может закончиться ничьей (даже если игроки играют не оптимально).

Оптимальная стратегия действительно проста:

  • Если вы можете выиграть, разместив свой символ, сделайте это.
  • В противном случае, если у вашего противника два символа в одной строке / столбце / диагонали, попробуйте заблокировать его. Иначе делай что хочешь.
  • Иначе делай что хочешь.

Каждая оптимальная игра состоит из ровно 7 ходов, и эти ходы можно описать следующим образом:

  1. Игрок 1 размещает X в любом месте на доске (9 вариантов)
  2. Игрок 2 ставит О в любом месте на доске (8 вариантов)
  3. Игрок 1 ставит X в любом месте на доске (7 вариантов)
  4. Ход игрока 2 может быть принудительным (1 выбор), в противном случае он размещает О где угодно (6 вариантов)
  5. Ход игрока 1 является вынужденным (1 выбор)
  6. Игрок 2 пойман на развилке (игрок 1 может выиграть двумя разными способами), поэтому игрок 2 должен заблокировать игрока 1 одним способом (2 варианта)
  7. Игрок 1 размещает свой последний ход и выигрывает (1 выбор)

На нашей прогнозируемой доске 9 * 8 * 1 * 6 * 1 * 2 * 1 + 9 * 8 * 6 * 1 * 1 * 2 * 1 = 1728 различных оптимальных игр. Здесь вы можете увидеть одну типичную оптимальную игру:

Пример оптимальной игры

Если мы помечаем каждую ячейку доски цифрами 0-8, мы можем описать эту игру цифрами 3518207. Сначала X находится в ячейке 3 (средний ряд, левый столбец), затем O в ячейке 5 (средний ряд, правый столбец), затем X в ячейке 1 (верхний ряд, средний столбец), ...

Используя эту цифровую запись, мы автоматически создали заказ. Теперь мы можем отсортировать все 1728 оптимальных игр и получим список:

Game 0000: 0123845
Game 0001: 0123854
Game 0002: 0124735
Game 0003: 0124753
Game 0004: 0125634
   ...
Game 0674: 3518207
   ...
Game 1000: 5167423
Game 1001: 5167432
Game 1002: 5168304
   ...
Game 1726: 8765034
Game 1727: 8765043

Вызов

Этот список является частью вашей работы. Вы получите одно число kот 0 до 1727, и вам нужно будет вернуть kигру в цифровом формате этого отсортированного списка.

Напишите функцию или программу, которая получает число k(целое число), вычисляет соответствующую игру. Вы можете прочитать ввод через STDIN, аргумент командной строки, приглашение или аргумент функции и распечатать результат (7 цифр) в читаемом формате (например, 0123845или [0, 1, 2, 3, 8, 4, 5]) или вернуть его, используя строку (читаемый человеком формат) или целое число (содержащее все цифры в базе 10) или в любом формате массива / списка.

Тип вызова - код-гольф. Поэтому самый короткий код выигрывает.

Jakube
источник
Почему первый ход игрока 2 должен быть в той же строке или столбце, что и первый ход игрока 1? Я разыграл несколько игр в моей голове, где первый ход игрока 2 вместо одной из тех же диагоналей, и они следуют той же оптимальной игровой схеме. Мне кажется, что одним из аспектов тороидальной доски является то, что строки, столбцы и диагонали оказывают симметричное влияние на игру.
Runer112
@ Runer112 Упростил оптимальную стратегию. Теперь единственное правило - блокировать противника, если можете, иначе делайте что хотите.
Якуб
7
Просто дополнительный комментарий: на самом деле уникальных игр здесь гораздо меньше. Трансляционная симметрия делает положение первого хода неуместным (потому что вы всегда можете отцентрировать свое представление), поэтому разделите на 9 ... и вращение доски делает только 2 различных вторых хода (диагональ или соседний квадрат) ... так что в 48 самых разных игр Если принять во внимание симметрию отражения, она снижается еще дальше. Эта версия Тора гораздо скучнее, чем обычная. Гольф прочь.
Орион
@orion На самом деле тот факт, что torus оборачивается, не запрещает нам думать о «0» как о «первом» прямоугольнике на доске тора и различать все девять полей в целом ... Тем не менее, мы согласны с тем, что «меридиан 0» находится в Гринвиче в то время как на противоположной стороне Земли мы можем быть одной ногой на месте, где это четверг, одной ногой на месте - среда (24-часовая разница по местному времени!) - и все это несмотря на то, что Земля круглая и не имеет «отправная точка» ...
pawel.boczarski
@ Родни Нет, это один, а не семь. Попробуйте рассчитать это.
Якуб

Ответы:

6

JavaScript (ES6), 266 308 317 334 341

Функция, возвращающая строку. Править Нашли арифметическое решение для функции M (наконец-то!)

F=(n,x=[],M=(a,b,t=-a-b)=>(a-b)%3?a<3&b<3?3+t:a>5&b>5?21+t:12+t:9+t+a%3*3)=>
[for(a of z='012345678')for(b of z)for(c of z)for(d of z) 
a-b&&c-a&&c-b&&(b-(y=M(a,c))?d==y:d-a&&d-b&&d-c)&&(
f=M(c,e=M(b,d)),g=M(a,e),g<f?[f,g]=[g,f]:0,r=a+b+c+d+e,x.push(r+f+g,r+g+f))]
&&x[n]

Очень наивно , его можно укоротить разными способами . Он просто перечисляет все возможные допустимые значения и возвращает то, что находится в месте n. Функция M возвращает позицию между двумя ячейками, то есть обязательный ход, чтобы заблокировать противника.

Более читаемый

F=(n,x=[],
  M=(a,b,t=-a-b)=>(a-b)%3? 
     a<3&b<3?
       3+t // first row
       :a>5&b>5?
          21+t // last row
          :12+t // middle row and diags
     :9+t+a%3*3 // columns
  )=>
  [for(a of z='012345678') // enumerate the first 4 moves
     for(b of z)
       for(c of z)
         for(d of z) 
           a-b&&c-a&&c-b // avoid duplicates
           &&(b-(y=M(a,c))?d==y:d-a&&d-b&&d-c) // and check if d must block a-c or it's free
           &&(
             e=M(b,d), // forced to block b-d
             f=M(c,e),g=M(a,e),g<f?[f,g]=[g,f]:0, // f and g could be in the wrong order
             r=a+b+c+d+e, // start building a return string
             x.push(r+f+g,r+g+f) // store all values in x
  )]&&x[n] // return value at requested position
edc65
источник
3

Октава, 467 369 363 309 297 знаков

297:

global t=0*(1:9)m=dec2bin([15;113;897;1170;1316;1608;2370;2216;2580])-48 a;
function g(s,p)global a t m;
if nnz(s)<8&&any((t==1)*m>2)a=[a;s];return;end;q=t==3-p;
(r=sort(~q.*(1:9)*m(:,find([3 1]*([q;t==p]*m)==6)')))||(r=find(~t));
for i=r t(i)=p;g([s i+47],3-p);t(i)=0;end;end;g('',1);f=@(n)a(n+1,:);

Единственное существенное изменение заключается в том, что мы никогда не проверяем, может ли текущий игрок выиграть, а только проверяют возможность противника выиграть в следующем ходу . Поскольку единственный ход, в котором игрок 1 может выиграть, - это ход 7 , это единственное место, где алгоритм может создать игру, которая не является оптимальной, но очень легко отфильтровать такую ​​ситуацию. Мы просто проверяем каждую сгенерированную игру, если она выиграна игроком 1 - если это не так, ход на 7-м ходу был неправильным, поэтому мы не добавляем эту игру в таблицу оптимальных игр.

(Ровно половина игр, созданных по этому правилу, ложны, т. Е. В 7-м ходу у игрока 1 всегда есть две возможности заблокировать второго игрока, но только одна заставит его выиграть мгновенно).

Использование:

$ octave
octave:1>> source script.m
octave:2>> f(634)
ans = 3270148

Негольфированный код выглядит так:

 global t=0*(1:9);
 global m=dec2bin([15;113;897;1170;1316;1608;2370;2216;2580])-48;
 global allgames;
 allgames=[];

 function r=canbewon(by)
  global t m
  q=[t==by;t==(3-by)]*m;
  g=(find([3 1]*q==6))';
  r=sort((~(t==by).*(1:9)) * m(:,g));
 end

 function games_starting_with(s)
 global allgames t;
 if 7==numel(s) && any((t==1)*m==3) # it's 7th move and first player won
  allgames=[allgames;s];
  return;
 end;
 poss=(find(~t));                  # for now all free slots are possible
 player=1+mod(numel(s),2);
 moves = canbewon(3-player);
 if numel(moves) poss=moves; end;  # ... no, we must block the other player
 for i=poss
  t(i)=player;
  games_starting_with([s char(i+47)]);
  t(i)=0;
 end
end

games_starting_with('');
f=@(n)(allgames(n+1,:));
pawel.boczarski
источник
1
Небольшой совет: вы можете вырезать старые версии, если они используют ту же логику, поскольку они хранятся в истории редактирования, поэтому они все еще доступны
masterX244