Візуалізуйте дошку Німа, як експерта


10

Фон

У грі Німа гравці по черзі видаляють "каміння" з "паль": на кожному кроці гравець повинен видаляти між одним і всіма камінням з однієї купи. Завдання Німа - взяти останній камінь або, у мізерному варіанті, змусити опонента зробити це - однак, виявляється, стратегії майже однакові.

Нім робить веселу гру в бар. Можна використовувати сірники або монети для "каменів", а "палі" зазвичай розташовані в рядку. Нижче наведено класичне налаштування з паліми 1, 3, 5 та 7:

нім сірниками

Якщо ви ніколи раніше не грали в Нім, ви можете спробувати свої сили перед тим, як спробувати це завдання. Ось версія під назвою «Перлини перед свинями» .

Стратегія

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

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

Є лише три кроки:

  1. Подумки групуйте «камені» в кожному рядку за підгрупами, розміри яких мають потужність 2, починаючи з максимально можливого розміру: 8, 4, 2 та 1 достатньо для більшості ігор.
  2. Спробуйте зіставити кожну групу з близнюком в іншому рядку, щоб у кожної групи була пара.
  3. Якщо це неможливо, видаліть неспарені "камені" з одного рядка (це завжди буде можливо - див. Посилання Вікіпедія для того, чому), щоб крок 2. став можливим.

Або, кажучи іншим способом: "Видаліть з однієї купки якийсь камінь (и), щоб, якщо потім згрупувати палі в потужності 2, усі групи можуть бути сполучені з групою в іншій купі". Застереження про те, що ви не можете розбити більші сили 2 на менші - наприклад, ви не можете згрупувати лінію з 8 камінням на дві групи по 4.

Наприклад, ось як ви візуалізували дошку вище:

збалансовані сірники

Ця дошка ідеально збалансована, тому ви хочете, щоб ваш противник рухався першим.

Змагання

З огляду на перелік позитивних цілих чисел, що представляють розмір Nim "паль", поверніть звичайну текстову візуалізацію дошки Nim, як її бачив експерт.

Що являє собою дійсну візуалізацію, найкраще пояснити на прикладі, але потрібно:

  1. Призначте виразний символ кожній «підгрупі потужності-2» та її парі (непарні підгрупи не кваліфікуються) та використовуйте цей символ для представлення «каменів» як у підгрупі, так і в парі.
  2. Представляють будь непарні «камені» (тобто, ті , експерт буде видалити при відтворенні нормально - НЕ мізер - NIM) з допомогою дефіса: -.

Існує кілька способів досягти дійсної візуалізації, і всі вони дійсні. Давайте розглянемо кілька тестових випадків:

Випробування

Вхід: 1, 3, 5, 7

Можливий вихід 1:

A
BBA
CCCCD
CCCCBBD

Ви можете додатково включати пробіли між символами, а також порожні рядки між рядками:

Можливий вихід 2:

A

B B A

C C C C D

C C C C B B D

Вхід: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10

Порядок та вибір символів можуть бути будь-якими:

Можливий вихід 1:

G
E E
E E G
C C C C
C C C C F
B B B B D D
B B B B D D F
H H I - - - - -
A A A A A A A A I
A A A A A A A A H H

Символи Unicode також добре:

Можливий вихід 2:

◎
◈  ◈
◈  ◈  ◎
△  △  △  △
△  △  △  △  ◉
◐  ◐  ◐  ◐  ◀  ◀
◐  ◐  ◐  ◐  ◀  ◀  ◉
▽  ▽  ◒  -  -  -  -  -
▥  ▥  ▥  ▥  ▥  ▥  ▥  ▥  ◒ 
▥  ▥  ▥  ▥  ▥  ▥  ▥  ▥  ▽  ▽  

Вхід: 7

З правил випливає, що будь-яку «єдину купу» необхідно повністю видалити.

Можливий вихід 1:

-------

Можливий вихід 2:

- - - - - - -

Вхід: 5, 5

Можливий вихід:

A A A A B
A A A A B

Додаткові правила

  • Це кодовий гольф зі стандартними правилами. Найкоротший код виграє.
  • Введення даних є гнучким і може здійснюватися в будь-якій зручній для вас формі списку.
  • Вихід також є гнучким, як це свідчить вищевказані приклади. Більшість розумних варіантів буде дозволено. Запитайте, чи не знаєте ви про щось.

1
Чи існує обмеження, скільки каменів може містити кожна купу, або скільки різних символів знадобиться для візуалізації? (На крайній випадок, що робити, якщо, наприклад, знадобилось більше кількості друкованих символів ASCII або більше 255 окремих символів?)
Doorknob

@Doorknob Ви можете припустити, що цього не відбудеться. Можна навіть припустити, що букв алфавіту буде достатньо для будь-якого введення.
Йона

@Jonah це був би дійсний вихід для другого тестового випадку? ["H","EE","EEH","CCCC","CCCCI","DDDDFF","DDDDFFI","AAAAAAAA","AAAAAAAA-","----------"]
ngn

1
@ Οurous Я думаю, що відповідь проста. Технічно AAAABBBBфактично є недійсним, і ABBце не так, - але це робить результат менш читабельним, тому я думаю, що саме зменшення в рядку явного правила найкраще.
Йона

1
@JonathanAllan Так, я покладаюся на логіку, що всі 3 кроки повинні відбуватися одночасно. Отже, якщо ви виконуєте кроки 1 і 2, але не можете виконати крок 3, ви повинні налаштувати своє рішення на кроки 1 і 2. Що я можу бачити, що це заплутане. Я додав ваш опис нижче.
Йона

Відповіді:


2

Рубі, 169 164 148 байт

->a{s=eval a*?^
c=?@
m={}
a.map{|x|z=x-(x^s);[$><<?-*z,x-=z,s=0]if z>0
n=1
eval'x&1>0?[$><<(m[n]||c.next!)*n,m[n]=!m[n]&&c*1]:0;n*=2;x/=2;'*x
puts}}

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

Спочатку ми ініціалізуємо

  • nim-сума з s=eval a*?^(яка коротше a.reduce:^)
  • змінна c, яка зберігає перший невикористаний унікальний символ
  • карта, mяка відображає довжину потужності двох символів для символів, які використовуються для їх представлення

Потім, перебираючи кожну купу, виконуємо наступне:

z=x-(x^s);[$><<?-*z,x-=z,s=0]if z>0

Згідно зі стратегією Вікіпедії , якщо купка XOR в nim-sum є меншою, ніж ворс , ми повинні видалити камені з цієї ворса таким чином, що її довжина стає ворсом XOR ворсом . Зберігаючи різницю в змінній z, ми можемо перевірити, чи є ця різниця позитивною, і якщо так 1.) надрукуйте, що багато тире, 2.) відніміть її з палі і 3.) встановіть змінну nim-суми на нуль, щоб запобігти подальшому видаленню каменю.

n=1
eval'[...];n*=2;x/=2;'*x

Тепер ми «петля» над кожним бітом і стежити за їх значення шляхом багаторазового поділу xна 2і помноживши акумулятор nна 2. Цикл - це фактично строка, що оцінюється xчасом, що набагато більше log2(x)разів, ніж це потрібно, але шкоди не відбувається (окрім неефективності). Для кожного біта ми виконуємо наступне, якщо біт 1 ( x&1>0):

$><<(m[n]||c.next!)*n

Роздрукуйте символи nраз. Якщо ми вже надрукували непарну групу з цього безлічі каменів, використовуйте цей символ; в іншому випадку використовуйте наступний невикористаний символ (просування cна місці за рахунок !).

m[n]=!m[n]&&c*1

Якщо m[n]існували (тобто ми щойно завершили пару), то m[n]скидаємо їх. В іншому випадку ми щойно започаткували нову пару, тому встановили m[n]характер, який ми використовували ( *1це короткий спосіб зробити копію c).


4

Python 2 , 150 196 206 байт

def f(p):
 c=48;s=[l*'.'for l in p];m=2**len(bin(l))
 while m:
  if sum(m*'.'in l for l in s)>1:
   for i,l in enumerate(s):s[i]=l.replace('.'*m,chr(c)*m,`s`.count(chr(c)*m)<2)
   c+=1
  else:m/=2
 return s

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


Я не думаю, що це працює 4, 9, 10.
Ніл

@Neil Хороший улов, його слід виправити зараз
TFeld

1
Вибачте, мені вдалося його знову обдурити, цього разу з 14, 21, 35.
Ніл

Він також не відповідає [1, 3, 4, 5], де він повинен видалити всю другу купу.
Дверна ручка

@Doorknob, це потрібно? Правила кажутьThere will be multiple ways to achieve a valid visualization, and all are valid
TFeld

1

JavaScript (ES6), 215 байт

f=
(a,c=0,x=eval(a.join`^`),i=a.findIndex(e=>(e^x)<e),b=a.map(_=>``),g=e=>(d=e&-e)&&a.map((e,i)=>e&d&&(a[i]-=d,b[i]=(c++>>1).toString(36).repeat(d)+b[i]))&&g(e-d))=>g(eval(a.join`|`),b[i]='-'.repeat(a[i]-(a[i]^=x)))||b
<textarea oninput=o.textContent=/\d/.test(this.value)?f(this.value.match(/\d+/g)).join`\n`:``></textarea><pre id=o>

Тільки візуалізує до 36 різних символів. Мені полегшено це працює 1, 3, 4, 5.


Дійсно приємно. Забавно грати з ним у режимі реального часу.
Йона

1

Чистота , 454 байти

все ще гольф

import StdEnv,Text,Data.List
$p=join"\n"[{#toChar c+'-'\\c<-e}\\e<-[take i(e++[0,0..])\\e<-r[[~c\\c<-reverse e,_<-[1..c]]\\e<-hd[q\\q<-foldr(\h t=[[a:b]\\a<-h,b<-t])[[]][[c\\c<-subsequences(takeWhile((>=)k)(iterate((*)2)1))|sum c<=k]\\k<-p]|sum[1\\a<-q&b<-p|sum a<>b]<2&&foldr(bitxor)0(flatten q)==0]]1&i<-p]]
r[]_=[]
r[h:t]n|all((<)0)h=[h:r t n]
#[m:_]=removeDup[e\\e<-h|e<0]
#(a,[x:b])=span(all((<>)m))t
=r([[if(e==m)n e\\e<-k]\\k<-[h:a]++[x]]++b)(n+1)

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

Визначає функцію $ :: [Int] -> String, приймаючи розміри ворсу і повертаючи рядок, де -позначають камені, які потрібно видалити, а групи представлені символами ASCII, що піднімаються -. Якщо потрібна достатня кількість груп, символи з часом повернуться назад, а завдяки цьому foldrдля запуску другого тесту потрібно більше гігабайт пам'яті.

Відступна версія гігантського розуміння:

$p=join"\n"[
    {#
        toChar c+'-'
        \\c<-j
    }
    \\j<-[
        take i(e++[0,0..])
        \\e<-r[
            [
                ~c
                \\c<-reverse e
                ,_<-[1..c]
            ]
            \\e<-hd[
                q
                \\q<-foldr(\h t=[
                    [a:b]
                    \\a<-h
                    ,b<-t
                ])[[]][
                    [
                        c
                        \\c<-subsequences(takeWhile((>=)k)(iterate((*)2)1))
                        |sum c<=k
                    ]
                    \\k<-p
                ]
                |sum[
                    1
                    \\a<-q
                    &b<-p
                    |sum a<>b
                ]<2&&foldr(bitxor)0(flatten q)==0
            ]
        ]1
        &i<-p
    ]
]

Що цікаво, Clean виглядає схожим на haskell ... які його переваги перед Haskell?
Йона

1
@Jonah Це дуже схоже, так. У верхній частині голови у мене є маніпуляція з нижчим рівнем, вбудована IL / зборка та взаємодія з C - все це досягається простіше, ніж з Haskell. Однак для фактичного використання, через невизначеність та експериментальний / академічний характер Clean, я б рекомендував Haskell (який також має більшу підтримку в частині бібліотек та довідкової інформації). Мені просто подобається, що Clean is all.
Οurous
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.