Відіграйте нім Wythoff's відмінно


16

Ваша мета - написати ідеального гравця для гри Nim Wythoff .

Правила Німа Вайтофа

Nim Wythoff - це детермінована гра для двох гравців, яка грається з двома купами однакових лічильників. Гравці чергують повороти, в яких вони роблять один із таких:

  1. Видаліть один або кілька лічильників з першої ворсу
  2. Видаліть один або кілька лічильників з другої ворсу
  3. Видаліть рівну кількість лічильників (одного або декількох) як з першої, так і з другої.

Звичайно, палі не можуть стати негативними, але вони можуть дорівнювати до 0. Залежно від того, хто з гравців видаляє останню лічилку, виграє гру.

Для більш геометрично налаштованих, ось еквівалентний склад гри, який ви можете грати в цей аплет . Єдина королева починається з якоїсь площі чверть-нескінченної шахової дошки, кут якої знаходиться внизу зліва. Гравці чергують королеву, яка рухається як шахова королева, але обмежується трьома напрямами:

  1. Вниз
  2. Зліва
  3. По діагоналі вниз і вліво

Хто перемістить королеву в кут, той виграє.

Пов’язавши координати королеви (з кутом (0,0)) до розмірів відповідних паль, легко побачити, що обидві ігри однакові.

Ідеальна гра

(Ви можете пропустити це, якщо знайомі з поняттями ідеальної гри та виграшних рухів.)

Оскільки Nim Wythoff - це скінчена та детермінована гра, вона має поняття ідеальної гри . Ідеальний гравець - це стратегія, яка завжди виграє з теоретично виграної позиції, тобто позиція, в якій існує стратегія, яка гарантує виграш.

Щоб бути виграшною стратегією, досить рухатися, щоб завжди переходити на теоретичну виграшну позицію для гравця, який щойно перемістився, і, отже, гравець не йде далі. Перші з цих виграшних позицій (їх також називають холодними ) (0,0), (1,2), (2,1), (3,5), (5,3). Пояснення статті алгоритму див. У статті Вікіпедії для пошуку виграшної стратегії Nim Wythoff, а також формули для отримання виграшних позицій.

Вимоги до програми

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

Якщо виграшного кроку не існує, тобто позиція є теоретичною втратою, ваша програма повинна вказати так і втратити.

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

Вхідні дані

Пара (i,j)невід’ємних чисел, що представляють розміри ворсу, кожен максимум 99. Це можуть бути два числа, кортеж, список або будь-який контейнер, який ви віддаєте перевагу.

Вихідні дані

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

Якщо виграшного кроку немає, потрібно вказати це у висновку. Будь-вихід , як False, None, 0, або (-1,-1)буде робити, поки це не правова позиція, і те ж саме для кожного входу тих, хто програв.

Приклад виконання

f(5,0)   = (0,0)
f(2,2)   = (1,2)   # Or (2,1) or (0,0) 
f(1,2)   = False
f(13,9)  = (13,8)  # Or (10,6)
f(10,6)  = False
f(5,10)  = (5,3)
f(25,30) = (8,13)    

2
+1, частково тому, що мені подобається ідея чверті нескінченності.
Рівень річки Св.

визначити "розумну кількість часу". Чи кілька секунд протягом (100,50) розумної кількості часу?
Джон Дворак

Ой. Зачекайте. вхід обмежений ... 30 ??? Це трохи низько, чи не так?
Джон Дворак

@JanDvorak Ви маєте рацію, це може призвести до дешевих скорочень. Змінено на 99 - я вважаю, що цього достатньо? Вибачення за редагування специфікацій після публікації.
xnor

@PeterTaylor Дякую, виправлено.
xnor

Відповіді:


6

Haskell, 167 165 символів

c p=o p:c(o p:p)
o p=[(a,b)|a<-[0..],b<-[0..a],(a,b)?p==[]]!!0
(a,b)?p=[y|y@(c,d)<-p,c==a||c==b||d==b||a+d==b+c]
f x@(a,b)|a<b=f(b,a)|x?c[]!!0==x=(0,-1)|1>0=x?c[]!!0

Алгоритм неефективний, але він все ще працює протягом секунди (хоча не в інтерактивній консолі) для входів <100.

Пояснення:

c p=o p:c(o p:p)

Список холодних позицій, що задається набором попередніх холодних позицій, - це одна холодна позиція, а за списком холодні позиції з урахуванням цієї позиції та попередні (Неефективність: ця позиція генерується двічі)

o p=[(a,b)|a<-[0..],b<-[0..a],(a,b)?p==[]]!!0

Одна холодна позиція - це перша пара, така що немає холодних позицій, доступних від цієї пари (Неефективність: нам слід шукати замість попередньої пари)

(a,b)?p=[y|y@(c,d)<-p,c==a||c==b||d==b||a+d==b+c]

Позиції, доступні від пари, є такими, що їхні перші елементи відповідають, їх другі елементи відповідають, різниця відповідає, або менша купа до видалення - це більша купа після видалення.

f x@(a,b)|a<b=f(b,a)
         |x?c[]!!0==x=(0,-1)
         |1>0=x?c[]!!0

(основний метод) Якщо купи в неправильному порядку, поміняйте їх місцями. В іншому випадку, якщо перше холодне становище, яке можна отримати з позиції, - це саме положення, вкажіть на провал (в ідеалі, Maybe (Int,Int)замість цього повернетесь ). Ще повернути холодне положення (Неефективність: згадану пару шукають двічі. Гірше, що список холодних позицій формується двічі)


Здається, що " Отже, експоненціального рекурсивного пошуку в дереві ігор не вистачить " було оптимістично, тому що те, що ви описуєте, звучить саме так.
Пітер Тейлор

@PeterTaylor це О (п ^ 4). Кожна холодна пара потребує часу O (n ^ 3), щоб знайти їх і є O (n). Оптимізація покоління приведе його до O (n ^ 2) (якщо я читаю послідовність правильно). Алгоритм експоненціального часу буде набагато повільніше. Чи варто провести якісь тести?
Джон Дворак

Це нормально, я вам вірю.
Пітер Тейлор

ви можете видалити x@зx@(a,b)?p=...
гордий haskeller

Не впевнений, як потрапив туди. Виправлення, дякую.
Джон Дворак

5

GolfScript ( 63 57 байт)

{\]zip{~-}%0|$(!*,1=}+1,4*{..,,^[(.@,2/+.2$]+}38*2/?0]0=`

Очікує введення від stdin у формі [a b]та залишає вихід у stdout у цій формі або 0якщо це програє позиція. Демонстрація в Інтернеті

Огляд

,4*{..,,^[(.@,2/+.2$]+}38*2/

обчислює список холодних позицій (включаючи перевернуту версію [b a]для кожної холодної позиції [a b]), використовуючи властивість послідовності Beatty .

Потім ?шукає першу холодну позицію, що задовольняє блок, створений

{\]zip{~-}%0|$(!*,1=}+

які в основному перевіряє , що позиція досяжна з позиції введення шляхом обчислення різниці векторів , а потім перевірити , що це або [0 x], [x 0]або [x x]для деяких x > 0. ІМО , що тест є найбільш розумним бітом: 0|$сили будь-якого масиву в одному з цих форматів в форму під [0 x]час відображення [0 0]до [0], [a b]де ні aні bзнаходиться 0в масив з трьох елементів, і [-x 0]чи [-x -x]з [-x 0]. Потім (!*,1=перевіряє, що у нас є [0 x].

Нарешті, 0]0=`робиться резервний випадок та форматування для виводу.


4

Піт 57 58 61 62

K1.618Jm,s*dKs*d*KKU39M<smf|}TJ}_TJm,-Ghb-Hebt^,0hk2U99 1

Спробуйте в Інтернеті.

Досить схожий на інші відповіді, але ця вікіпедійна сторінка не дала багато іншого;) Магічне число 39- це кількість холодних позицій зі значеннями < 99.

Визначає функцію, за gякою можна дзвонити g 30 25. Повертається []за невдачею, [(13,8)]за успіхом.

Пояснення

K1.618                            : K=phi (close enough for first 39 values)
      Jm,s*dKs*d*KKU39            : J=cold positions with the form (small,big)
M<s                              1: Define g(G,H) to return this slice: [:1] of the list below 
   mf|}TJ}_TJ                     : map(filter: T or reversed(T) in J, where T is each member of..
             m,-Ghb-Hebt^,0hk2    : [(G H) - (0 x+1),(x+1 0) and (x+1 x+1)]
                              U99 : for each x from 0 - 98

sпередано до int - зберігає кілька символів /____1. rZ39можна замінити U39, використовуючи одинарну дальність. Так само ви можете замінити r99)на U99.
isaacg

@isaacg Дякую! Я зовсім забув про це U. Мені слід дійсно оновити пояснення: P
FryAmTheEggman

@isaacg Подумайте лише про Pyth, я думаю, ви могли б зробити @перехрестя встановлених, якщо його другим аргументом є список. Це трохи незручно залишилось, оскільки aбуло змінено: P
FryAmTheEggman

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

2

Javascript ES6 - 280 байт

Укорочений

r=x=>~~(x*1.618);g=(y,x)=>y(x)?g(y,x+1):x;s=A=>A?[A[1],A[0]]:A;f=(a,b)=>j([a,b])||j([a,b],1);j=(A,F)=>l(A,F)||s(l(s(A),F));l=(A,F)=>([a,b]=A,c=(F&&a+b>=r(b)&&(e=g(x=>a+b-2*x-r(b-x),0))?[a-e,b-e]:(e=g(x=>r(a+x)-2*a-x,0))+a<b?[a,a+e]:(e=r(b)-b)<a?[e,b]:0),c&&r(c[1]-c[0])==c[0]?c:0)

Розширено

r = x => ~~(x*1.618);
g = (y,x) => y(x) ? g(y,x+1) : x;
s = A =>A ? [A[1],A[0]] : A;
f = (a,b) => j([a,b]) || j([a,b],1);
j = (A,F) => l(A,F) || s(l(s(A),F));
l = (A,F) => (
    [a,b] = A,
    c = (
        F && a+b >= r(b) && (e = g( x => a+b - 2*x - r(b - x), 0 )) ? [a-e,b-e] :
        (e = g( x => r(a+x) - 2*a - x, 0)) + a < b ? [a,a+e] :
        (e = r(b) - b) < a ? [e,b] : 0
    ),
    c && r(c[1] - c[0]) == c[0] ? c : 0
);

Гарний та швидкий алгоритм. Працює в O (n), але запускається постійно, якби не цикл економії байтів. Цикл реалізований як рекурсивний інкремент, отже, сценарій зрештою вийде з ладу з помилкою рекурсії для n у сотні чи тисячі. Використовує ту саму властивість послідовності Beatty, яку згадує містер Тейлор, але замість того, щоб обчислювати послідовність, вона просто переходить до правильних термінів.

Працює належним чином на всіх тестових входах і багатьох десятках, крім того, що я тестував.

Функція викликати є f. Він повертає масив успіху і 0після відмови.


Зачекайте, виведення масиву нормально?
Іван Дворак

@JanDvorak: xnor має кортеж, перелічений у списку дійсних результатів, отже, я так зрозумів. Можливо, він може прояснити справу. Це будь-яке банальне виправлення.
COTO

Масив або однотонний масив пари добре; кілька виграшних кроків не є.
xnor

1

Perl 5 - 109 (з 2 прапорцями)

#!perl -pl
for$a(@v=0..99){for$b(@v){$c=$a;$d=$b;${$:="$a $b"}||
map{$$_||=$:for++$c.$".++$d,"$a $d","$c $b"}@v}}$_=$$_

Використання:

$ perl wyt.pl <<<'3 5'

$ perl wyt.pl <<<'4 5'
1 2

Просто обчислює рішення для кожного можливого введення, а потім робить лише один пошук.

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