ASCII动画雪景


22

编写最短的程序,将任何ASCII艺术作品转换成动画的雪景,然后从下雪开始形成雪景(非高尔夫JavaScript示例最后更新于2011-12-19)。

输入规范:您的程序必须接受空格,星号和换行符的任意组合。输入最多包含23行,每行80个字符。将没有空行,但行可能仅包含空格。仅包含一个尾随的换行符,必须将其忽略。

输出:为您的操作系统的文本控制台或终端仿真器输出ASCII字符(空格,星号)和控制代码(回车,换行符,ANSI转义代码等),直到用户手动终止程序为止。如果操作系统允许该设置,则可以假定终端窗口为80x24个字符。

规则

  • 动画必须流畅且快速(首选15 fps)。
  • 雪密度必须在5%到15%之间。
  • 每秒最多滚动不超过一个屏幕的雪。(这意味着在任何一秒钟的时间内最多只能添加24行新雪。)
  • 进入屏幕顶部时,雪花不得显示任何明显的图案;它必须看起来是随机的。
  • 该程序启动时必须尽快将屏幕上的所有行都积雪。屏幕的各个行的初始填充对于查看者而言不应是显而易见的。
  • 输入ASCII文字的左下角必须位于屏幕的左下角(图1进行了进一步说明)。
  • ASCII图形内部或下方的区域不得永久性地加星号。但是,星号可以(但不是必须)滚动通过该区域。
  • 除输入内容外,雪不得堆积在屏幕底部或现有雪的顶部。
  • 下部空间必须在上部空间之前填充,因为相反的填充空间使圣诞树动画看起来与原始代码的输出完全不同。(添加2011-12-20)

节日快乐!

图1:80x24屏幕的标记区域

---------------------------New snow added on this line--------------------------
                                                                             |
                                                                             |
----------------------------------------------------------+                  |
                                                    ****  |                  |
    Snow MUST fall  Snow MAY fall ---------------->  **** |                  |
    through this    through these          ****      **** |  Snow MUST fall  |
    area.           areas of a              ****     **** |  through this    |
                    completed   \--------->  ****     ****|  area.           |
        ASCII art   scene.    \     ***        ****   ****|                  |
          area         \       \   *******      ****  ****|                  |
                        \       \    ********     ***  ***|  (ALL CAPS terms |
      (located in        \       \-->   *********  ***    |  have standard   |
       lower left         \     *******     ******  MAY   |     RFC 2119     |
       corner of           \    *************  **   fall  |    meanings.)    |
       screen)              \        ***********    here  |                  |
                         *** +--->          ****  ***     |                  |
                         *** | ****************   ***     |                  |
  | Snow MUST fall       *** | ****************   ***     |                  |
  | through this         *** +--->                ***     |                  |
  | area.                *** | ****************   ***     |                  |
--+---------------------+*** +--->                ***+----+------------------+--
  |   Snow MUST NOT     |****************************|      Snow MUST NOT    |
  V  accumulate here.   |****************************|     accumulate here.  V

输入示例

代码高尔夫横幅

 ******   *******  ********  ********     ******    *******  **       ******** 
**    ** **     ** **     ** **          **    **  **     ** **       **       
**       **     ** **     ** **          **        **     ** **       **       
**       **     ** **     ** ******      **   **** **     ** **       ******   
**       **     ** **     ** **          **    **  **     ** **       **       
**    ** **     ** **     ** **          **    **  **     ** **       **       
 ******   *******  ********  ********     ******    *******  ******** **       

堆栈溢出徽标

                                                    ****
                                                     ****
                                           ****      ****
                                            ****     ****
                                             ****     ****
                                    ***        ****   ****
                                   *******      ****  ****
                                     ********     ***  ***
                                        *********  ***
                                *******     ******
                                *************  **
                                     ***********
                         ***                ****  ***
                         ***   ****************   ***
                         ***   ****************   ***
                         ***                      ***
                         ***   ****************   ***
                         ***                      ***
                         ****************************
                         ****************************

圣诞树

                                        *
                                       ***                           *
                *                     *****                         ***
               ***                   *******           *           *****
              *****                 *********         ***            *
                *                  ***********       *****
                       *          *************     *******
        *             ***        ***************       *               *
       ***           *****      *****************                     ***
      *****         *******    *******************                   *****
     *******           *      *********************                 *******
    *********                           *                          *********
        *                                                              *

1
第三棵圣诞树坏了。
鲍比(Bobby)

好挑战!我认为应该列举规则以便于参考,而且我不理解第三和第六条规则……
hallvabo 2011年

@hallvabo我已经阐明了这两个规则,后者通过添加标签来阐明。
2011年

澄清要求:换行符是否包含在最大80个字符的行长中,还是最大80个字符换行符?(我假设是后者,但某些意见书似乎假定了后者。)
Ilmari Karonen 2011年

@IlmariKaronen后者。
2011年

Answers:


5

Perl,196/239个字符

chomp(@p=(@f=($"x80)x24,<>)[-24..-1]);{@s=(join("",map rand>.1?$":"*",1..80),@s);if(@s>23){$t=$f[$_],print$_?$/:"\e[H",($f[$_]|=$s[$_]&$p[$_])|($s[$_]&=~$t^$f[$_])for 0..23;select"","","",.1}redo}

该解决方案与您的JS示例的不同之处在于,模式是从上至下而不是从下至上填充的,但是我认为这是可以的,因为您在规则中未对此进行任何说明。

可以通过用\e文字ESC字符代替来减少1个字符的数量,但这使代码更难以阅读和编辑。


更新:确实设法提出了一个从下到上填充模式的版本,并且不允许雪落入模式的填充部分(如示例JS实现),但要花43个额外的字符:

chomp(@p=(@q=@f=($"x80)x24,<>)[-24..-1]);{@s=(join("",map rand>.1?$":"*",1..80),@s);if(@s>23){my$q;$q[-1-$_]=($q|=$p[-$_]&~$f[-$_])for@a=0..23;print$_?$/:"\e[H",($f[$_]|=$s[$_]&$p[$_]&~$q[$_])|($s[$_]&=~$f[$_])for@a;select"","","",.1}redo}

通过使用掉落的雪花穿过模式的填充部分(与规范匹配,但与示例实现不匹配),替换($s[$_]&=~$f[$_])为just $s[$_]将节省11个字符。


好吧,由于一周后我似乎仍然处于领先地位,我想我应该解释一下我的解决方案如何鼓励更多的比赛。(注意:此说明适用于196字符的自上而下填充版本。稍后我可能会对其进行修改以包括其他版本。)

首先,我的解决方案所基于的一个大技巧是,由于ASCII字符代码的排列方式,ASCII代码中空格的1位恰好是空格中ASCII码的子集。星号。

因此,以下表达式是正确的:" " & "*" eq " "" " | "*" eq "*"。这就是让我使用按位字符串操作来组合场景的静态和运动部分而不必循环各个字符的原因。

因此,在此之前,让我们回顾一下代码。这是它的简化版本:

chomp(@p = (@f = ($" x 80) x 24, <ARGV>)[-24..-1]);
{
    @s = (join('', map((rand > 0.1 ? $" : '*'), 1..80)), @s);
    if (@s > 23) {
        foreach (0 .. 23) {
            $t = $f[$_];
            print( $_ ? $/ : "\e[H" );
            print( ($f[$_] |= $s[$_] & $p[$_]) | ($s[$_] &= ~$t ^ $f[$_]) );
        }
        select '', '', '', 0.1;
    }
    redo;
}

第一行设置数组@f(用于“固定”)和@p(用于“模式”)。 @f将形成显示器的固定部分,开始时除了空格外什么都没有,而未@p直接显示的则包含输入模式;随着动画的进行,我们将添加越来越多的星号,@f直到最终看起来像@p

具体来说,@f = ($" x 80) x 23设置@f为24个字符串,每个字符串80个空格。(这$"是一个特殊的Perl变量,其默认值恰好是一个空格。)然后,获取此列表,使用readline运算符将输入行附加到该<>行,获取此组合列表的最后24行并将其分配给@p:一种@p用空白行填充的紧凑方法,以便该图案出现在应有的位置。最后,我们chomp在输入行中@p删除所有尾随的换行符,以便它们以后不会引起问题。

现在,让我们看一下主循环。事实证明,这{...;redo}是写无限循环的一种比while(1){...}甚至甚至更短的方法for(;;){...},尤其是在我们之前省略分号的情况下,redo因为它紧跟在一个if块之后。

主循环的第一行引入了数组@s(当然是“ snow”),该数组在每次迭代时都添加了一个随机的80个字符的字符串,其中包含90%的空格和10%的星号。(为了节省一些字符,我实际上从未在@s数组末尾弹出多余的行,因此它会越来越长。最终,由于数组太长而无法容纳在内存中,这会使程序停止运行,但是那会花费的时间将比大多数人观看该动画要长得多。在pop@s;之前添加一条声明select将以7个字符的费用解决该问题。)

主循环的其余部分包装在一个if块中,因此只有在@s数组包含至少24行时才运行。这是符合规范的简单方法,该规范要求整个显示器从一开始就被大雪覆盖,并且还稍微简化了按位操作。

接下来是一个foreach循环,在高尔夫版本中,实际上是带有for 0..23修饰符的单个语句。由于循环的内容可能需要一些解释,因此我将在下面对其进行一些分解:

foreach (0 .. 23) {
    print $_ ? $/ : "\e[H";     # move cursor top left before first line, else print newline
    $t = $f[$_];                # save the previous fixed snowflakes
    $f[$_] |= $s[$_] & $p[$_];  # snowflakes that hit the pattern become fixed 
    $s[$_] &= ~$t ^ $f[$_];     # ...and are removed from the moving part
    print $f[$_] | $s[$_];      # print both moving and fixed snowflakes ORed together
}

首先,$_是Perl中的默认循环计数器变量,除非指定了另一个变量。在这里,它从0到23,即在显示框架的24行上。 $foo[$_]表示$_数组中由索引的元素@foo

在去高尔夫球循环的第一行上,我们打印换行符(方便地从$/特殊变量获取),或者在$_等于0 时打印字符串"\e[H",其中\e表示ESC字符。这是一个ANSI终端控制代码,它将光标移动到屏幕的左上角。从技术上讲,如果我们假设使用特定的屏幕尺寸,则可以忽略不计,但是我将其保留在此版本中,因为这意味着我不必调整终端的大小即可运行动画。

在该$t = $f[$_]行上,我们只是将的当前值保存$f[$_]在“临时”变量(因此$t)中,然后在下一行中进行可能的更改,然后$s[$_] & $p[$_]给出下雪和输入模式的交集(按位与),以及|=运算符OR进入固定输出线$f[$_]

在其下面的行上,$t ^ $f[$_]给出的前一个值和当前值的按位XOR $f[$_],即,如果有的话,我们在上一行中更改的位的列表,并用两个~否定输入字符串来取反输出。因此,我们得到的是一个位掩码,所有位都设置为1,除了我们$f[$_]在上一行中刚刚添加的位。将该位掩码与$s[$_]与,然后从中删除那些位;实际上,这意味着当飘落的雪花填充固定模式的孔时,会从飘落的雪花阵列中删除。

最后,print $f[$_] | $s[$_](在高尔夫球版本中,通过对前两行进行“或”运算来实现)仅在当前行上打印固定雪花和移动雪花的并集(按位或)。

剩下的要解释的是select '', '', '', 0.1内部循环下面的内容。这只是在Perl中睡眠0.1秒的一种笨拙的方法;由于某些愚蠢的历史原因,标准Perl sleep命令具有一秒钟的分辨率,并且sleepTime::HiRes模块中导入更好的字符比滥用4-argselect需要更多的字符。


似乎有一个小错误:不使用第24行,而ASCII艺术的底线是第23行。它实际上应该先填充下部空间,然后再填充上部空间,尽管我最初并未指定。
2011年

@PleaseStand:我将代码更改为全部使用24行(并顺便删除了say),但要额外花2个字符。不过,我认为我不能轻易更改填充顺序;我的实现从根本上与之相关。
Ilmari Karonen

很好的解释!希望我能再次投票赞成。
狄龙考尔

@PleaseStand:我确实设法制作了一个自下而上的填充版本,现在看起来与您的JS示例几乎相同;它比自上而下的条目长一点,但比到目前为止的其他条目还短。
Ilmari Karonen 2011年

3

HTML和JavaScript,436个​​字符

在输入之前添加:

<body onload="for(a=[],b=[],c=document.body.firstChild,e=c[H='innerHTML'].split(N='\n'),f=e.length-1,g=24,h=g-f;f--;)for(X=80;X--;)b[80*(h+f)+X]='*'==e[f][X];for(setInterval(F='for(y=24;y--;)for(x=80;x--;)if(a[w=80*y+x]){d=1;if(b[w])for(d=0,z=y+1;24>z;++z)b[s=80*z+x]&&!a[s]&&(d=1);d&&(a[w]=0,a[w+80]=1)}for(x=80;x--;).1>Math.random(i=0)&&(a[x]=1);for(t=\'\';1920>i;++i)t+=\'* \'[+!a[i]],79==i%80&&(t+=N);c[H]=t',67);g--;)eval(F)"><pre>

每个示例都可以看到它的运行情况:代码高尔夫堆栈溢出徽标圣诞树。Internet Explorer用户需要运行版本9并将“文档模式”设置为“ IE9标准”(使用F12开发人员工具),此提交才能正常工作。


1
这三个似乎都坏了?pasteall.org/pic/show.php?id=66297
CoDEmanX 2014年

1

Python,299个字符

假设换行符包含在80个字符的限制内,则这应符合规则。

import random,sys,time
C=1920
v=_,x=' *'
a=['']*C
f=lambda n:[random.choice(e*9+x)for e in _*n]
for e in sys.stdin:a+="%-80s"%e
a=a[-C:]
s=f(C)
while 1:
 z=0;t=''
 for e in s:
    t+=v[x<a[z]or e>_]
    if(e>_<a[z])>(x in a[z+80::80]):a[z]='+'
    t+=z%80/79*'\n';z+=1
 print t;s=f(80)+s[:-80];time.sleep(.1)

如果输入的任何行正好是80个字符(加上换行符),则输出将变得混乱。我已请PleaseStand澄清是否可以。
Ilmari Karonen 2011年

这是因为我在每行80个字符的行末添加了换行符。这里的描述是模棱两可的,因为它指定必须包含换行符,而且还可以假定终端的字符宽度为80个字符(在这种情况下,可以省略换行符并依赖于自动换行)。
hallvabo 2011年

我认为实际的问题是,在将输入行填充到80个字符之前,您不会去除换行符,因此,一个80个字符加上换行符的输入实际上实际上会将81个字符附加到后面a,从而使索引混乱。我刚才试了一下,它看起来像更换%e%e.rstrip()第6个修复该问题。(当然,可能会有更短的修复;我
不太

如果您想支持81个字符行,只需更改数字,就可以了。只要您保持每行少于100个字符,它就不会更改字符数:-)
hallvabo 2011年

如果我将代码更改为使用81个字符的行,那么它将无法在80列的终端上正确运行,现在可以吗?您正在生成80列输出就很好了,只是您没有正确接受80列输入。尝试一下:创建一个包含几行,每行带有80个星号的输入文件,然后看看会发生什么。它不应该那么难:我的解决方案就可以了。
Ilmari Karonen 2011年

0

Java,625个字符

import java.io.*;import java.util.*;class s extends TimerTask {int _c,_k;char _i[],_o[];boolean _b[];public s(String f) throws IOException {_i=new char[23*80];_o=new char[80];_b=new boolean [23*80];BufferedReader br = new BufferedReader(new FileReader(f));while (br.read(_i,_c++*80,80)!=-1);} public void run(){_k=--_k<0?_c:_k;for(int i=0;i<80;_b[_k*80+i]=Math.random()>0.9?true:false,i++);for(int m=0;m<_c;m++){for(int n=0;n<80;_o[n]=_b[(_k+m)%_c*80+n]?'*':_i[m*80+n],n++);System.out.println(_o);}}public static void main(String[] a) throws IOException{Timer timer=new Timer();timer.scheduleAtFixedRate(new s(a[0]),0,500);}}

Java中的简单解决方案。


很好,但我认为您不符合规范。特别是,您从一开始就展示了整个模式-它不像示例演示中的“从下雪形成”。(诚​​然,这可以在问题中得到更好的解释。您确实需要观看演示以了解它应该做什么。)而且,您的帧速率太慢了,您不是从一个大雪覆盖的屏幕开始的,假定输入格式与给定的示例不同,那么您真的应该在帧之间重置光标位置(打印"\033[H"应该这样做)。
Ilmari Karonen 2011年
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.