Знищити кілька доміно!


22

Завдяки цьому питанню за деяке натхнення

У цих проблемах ми будемо представляти лінію доміно у вигляді рядка |, /і \. Вам буде надано рядок доміно в якості вхідного даних, і ви повинні визначити, як вони виглядають, коли вони осіли. Ось правила того, як перепадають доміно

  • Стояче доміно, |зліва від лівого впалого доміно \, також стане лівим упалим доміно.

  • Стояче доміно, |праворуч праворуч упадене доміно /, також стане правильним упалим доміно.

  • Якщо стояче доміно знаходиться між лівим упалим \та правим упалим /доміно, воно залишатиметься стоячим.

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

Ось приклад того, як один висновок може досягти свого завершення

|||||||\/|||||||\||\|||/||||||\|||||

||||||\\//|||||\\|\\|||//||||\\|||||
|||||\\\///|||\\\\\\|||///||\\\|||||
||||\\\\////|\\\\\\\|||////\\\\|||||
|||\\\\\////|\\\\\\\|||////\\\\|||||
||\\\\\\////|\\\\\\\|||////\\\\|||||
|\\\\\\\////|\\\\\\\|||////\\\\|||||

\\\\\\\\////|\\\\\\\|||////\\\\|||||

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

Це тому відповіді будуть набрані в байтах, а менша кількість байтів буде кращою.

Тестові кейси

|||/||||  -> |||/////
|||\||||  -> \\\\||||
|/||||\|  -> |///\\\|
||/|||\|  -> ||//|\\|
||\|||/|  -> \\\|||//

6
Відверта втеча ахой! (Чи можемо ми використовувати інші символи?)
Арнольд

1
@Arnauld Не потрібно використовувати косої риски.
Пшеничний майстер

1
Я не можу ... зрозуміти, що врятувати, а що ні.
абсолютнолюдський

Чи буде вхід колись порожнім рядком або одним символом?
Дверна ручка

3
Мене це турбує більше, ніж слід, що такі речі, як `//////// | \, вважаються стабільними.
MooseBoys

Відповіді:


13

Сітківка , 32 байти

+`(/.\\)|(/)\||\|(\\)
$1$2$2$3$3

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

Пояснення

+Каже Retina запустити заміну в циклі до тих пір, поки не зможе змінити рядок. Кожна заміна обчислює один крок падаючих доміно. Сама заміна - це справді три заміни в одній, але це гарантує, що вони відбуваються одночасно:

(/.\\)...
$1

Це просто відповідає /|\(як /\\і /\\, але, але це не має значення) і знову вставляє його без змін. Мета цього - пропустити |падіння доміно з обох сторін, тому що це коротше, ніж виключати ті випадки, що мають інші розділи навколо двох інших випадків.

...(/)\|...
$2$2

Це відповідає /|і перетворює його на //.

...\|(\\)
$3$3

Це відповідає |\і перетворює його на \\.


Не можу сказати, що я не бачив, що це буде. Сітківка, безумовно, хороший інструмент для роботи.
Пшеничний майстер

@WheatWizard Це легко вирішити, але, мабуть, все-таки занадто багатослівний з усіма втечами і тим, що $1$2$2$3$3бити в гольф мовах.
Мартін Ендер


4

V , 23 байти

òÓ¯À<!|¨Ü©ü¨¯©|ÜÀ!/±±²²

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

Дійсно, це дуже схоже на відповідь на сітківку, просто так виглядає потворніше. Використовує стиснення регексу.

Hexdump:

00000000: f2d3 afc0 3c21 7ca8 dca9 fca8 afa9 7cdc  ....<!|.......|.
00000010: c021 2fb1 b1b2 b2                        .!/....

Пояснення:

òвказує V бігати, поки рядок не зміниться. Решта - стислий вираз. Перетворимо його в еквівалент vim ...

:s/\v\/@<!\|(\\)|(\/)\|\\@!/\1\1\2\2/g

:s/                                     " Substitute...
   \v                                   " Turn on magic (use less escaping)
          \|                            " A bar
            (\\)                        " Followed by a captured backslash
       @<!                              " That is not preceded by
     \/                                 " A forward slash
                |                       " OR...
                 (\/)                   " A captured forward slash
                     \|                 " Followed by a bar
                       \\@!             " That is not followed by a backslash
                           /            " Replace this with
                            \1\1        " Pattern 1 twice (will be empty if we matched the second path)
                                \2\2    " Pattern 2 twice (will be empty if we matched the first path)
                                    /g  " Replace every match on this line

4

SNOBOL4 (CSNOBOL4) , 117 115 112 111 байт

	D =INPUT
S	D '/|\' ='_'	:S(S)
	E =D
	D '/|' ='//'
	D '|\' ='\\'
	D E	:F(S)
R	D '_' ='/|\'	:S(R)
	OUTPUT =D
END

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

Кредит пітона відповідь Рода для ідеї для стопорного стану з другої змінної , щоб побачити зміни , а не тестування D '/|' | '|\'.

	D =INPUT		;* read input
S	D '/|\' ='_'	:S(S)	;* replace '/|\' with '_', recursively
	E =D			;* set E to D, this is the while loop
	D '/|' ='//'		;* topple right
	D '|\' ='\\'		;* topple left
	D E	:F(S)		;* if D doesn't match E, goto S
R	D '_' ='/|\'	:S(R)	;* replace '_' with '/|\' (inverse of statement S)
	OUTPUT =D		;* output
END

3

Haskell , 114 107 байт

until=<<((==)=<<)$g
g s=t<$>zip3('|':s)s(tail s++"|")
t(l,'|',r)|l<'0',r/='\\'=l|r=='\\',l>'/'=r
t(_,e,_)=e

Спробуйте в Інтернеті! Перший рядок визначає анонімну функцію.

Пояснення:

  • until=<<((==)=<<)$gє функцією точки виправлення (див тут для пояснення) , який не застосовує функцію gдо вхідному рядку , поки результат більше не змінюється.
  • zip3('|':s)s(tail s++"|")створює для кожного доміно, тобто символу в струні s, трійку з до- і наступним доміно, набиваючи |по краях. Напр. /\|Стає [(|,/,\),(/,\,|),(\,|,|)](ігнорування втечі).
  • Потім функція tзастосовується до кожної з трійки для обчислення нового положення центральної частини трійки.


2

Пролог (SWI) , 132 байти

+[]-->[].
+[47,124,92|T]-->"/|\\",+T.
+[47,47|T]-->"/|",+T.
+[92,92|T]-->"|\\",+T.
+[X|T]-->[X],+T.
X+Y:- +(N,X,[]),!,(X=N,Y=N;N+Y).

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

Ця програма визначає предикат, +/2який є істинним, якщо другий аргумент - це врегульована версія першого. Обидва аргументи - це списки кодів символів.

Пояснення

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

DCG

+[]-->[].
+[47,124,92|T]-->"/|\\",+T.
+[47,47|T]-->"/|",+T.
+[92,92|T]-->"|\\",+T.
+[X|T]-->[X],+T.

Ці п'ять рядків коду визначають правило DCG (Definite Clause Grammar), +яке використовується в програмі для обчислення одного кроку дощипування доміно. DCG в Prolog працюють, знаходячи перший випадок правила, правий бік якого відповідає рядку та визначаючи аргумент правила з лівої сторони через цей процес. Якщо випадок не збігається, він відхилиться та спробує пізніший випадок.

+[]-->[].

Цей рядок представляє базовий випадок +правила. Він просто стверджує, що якщо зараз немає доміно, то на наступному кроці доміно досі не буде.

+[47,124,92|T]-->"/|\\",+T.

Оскільки ця програма має справу виключно зі списками кодів символів, важливо відзначити , що коди символів для /, \і |в 47, 92 і 124 відповідно. Цей випадок +правила обробляє /|\рядок.

+[47,47|T]-->"/|",+T.

Цей випадок стосується правого падаючого доміно, що б'є над доміно праворуч від нього. Оскільки це стосується справи для обробки, /|\вона не буде використана для такої можливості.

+[92,92|T]-->"|\\",+T.

Обробляє корпус для лівого падаючого доміно, що стукає над доміно ліворуч від нього.

+[X|T]-->[X],+T.

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

Присудок

X+Y:- +(N,X,[]),!,(X=N,Y=N;N+Y).

Основний предикат бере два аргументи, перший - початкова установка доміно, другий - осілі доміно. Оскільки це Prolog, другий може бути невизначений і програма обчислить його. Присудок сам по собі досить простий +(N,X,[])називає DCG і обчислює наступний крок доміно, що його зберігає N. (X=N,Y=N;N+Y)перевіряє, чи є наступний крок доміно таким же, як поточний, і якщо він його встановлює, Yоскільки доміно повинно осісти, а якщо це не повторюється, викликаючи той же предикат із наступним кроком доміно Nзамість X.



1

обличчя , 166 байт

\|/,cm_/o>AvI[IIcP/+PP|m_/m*/Sl*Im1/11:~-_I|'|?_1-_P|?_1`I-III_|+II|'I.C:1-_I|?_C'|-_P|?_C_|'I-_I|`I?_!'I.C:!'|'|-III+II|'I:C_|-PPP+PPI'I?I~_I-PPP+PP|-**1?*~Sl*Iw*I*>

Приймає введення як аргумент командного рядка і виводить в STDOUT. Це працює лише в команді 86494f6 і вище через помилку в цьому коміті.

Обгорнутий для естетики:

\|/,cm_/o>AvI[IIcP/+PP|m_/m*/Sl*Im1/11:~-_I|'|?_1-_P|?_1`I
-III_|+II|'I.C:1-_I|?_C'|-_P|?_C_|'I-_I|`I?_!'I.C:!'|'|-III
+II|'I:C_|-PPP+PPI'I?I~_I-PPP+PP|-**1?*~Sl*Iw*I*>

І невольф / прокоментував:

\|/,cm_/o>              ( setup )

AvI[II                  ( store input into I )
cP/+PP|m_/              ( store 92, ascii for \, into P, meaning prev char )
m*/Sl*Im1/11            ( store length of input into counter variable * )

( main loop: )
:~

    -_I|'|?_1           ( branch to 1 if the character is not \ )
    -_P|?_1             ( also branch to 1 if the previous character wasn't | )
    `I-III_|+II|'I      ( we have a sequence |\ so prev needs to be toppled )
    .C                  ( jump to C, the "continue" label at end of loop )

    :1
    -_I|?_C             ( branch to C if the character is not | )
    '|-_P|?_C           ( also branch to C if the previous character wasn't / )
    _|'I-_I|`I?_!       ( branch to ! if the next character isn't \ )
    'I.C:!              ( otherwise, skip the next \ and branch to continue )
    '|'|-III+II|'I      ( if all conditions hold we have /|| or /|/ so topple )

    :C
    _|                  ( reset pointer to source )
    -PPP+PPI            ( update prev variable )
    'I                  ( step through data )

?I~

_I-PPP+PP|-**1          ( reset input/prev and decrement counter )
?*~                     ( repeat main loop as many times as there are chars )

Sl*Iw*I*>               ( output final string to stdout )

Тут є кілька тонких хитрощів, які вибривають кілька зайвих байтів, наприклад

  • називання змінних | та /, до значень ASCII якого надається доступ через інтроспекцію пізніше в коді

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


1

Perl 5 , 52 + 1 (-p) = 53 байти

-6 байт завдяки mik

Напевно, не найкраще для Перла, але це те, що я міг би придумати.

0while(s/(?<!\/)\|(?=(\\))|(?<=(\/))\|(?!\\)/$1$2/g)

Пояснення

while(
  s/
    (?<!\/)\|(?=(//)) # If there's a | that precedes a \ but doesn't follow a /, capture /
      | # Or
    (?<=(\/))\|(?!//)/ # If there's a | that follows a / doesn't precede a \, capture /
  /$1$2/ # Replace all | with capture group 1 or 2, as one of the two will always be empty
  g # Repeat as much as possible for this string
)

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


-pзамість -aусунення потреби в print;; використання whileв якості суфікса до макетного виразу (наприклад 0) збереже ще 2 байти
mik

Дякую @mik, я не знав цих хитрощів. Я також усвідомлюю, що можу розмежувати регулярний вираз ще чимось іншим, щоб зберегти кілька байт. Можливо, дійти до цього пізніше.
Джеффрі Х.

1

Perl 5 , 44 (код) + 1 (-p ) = 45 байт

1while s,(/)\|(?!\\)|(?<!/)\|(\\),$1$1$2$2,g

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

Пояснення

1while s,                        ,        ,g   while anything found substitute globally
         (/)\|(?!\\)              $1$1         /| that is not followed by \ to //
                    |                          or
                     (?<!/)\|(\\)     $2$2     |\ that is not preceded by / to \\


0

Рубін , 83 байти

Технічно вигідне з 9.times, або навіть просто, 999.timesале мені не здається дешевим :)

Досі має величезний потенціал для гольфу. (Примітка: y while undoneнабагато довше, ніж x.size.times)

->x{x.size.times{x.gsub! /\/\|\\?|\|\\/,'/|\\'=>'/|\\','/|'=>'//','|\\'=>'\\\\'}
x}

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



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