Визуализируйте доску Nim как эксперт


10

Фон

В игре «Ним» игроки поочередно убирают «камни» из «груд»: на каждом ходу игрок должен убрать от одного до всех камней из одной кучи. Цель Нима - взять последний камень или, в мизерском варианте, заставить своего противника сделать это - однако оказывается, что стратегии почти идентичны.

Ним делает веселую игру в баре. Вы можете использовать спички или монеты для «камней», а «груды» обычно располагаются в линию. Ниже приведена классическая установка с кучами 1, 3, 5 и 7:

Ним со спичками

Если вы никогда раньше не играли в Nim, вы можете попробовать свои силы, прежде чем пытаться выполнить этот вызов. Вот версия под названием «Жемчуг перед свиньями» .

стратегия

Оптимальная стратегия в Nim достаточно сложна, чтобы большинство непрофессионалов последовательно проигрывали эксперту, но ее легко описать с помощью бинарной арифметики .

Однако выполнять ментальные бинарные операции 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 Вы можете предположить, что этого не произойдет. Можно даже предположить, что букв алфавита будет достаточно для любого ввода.
Иона

@ Джона, это будет верный вывод для второго контрольного примера? ["H","EE","EEH","CCCC","CCCCI","DDDDFF","DDDDFFI","AAAAAAAA","AAAAAAAA-","----------"]
нгн

1
@ Я думаю, простой ответ - да. Технически 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-суммой меньше, чем куча , мы должны удалить камни из этой стопки так, чтобы ее длина стала кучей XOR с nim-суммой . Сохраняя разницу в переменной z, мы можем проверить, является ли эта разница положительной, и если да, то 1.) вывести столько штрихов, 2.) вычесть ее из кучи и 3.) установить переменную nim-sum в ноль, чтобы предотвратить дальнейшее удаление камня.

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 / Assembly и взаимодействие с C, все достигается легче, чем с Haskell. Тем не менее, для фактического использования, из-за неясности и экспериментального / академического характера Clean, я бы порекомендовал Haskell (который также имеет большую поддержку в плане библиотек и справочной информации). Мне просто нравится, что Clean - это все.
Οurous
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.