Смотри, как они падают, как домино


22

Вы живете внутри терминала шириной 80 символов. Вам скучно, поэтому вы решили играть в домино. Нет, не скучный вид, похожий на Скрэббл, забавный вид, где вы проводите час, заставляя их смотреть, как они падают за секунду.

В терминалах домино выглядит так:

|   upright domino
\   left-tilted domino
/   right-tilted domino
__  fallen domino

Как мы все знаем, если наклонное домино касается вертикального, второе домино также наклоняется. Единственное исключение из этого, если два наклонных домино касаются этого:

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

Настройте гравитационную постоянную вашего терминала так, чтобы этот переход занимал 100 мс.

Если наклонное домино поддерживается другим домино или стенами терминала, его путешествие заканчивается.

Ни одно из наклоненных домино в

\||||____||||/__                /|\    /\    /|\                __\||||____||||/

(80 символов) будет двигаться, так как два самых наклонных домино поддерживаются стенами терминала, а все остальные поддерживаются другими домино.

Однако, если пространство в направлении наклона пусто, домино падает:

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

Терминал. Гравитационная постоянная. Вы понимаете, суть ...

Наконец, слева слегка дует ветер, поэтому домино с правым наклоном падают быстрее, чем левое:

|/ \| --> |__\|

задача

Напишите программу / функцию, которая показывает анимацию игры в домино в терминале.

Ваш код должен сделать следующее:

  1. Прочитайте строку из ввода, представляющую начальное состояние домино.

    Эта строка будет содержать не более 80 символов и состоит исключительно из описанных выше домино и пустых мест.

  2. Напечатайте состояние и подождите 100 мс.

  3. Измените состояние, как описано выше.

  4. Если состояние изменилось, вернитесь к 2.

Дополнительные правила

  • Длина входной строки не влияет на ширину терминала; даже если строка короче 80 символов, стены терминала по-прежнему находятся на расстоянии 80 символов.

  • Каждый раз, когда выполняется шаг 2, состояние должно быть напечатано в том же месте, перезаписывая предыдущее состояние.

  • Поскольку некоторые языки не могут ждать ровно 100 мс, не стесняйтесь ждать любую сумму от 50 до 1000 мс.

  • Применяются стандартные правила .

Примеры

  • Для начального состояния

     ||\/||
    

    напечатать следующее (одно поверх другого):

     ||\/||
     |\\//|
     \\\///
    __\\//__
    
  • Для начального состояния

    /||||\
    

    напечатать следующее

    /||||\
    //||\\
    ///\\\
    
  • Для начального состояния

    /|||\
    

    напечатать следующее

    /|||\
    //|\\
    
  • Для начального состояния

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

    распечатайте следующее:

    |__\|__\|__\|__\|
    
  • Для начального состояния (80 символов)

    \||||____||||/__                /|\    /\    /|\                __\||||____||||/
    

    напечатать следующее

    \||||____||||/__                /|\    /\    /|\                __\||||____||||/
    

Ответы:


13

Retina , 87 86 85 байт

Спасибо Деннису за сохранение 1 байта.

^.{0,79}$
$0 
:`^
<ESC>c
(`/ | \\
__
/\|(?!\\)
//a
(?<!/)\|\\
\\
$
aaaaa
a
aaaa
(a+)+b|a
<empty>

<ESC>должен быть заменен фактическим управляющим символом (0x1B). <empty>представляет пустую завершающую строку. Затем вы можете запустить приведенный выше код из одного файла с -sфлагом.

Код требует терминала, который поддерживает коды выхода ANSI. Я не могу подавить перевод строки в выводе Retina, поэтому мне нужно <ESC>cкаждый раз очищать всю консоль . Я протестировал код в bash, используя Mono для запуска Retina.

объяснение

^.{0,79}$
$0 

Мы начинаем с добавления пробела, если ввод содержит менее 80 символов. Это так, что /на правом конце не должны рассматриваться отдельно.

:`^
<ESC>c

Теперь мы добавим <ESC>cстроку, которая является escape-кодом ANSI для очистки терминала. Таким образом, каждый раз, когда строка печатается, она будет делать это в верхней части терминала. :`Инструктирует Retina распечатать результат этой подстановки, т.е. начальной конфигурации.

(`/ | \\
__

(`начинается петля. Поскольку соответствия нет ), предполагается, что цикл продолжается до последнего этапа программы. Каждая итерация будет имитировать один шаг падения домино, а затем немного «спать». Этот первый этап заменяет /и \рядом с пробелом в __. Это автоматически обрабатывает / \регистр правильно, потому что совпадения не могут перекрываться и ищутся слева направо. Таким образом /<sp>, будет сопоставлено и превращено в __такое, что \не может быть сопоставлено, и мы получим правильное __\.

/\|(?!\\)
//a

Это превращается /|в //условии, что \рядом с ним нет. Мы добавляем aтакое, чтобы это новое /не связывалось со следующим этапом (который еще не должен «знать» об этом изменении).

(?<!/)\|\\
\\

Противоположная ситуация: превратиться |\в, \\если рядом нет /. Нам не нужно помещать aздесь, потому что мы закончили с этим шагом моделирования.

Теперь спальная часть ...

$
aaaaa

Добавляет еще 5 aс в конец кода.

a
aaaa

Превращает каждый aв 4 aс, так что мы получаем 20 aс в конце.

(a+)+b|a
<empty>

Теперь самое интересное ... мы немного спим с помощью катастрофического возврата . Существует экспоненциальное количество способов разделить совпадение (a+)+между повторениями группы. Поскольку bсовпадение приводит к сбою, движок откатится назад и попробует каждую из этих комбинаций, прежде чем решить, что (a+)+bне может совпадать. Для двадцати aсекунд в конце это занимает примерно полсекунды.

В то же время мы разрешаем регулярному выражению совпадать с одним a, но только после возврата. Когда это совпадает, мы заменяем его пустой строкой, удаляя все as, которые мы вставили по той или иной причине из строки.

Это оставляет печать строки в конце итерации цикла. Здесь пригодится то, что я еще не исправил поведение петель при печати в Retina. В настоящее время есть только один флаг для каждой стадии, который говорит «напечатать» или «не печатать». По умолчанию используется «не печатать», за исключением последнего этапа программы, который по умолчанию «печатать». Но этап зациклен, что означает, что он фактически печатает текущую строку на каждой итерации. Обычно это действительно раздражает, и вам почти всегда нужно включать дополнительный пустой этап в конце, если вы хотите только конечный результат, но здесь он позволяет мне сохранить четыре байта.


6

Javascript (ES6), 206 148 129 158 байт

Я, наконец, опустил его до довольно низкой точки, но это не очистило бы консоль и не добавило бы дополнительное пространство; эти проблемы были исправлены в настоящее время.

c=console;d=s=>{c.clear(s[79]||(s+=' ')),c.log(s),t=s[R='replace'](/\/ | \\/g,'__')[R](/\/\|/g,'//a')[R](/\|\\/g,'\\\\')[R](/a/g,'');t!=s&&setTimeout(d,99,t)}

Альтернативная 153-байтовая версия, которая должна работать в Node.JS:

d=s=>{s[79]||(s+=' '),console.log("\033c"+s),t=s[R='replace'](/\/ | \\/g,'__')[R](/\/\|/g,'//a')[R](/\|\\/g,'\\\\')[R](/a/g,'');t!=s&&setTimeout(d,99,t)}

ИМХО, с ним довольно весело играть. Попробуйте HTML-версию здесь:

Там, вероятно, гораздо больше места для игры в гольф. Предложения приветствуются!


+1 за работающую демонстрацию, которая потратила 10 минут моего времени, и +1 за функцию рандомизатора. Однако, как упоминает Деннис, в первом тестовом случае это не удалось. Попробуйте /или, /|и вы увидите, что плитка не падает полностью, как следует.
dberm22

@Dennis Спасибо за указание на эти проблемы. Я думаю, что я исправил их обоих сейчас.
ETHproductions

Узел не в восторге от жирной стрелы, но в остальном работает нормально. Вы можете заменить \033его буквенным байтом ESC, сохранив 3 байта.
Деннис

2

Perl 5, 154 146

Пришлось использовать временного персонажа для поддержания состояния между 2 регулярными выражениями.
Чтобы справиться с риском, что-то вроде / | | | \ закончится как / / / \ \ вместо / / | \ \.

$_=substr(pop.' ',0,80);$|++;while($}ne$_){print"$_\r";$}=$_;s@ \\|/ @__@g;s@/\|(?=[^\\])@/F@g;s@([^/])\|\\@$1\\\\@g;tr@F@/@;select($\,$\,$\,0.1)}

Тест

$ perl dominos.pl '|\ |\/|||\/|'
|\__\//|\\/__

1
Вы можете устранить несколько обратных косых черт, если используете разделитель, отличный от косой черты, например s, \\|/ ,__,gвместо s/ \\|\/ /__/g.
Хоббс

Хороший совет. Забыл об этом трюке. И несколько дополнительных байтов были вырезаны с использованием отрицательных наборов.
LukStorms

2

ES6 , 220 218 195 байт

Минимизированный

f=d=>{var e,c=console;if(!d[79])d+=' ';c.clear();c.log(d);e=d;d=d[R='replace'](/\/\|\\/g,'a')[R](/\/ | \\/g,'__')[R](/\/\|/g,'//')[R](/\|\\/g,'\\\\')[R]('a','/|\\');if(e!=d)setTimeout(f,100,d);};

Более читаемый

f=d=> {
    var e,
    c=console;
    if(!d[79])
        d+=' ';
    c.clear();
    c.log(d);
    e=d;
    d = d[R='replace'](/\/\|\\/g, 'a')  //Substitute '/|\' with 'a' so it doesn't get replaced
        [R](/\/ |  \\/g, '__')     //Replace '/ ' and ' \' with '__'
        [R](/\/\|/g, '//')    //Replace '/|' with '//'
        [R](/\|\\/g, '\\\\')  //Replace '|\' with '\\'
        [R]('a', '/|\\');     //Put '/|\' back
    if(e!=d)
        setTimeout(f,100,d);
};

2
Добро пожаловать в Программирование Пазлов и Code Golf! 1. Я не уверен, почему вы используете обозначение ES6. () = > {и }()может быть просто удален из вашего кода. 2. Я не думаю, что окна предупреждений являются приемлемым выходным форматом для анимации. Вы можете встраивать свой JS в HTML или вносить необходимые изменения, чтобы он работал из командной строки. 3. В любом случае ваш код должен ждать ок. 100 мс между печатью одного состояния и следующего.
Деннис

2
Добро пожаловать в PPCG! Я бы посоветовал проверить этот пост и этот пост, чтобы улучшить вашу игру в гольф.
jrich

Спасибо за предложения и ссылки на советы по игре в гольф. Это все еще немного долго, но я немного сократил его и добавил таймер. Я пройду советы более подробно и посмотрю, что я могу сократить.
user3000806

1
Теперь это должно работать в терминале, но все равно не будет печатать обновленное состояние поверх старого. В Linux вы можете исправить это, вызвав console.log("^[c"+d)вместо этого ^[символ ESC (один байт).
Деннис

1
Если вы измените первый .replaceна [R='replace'], а затем на каждый последующий [R], это немного сократится. Вы также можете сохранить несколько байтов, используя setTimeout(f,100,d)вместо текущей настройки.
ETHproductions

2

C #, 335 байт

Не большой выбор языка.

Я злоупотреблял разрешенной задержкой между 50 и 1000, чтобы выбрать двузначное число.

Добавлены новые строки и отступы для ясности:

namespace System.Threading{
    class P{
        static void Main(string[]z){
            var c=@"/|\,/|\,/|,//,|\,\\,/ ,__, \,__".Split(',');
            for(string a=z[0].PadRight(80),b="";a!=b;){
                Console.Clear();
                Console.Write(b=a);
                Thread.Sleep(99);
                a="";
                for(int i,j;(i=a.Length)<80;)
                    a+=(j=Array.FindIndex(c,d=>b.Substring(i).StartsWith(d)))%2==0
                        ?c[j+1]
                        :b.Substring(i,1);
            }
        }
    }
}

1

PHP, 175 байт

$i=sprintf("%-80s",$argv[1]);$p='preg_replace';do{echo($o=$i)."\r";$i=$p('(/\|\\\\(*SKIP)(?!)|(?|(/)\||\|(\\\\)))','$1$1',$p('(/ | \\\\)','__',$i));usleep(1e5);}while($i!=$o);

Un-уменьшенная:

$input = sprintf("%-80s",$argv[1]);
do {
  echo $input."\r";
  $old = $input;
  $input = preg_replace('(/ | \\\\)','__',$input);
  $input = preg_replace('(/\|\\\\(*SKIP)(?!)|(?|(/)\||\|(\\\\)))','$1$1',$input);
  usleep(100000);
}
while( $input != $old);

В основном, regex golf. Сначала сглаживаются все падающие домино, у которых есть место (и из-за порядка соответствия слева направо «дует ветер»). Затем наступает уродливая часть (проклинаю тебя, косая черта!)

  • Подходим /|\, затем пропускаем.
  • Сопоставить (/)|и заменить на//
  • Сопоставить |(\)и заменить на\\

Это заставляет домино падать. Наконец, просто подождите 100 мс до следующего шага.

Использование в ()качестве разделителя в регулярном выражении означает, что /s не нужно экранировать, что помогает минимально!


Вам разрешено ждать 50 мс вместо 100, сохраняя 1 символ;) Разрешает ли PHP 10 ^ 5?
BlueCacti

1

POSIX shell + sed, 144

sed 's/^.\{1,79\}$/& /;s/.*/printf '"'&\\r'"';sleep .1/;h;:;s,/|\\,/:\\,g;s,\(/ \| \\\),__,g;s,/|,//,g;s,|\\,\\\\,g;H;t;x;y/:/|/;s/\\/\\\\/g'|sh

Это в двух частях. Основная работа по свержению домино - это sedзамена стандартного паттерна, накопление линий в трюме. Мы временно превращаемся /|\в /:\его защиту, выздоравливаем в конце.

s/^.\{0,79\}$/& /
h

:
s,/|\\,/:\\,g
s,\(/ \| \\\),__,g
s,/|,//,g
s,|\\,\\\\,g
H
t

x
y/:/|/

Поскольку sedу меня нет способа вставки задержек (я посмотрел на terminfo / termcap, но не смог найти никакого стандартного способа), я обертываю каждую строку, printf "...\r"; sleep .1 чтобы печатать строку каждые 100 мс. Я фактически делаю это сначала, когда у нас есть только одна строка, так как символы в команде не будут затронуты никакими заменами toppling.

Все проверено с использованием dashи GNU coreutils, с POSIXLY_CORRECTустановленным в среде.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.