ASCII анимированная снежная сцена


22

Напишите самую короткую программу, чтобы превратить любое произведение искусства ASCII в анимированную снежную сцену, которая начинает формироваться из падающего снега ( пример JavaScript без игры в гольф, последнее обновление 2011-12-19).

Спецификация ввода : Ваша программа должна принимать произвольные комбинации пробелов, звездочек и переносов. Ввод будет содержать не более 23 строк и 80 символов в строке. Пустых строк не будет, но строки могут состоять только из пробелов. Один завершающий перевод строки будет включен и должен игнорироваться.

Вывод : Вывод символов ASCII (пробелы, звездочки) и управляющих кодов (возврат каретки, перевод строки, коды выхода ANSI и т. Д.) Для текстовой консоли операционной системы или эмулятора терминала, пока пользователь не завершит программу вручную. Вы можете считать, что окно терминала имеет размер 80x24 символа, если ваша операционная система разрешает эту настройку.

Правила :

  • Анимация должна быть плавной и быстрой (желательно 15 кадров в секунду).
  • Плотность снега должна составлять от 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
Третья рождественская елка сломана.
Бобби

Отличный вызов! Я думаю, что правила должны быть перечислены для облегчения ссылки, и я не понимаю третьего и шестого правила ...
hallvabo

@hallvabo Я разъяснил эти два правила, последнее, добавив помеченную цифру.
Пожалуйста, установите

Запрос на уточнение: включен ли перевод строки в максимальную длину строки из 80 символов или это максимум 80 символов плюс перевод строки? (Я предполагал последнее, но некоторые представления, кажется, предполагали первое.)
Илмари Каронен

@IlmariKaronen Последний.
Пожалуйста, установите

Ответы:


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 тем, что шаблон заполняется сверху вниз, а не снизу вверх, но я предполагаю, что это нормально, поскольку вы ничего не сказали об этом в правилах.

Тривиальное сокращение на 1 символ может быть получено путем замены \eбуквенным символом ESC, но это делает код намного сложнее для чтения и редактирования.


Обновление: Я сделал удается придумать версию , которая заполняет шаблон из снизу вверх, и не позволяет снег падать через заполненные части рисунка, как и в примере реализации 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[$_])на только $s[$_]что сэкономит 11 символов, позволяя падающему снегу проходить через заполненные части шаблона (что соответствует спецификации, но не реализации примера).


Хорошо, так как я все еще продолжаю лидировать в гонке через неделю, думаю, я должен объяснить, как работает мое решение, чтобы стимулировать конкуренцию. (Примечание. Это объяснение относится к заполнению сверху вниз по 196 символам. Я могу изменить его, чтобы позже включить другую версию.)

Прежде всего, одна большая хитрость, на которой основано мое решение, заключается в том, что из-за того, как расположены коды символов 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устанавливает @f24 строки по 80 пробелов в каждой. ( $"это специальная переменная Perl, значением которой по умолчанию является пробел.) Затем мы берем этот список, добавляем к нему строки ввода с помощью оператора readline <>, берем последние 24 строки этого комбинированного списка и присваиваем его @p: компактный способ заполнить @pпустыми строками, чтобы шаблон появился там, где и должен. Наконец, мы chompвводим строки, @pчтобы удалить любые завершающие символы новой строки, чтобы они не вызывали проблем позже.

Теперь давайте посмотрим на основной цикл. Оказывается, это {...;redo}более короткий способ написать бесконечный цикл, чем while(1){...}или даже for(;;){...}, особенно если мы пропустили точку с запятой раньше, redoпотому что она следует сразу за ifблоком.

Первая строка основного цикла представляет массив @s(конечно, для «снега»), к которому добавляется случайная 80-символьная строка из 90% пробелов и 10% звездочек на каждой итерации. (Чтобы сохранить несколько символов, на самом деле я никогда не добавляю лишних строк в конец @sмассива, поэтому он становится все длиннее и длиннее. В конечном итоге это приведет к остановке программы, поскольку массив становится слишком длинным, чтобы поместиться в памяти, но это это займет гораздо больше времени, чем большинство людей будут смотреть эту анимацию. Добавление pop@s;заявления до того, как selectэто исправит это за счет семи символов.)

Остальная часть основного цикла заключена в 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.

В первой строке цикла «de-golfed» мы печатаем либо новую строку (удобно полученную из $/специальной переменной), либо, когда $_равен 0, строку "\e[H", где \eобозначает символ ESC. Это код управления терминалом ANSI, который перемещает курсор в верхний левый угол экрана. Технически, мы могли бы опустить это, если бы мы предполагали определенный размер экрана, но я сохранил его в этой версии, поскольку это означает, что мне не нужно изменять размер моего терминала для запуска анимации.

В $t = $f[$_]строке мы просто сохраняем текущее значение $f[$_]во «временной» переменной (следовательно $t), прежде чем потенциально изменить его в следующей строке, где $s[$_] & $p[$_]дается пересечение (поразрядное И) падающего снега и входного шаблона, а также |=оператор ИЛИ это в фиксированной выходной линии $f[$_].

В строке ниже это $t ^ $f[$_]дает побитовое значение XOR предыдущего и текущего значений $f[$_], то есть список битов, которые мы изменили в предыдущей строке, если таковые имеются, и отрицание любой из входных строк с отрицанием ~вывода. Таким образом, мы получаем битовую маску со всеми битами, установленными в 1, кроме тех, к которым мы только что добавили $f[$_]в предыдущей строке. И добавление этой битовой маски $s[$_]удаляет эти биты из нее; фактически это означает, что когда падающая снежинка заполняет отверстие в фиксированном узоре, она удаляется из массива падающего снега.

Наконец, print $f[$_] | $s[$_](который в версии для гольфа реализован простым объединением двух предыдущих строк в ИЛИ) просто печатает объединение (побитовое ИЛИ) фиксированных и движущихся снежинок на текущей строке.

Еще одна вещь, которую нужно объяснить, это select '', '', '', 0.1нижняя внутренняя петля. Это просто отличный способ спать 0,1 секунды в Perl; по какой-то глупой исторической причине стандартная sleepкоманда Perl имеет разрешение в одну секунду, и импорт лучшего sleepиз Time::HiResмодуля занимает больше символов, чем злоупотребление 4-аргументомselect .


Кажется, есть небольшая ошибка: 24-я строка не используется, а нижняя строка в ASCII-графике - это 23-я строка. И это действительно должно заполнить нижние пробелы, прежде чем заполнить верхние, хотя я изначально не указывал это.
Пожалуйста, установите

@PleaseStand: я изменил код, чтобы использовать все 24 строки (и случайно избавился от say), за счет 2 дополнительных символов. Я не думаю, что смогу изменить порядок заполнения очень легко; моя реализация довольно фундаментально связана с этим.
Ильмари Каронен

Отличное объяснение! Я хотел бы снова проголосовать за эту запись.
Диллон Кауэр

@PleaseStand: Я на самом деле так удалось сделать снизу вверх вариант заполнения, который теперь выглядит почти так же , как ваш пример JS; это немного длиннее, чем сверху вниз, но все же короче, чем другие записи до сих пор.
Ильмари Каронен

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>

Смотрите его для каждого примера: код гольфа , логотип Stack Overflow , новогодние елки . Пользователям Internet Explorer необходимо запустить версию 9 и установить для «Режима документа» значение «Стандарты IE9» (с использованием инструментов разработчика F12), чтобы эта отправка работала корректно.


1
Все 3, кажется, сломаны? pasteall.org/pic/show.php?id=66297
CoDEmanX

1

Питон, 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 уточнить, все ли в порядке.
Ильмари Каронен

Это потому, что я добавляю новую строку в конце каждой строки шириной 80 символов. Описание здесь неоднозначно, так как оно указывает, что должны быть включены новые строки, но также и то, что можно предположить, что терминал имеет ширину 80 символов (в этом случае можно пропустить новые строки и зависеть от автоматического переноса).
hallvabo

Я думаю, что реальная проблема заключается в том, что вы не удаляете символы новой строки перед заполнением входных строк до 80 символов, так что вход 80-символов-плюс-новая строка заканчивает тем, что фактически добавляет 81 символ к aи таким образом портит индексацию. Я просто попытался это, и это выглядит как замена %eс %e.rstrip()на линии 6 исправляет проблему. (Конечно, может быть более короткое исправление; я не очень хорош в питон-гольфе.)
Илмари Каронен

Если вы хотите поддерживать 81 символьную строку, просто измените числа, и все работает отлично. Пока вы не
наберете

Если я изменю ваш код на использование строк из 81 символа, он не будет работать правильно на терминале с 80 столбцами, не так ли? Вы производите 80-колоночный вывод просто отлично, просто вы неправильно воспринимаете 80-колоночный ввод . Попробуйте: создайте входной файл с парой строк по 80 звездочек на каждой и посмотрите, что получится. Это не должно быть так сложно: мое решение справляется с этим просто отлично.
Ильмари Каронен

0

Ява, 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"должна это сделать).
Илмари Каронен
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.