Хрестики, ні носочки


10

Усі розуміють, що Tic Tac Toe - це вирішена гра. Однак версія Misère only-Xs пропонує цікаву альтернативу.

У цій версії гри обидва гравці грають X на дошку і намагаються уникати трійки підряд. Якщо ви хочете дізнатися більше про це, у Numberphile є приємне відео про цю концепцію.

Враховуючи дошку Misère Crosss, грайте в оптимальний хід.

Дошка - це три рядки по три символи, які є 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]


Які формати введення та виведення? Я припускаю, що дошка взята як масив чи рядок? Однак це не дає інформації про останній крок, звідси і моє наступне запитання.
Річка Рівня Св.

1
Стратегія "грати навпроти останньої гри опонента" передбачає або знання історії ходу опонента, або що ви раніше дотримувались цієї стратегії, тобто не успадкували дошку, наприклад, .XX\nX..\nX..наприклад. Чи потрібно розглядати такі спадкові дошки, як ця?
Річка Рівня Св.

@LevelRiverSt Як було написано: "Ви можете припустити, що всі ваші попередні кроки були оптимальними", так що дошка буде недійсною. Ви можете брати вхід у будь-якому форматі, який вам подобається, але багаторядковий рядок, такий як ваш приклад, був би "за замовчуванням": я просто не хочу нікого обмежувати в розборі рядка, коли логіка переміщення є точкою змагання.
CAD97

Відповіді:


3

Ruby, оберт 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.

Рубі, об. 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 вздовж одного краю (негайне самогубство.)

Ungolfed в тестовій програмі

Негольована версія слідує логіці, висловленій у питанні.

У версії для гольфу стіл трохи змінений, [[0],[1,17],[9],[37,7,51,85],[45],[47,119]]щоб реалізувати дещо іншу поведінку для цього випадку r=3. Потім він стискається для друку ASCII (вимагає розшифровки (t-36)%120). Необхідний додатковий біт логіки для розмежування трьох X у куті та трьох X по краю у випадку запису таблиці 7:&&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: \ Користувачі \ steve> ruby ​​tictac.rb
0
256

000
010
000

C: \ Користувачі \ steve> ruby ​​tictac.rb
256
384

010
010
000

C: \ Користувачі \ steve> ruby ​​tictac.rb
384
400

010
010
100

C: \ Користувачі \ steve> ruby ​​tictac.rb
400
404

010
010
101

C: \ Користувачі \ steve> ruby ​​tictac.rb
404
436

010
110
101

C: \ Користувачі \ steve> ruby ​​tictac.rb
436
444

010
110
111

ІГРАЛЬНИЙ АНАЛІЗ ІГРАЙ ПЕРШИЙ

Це насправді дуже просто і лінійно.

Під час першої гри середній квадрат завжди буде зайнятим першим квадратом.

r = 0

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

r = 2

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

r = 4

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

Є лише один спосіб (аж до симетрії) мати п'ять X, включаючи середній квадрат на дошці, без того, щоб гра закінчилася. У середньому квадраті є X, по одній діагоналі (90 градусів один до одного) і по одній на кожній горизонтальній / вертикальній центральній лінії (на 90 градусів один до одного.) Оскільки ціле ребро не може бути зайняте, вище є єдиним можлива домовленість. Інший гравець повинен програти при наступному кроці.

ІГРАЛЬНИЙ АНАЛІЗ, ІГРЕННЯ ВТОРОМ

Гра зовсім інша, залежно від того, якщо інший гравець вибере середній квадрат.

r = 1

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

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

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

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

r = 3

Середня площа зайнята, якщо інший гравець грає поруч з вашою останньою X Відігравання рицарського руху, як показано нижче, підтримується у версії без вольфів

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)

r = 5

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

середня площа вільна. Існує лише одна можлива дошка до симетрії, як описано нижче. Інший гравець повинен програти при наступному кроці, тому немає необхідності підтримувати r> 5.

XX. binary representation 1110111=119
X.X
.XX

Це дивовижна відповідь! Я думав, що я перевірив усі випадки на оптимальне мо, але я здогадуюсь, я пропустив. Характеристика залишиться однаковою для простоти, хоча. (Дійсно, це дивовижне дякую вам за це, і це так добре пояснено! Я залишив програвання вводу / виводу, щоб люди могли зробити щось дивовижне.)
CAD97

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