Оптимальні ігри Tic Tac Torus


31

Це завдання стосується гри Tic Tac Toe, але вона грається на торі.

Як грати

Щоб створити необхідну ігрову дошку, ви починаєте з звичайної ігрової дошки Tic Tac Toe. Спочатку складіть його в циліндр, з'єднавши лівий і правий край. Потім складіть його в торус, з'єднавши верхній і нижній край. Ось проста візуалізація такої ігрової дошки з кількома відтвореними рухами (навички Sick Paint!).

Tic Tac Torus

Правила Tic Tac Toe на торі такі ж, як і у звичайного Tic Tac Toe. Кожен гравець розміщує чергування Xs та Os. Виграє перший з 3-ма однаковими символами в рядку, стовпцем або діагоналлю.

Оскільки тору досить важко уявити, ми просто проектуємо дошку назад на папір. Тепер ми можемо грати в гру як звичайний Tic Tac Toe. Різниця лише в тому, що ви також можете виграти з трьома однаковими символами в розбитій діагоналі. Наприклад, Player 1 (X) виграє наступну дошку. Це можна легко побачити, трохи змінивши погляд на торус.

Гравець 1 (X) виграє через 3 X в одній розбитій діагоналі

Якщо вас зацікавило, ви можете пограти в Tic Tac Toe на торі в Torus Games . Є версія для Windows, Mac та Android.

Оптимальні ігри

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

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

  • Якщо ви можете перемогти, розмістивши свій символ, зробіть це.
  • Інакше, якщо у опонента є два символи в одному рядку / стовпці / điagonal, спробуйте заблокувати його. Інакше робіть те, що хочете.
  • Інакше робіть те, що хочете.

Кожна оптимальна гра складається з рівно 7 ходів, і ці рухи можна описати наступним чином:

  1. Гравець 1 розміщує X десь на дошці (9 варіантів)
  2. Гравець 2 розміщує O де-небудь на дошці (8 варіантів)
  3. Гравець 1 розміщує X десь на дошці (7 варіантів)
  4. Хід гравця 2 може бути вимушеним (1 вибір), якщо ні, він розміщує O куди завгодно (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) або у будь-якому форматі масиву / списку.

Тип виклику - код-гольф. Тому виграє найкоротший код.


Чому перший хід гравця 2 повинен бути в тому ж рядку або стовпці, що і перший хід гравця 1? Я провів кілька ігор в голові, де перший хід гравця 2 знаходиться в одній і тій же діагоналі, і вони дотримуються тієї ж оптимальної схеми гри. Мені здається, що одним із аспектів тороїдальної дошки є те, що рядки, стовпці та діагоналі мають симетричний вплив на гру.
Runer112

@ Runer112 багато спростив оптимальну стратегію. Єдине правило зараз - блокувати опонента, якщо можете, інакше робіть те, що хочете.
Якубе

7
Лише побічний коментар: насправді тут можливо набагато менше унікальних ігор. Поступальна симетрія робить положення першого ходу неактуальним (тому що ви завжди можете зосереджувати свій погляд), тому розділіть на 9 ... і поворот дошки робить лише два різні секундні ходи (діагональний або сусідній квадрат) ... так у 48 найрізноманітніших ігор. Якщо взяти до уваги симетрію відображення, вона знизиться ще далі. Ця версія тору набагато нудніша, ніж звичайна. Гольф далеко.
Оріон

@orion Насправді той факт, що торус обгортає, не забороняє нам думати про "0" як "першу" пряму на торовій дошці та розрізняти всі дев'ять полів загалом ... Але ми погоджуємось, що "меридіан 0" знаходиться у Грінвічі , тоді як на протилежному від Землі ми можемо бути однією ногою на місці, де є четвер, одна нога на місці, це середа (24 години різниці за місцевим часом!) - і все, незважаючи на те, що Земля кругла і не має a "відправна точка" ...
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

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,:));

1
невеликий натяк: ви можете вирізати старі версії, якщо вони використовують ту саму логіку, оскільки вони зберігаються в історії редагування, тому вони все ще доступні
masterX244
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.