看他们像多米诺骨牌一样倒下


22

您住在一个80字符宽的终端中。您很无聊,所以决定玩多米诺骨牌。不,不是像Scrabble这样的无聊类型,它是一种有趣的类型,您花一个小时设置它们,以观察它们在一秒钟内掉落。

在终端中,多米诺骨牌如下所示:

|   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个字符)

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

    打印以下

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

Answers:


13

视网膜87 86 85字节

感谢Dennis节省了1个字节。

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

<ESC>应该用实际的控制字符(0x1B)代替。<empty>代表空的尾线。然后,您可以从带有-s标志的单个文件中运行以上代码。

该代码需要一个支持ANSI转义代码的终端。我无法在Retina的输出中禁止换行,因此<ESC>c每次都需要清除整个控制台。我已经使用Mono运行Retina在bash中测试了代码。

说明

^.{0,79}$
$0 

如果输入的字符数少于80个,我们首先添加一个空格。这样一来/,不必单独处理右端的a 。

:`^
<ESC>c

现在,我们<ESC>c在字符串之前添加了字符串,这是用于清除终端的ANSI转义代码。因此,每次打印字符串时,它将在终端顶部进行打印。的:`指示视网膜打印出这种替代,即,初始配置的结果。

(`/ | \\
__

(`开始循环。由于没有匹配项),因此假定循环将一直进行到程序的最后阶段。每次迭代将模拟多米诺骨牌下降的一个步骤,然后再“睡眠”一下。这是第一个阶段,/并将\其替换为__。这会自动/ \正确处理此案件,因为匹配项不能重叠,并且会从左到右搜索。因此,/<sp>将匹配的,变成__这样了\无法比拟的,并且我们得到正确的__\

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

这种轮流/|//提供有没有\给它下一个。我们附加了一个a新内容/,以使新内容不会影响下一阶段(尚不应该“知道”这一更改)。

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

相反的情况:转|\\\提供没有/在它旁边。我们不需要在a此处放置一个,因为我们已经完成了此模拟步骤。

现在睡觉的部分...

$
aaaaa

a在代码末尾附加5个s。

a
aaaa

将每个a转换为4 as,所以最后得到20 as。

(a+)+b|a
<empty>

现在最有趣的部分是……在灾难性的回溯的帮助下,我们睡了一会儿。(a+)+在组的重复之间拆分匹配项的方式有很多。由于b导致匹配失败的原因,因此引擎将回溯并尝试这些组合中的每个组合,然后再确定(a+)+b不匹配的组合。最后的20 a秒需要大约半秒钟的时间。

同时,我们允许正则表达式匹配单个a,但仅执行回溯之后才匹配。当匹配时,我们用一个空字符串替换它,从字符串中删除a由于某种原因插入的所有s。

这样就可以在循环迭代结束时打印字符串。在这里,我还没有解决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)}

应在Node.JS中工作的备用153字节版本:

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 2015年

@Dennis感谢您指出这些问题。我相信我已经修复了这两个问题。
ETHproductions 2015年

Node对粗箭头并不满意,但否则效果很好。您可以\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
hobbs

好提示。忘了那个把戏。通过使用否定集减少了一些额外的字节。
LukStorms

2

ES6220个 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
欢迎来到编程难题和代码高尔夫球!1.我不确定您为什么使用ES6表示法。() = > {并且}()可以简单地从你的代码中删除。2.我不认为警报框是动画可接受的输出格式。您可以将JS嵌入HTML或进行所需的更改,以便可以从命令行使用它。3.无论哪种情况,您的代码都必须等待大约。在打印一个状态和下一个状态之间需要100毫秒。
丹尼斯

2
欢迎来到PPCG!我建议您查看这篇文章这篇文章,以帮助提高您的高尔夫水平。
jrich

感谢您的建议和高尔夫球技巧的链接。它仍然有点长,但是我将其缩短了一点并添加了计时器。我将更深入地介绍这些技巧,看看可以缩短哪些内容。
user3000806 2015年

1
现在应该可以在终端机上工作了,但是仍然不会在旧终端机上显示更新后的状态。在Linux上,你可以通过调用解决这个问题console.log("^[c"+d),而不是,这里^[是ESC字符(一个字节)。
丹尼斯

1
如果将第一个更改.replace[R='replace'],然后将每个后一个更改为[R],则将减少很多。您也可以通过使用一些字节来setTimeout(f,100,d)代替当前设置。
ETHproductions 2015年

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);

未缩小:

$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);

基本上是正则表达式高尔夫。首先,将所有具有空间的下降的多米诺骨牌弄平(由于从左到右的匹配顺序,“风”开始吹动)。然后是丑陋的部分(诅咒你砍!)

  • 匹配/|\,然后跳过它。
  • 匹配(/)|并替换为//
  • 匹配|(\)并替换为\\

这会使多米诺骨牌掉落。最后,只需等待100毫秒即可进行下一步。

使用()上的正则表达式来分隔符/,说自己需要转义,这有助于最低限度!


您可以等待50ms而不是100ms,节省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 以每100ms打印一行。实际上,我仅在一行只有一行时首先执行此操作,因为该命令中的字符不会被任何倾斜的替换所影响。

所有测试都使用dash和GNUcoreutilsPOSIXLY_CORRECT在环境中设置的进行。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.