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.

На першому рядку циклу де-гольфу ми друкуємо або новий рядок (зручно отримувати зі $/спеціальної змінної), або $_рядок "\e[H", коли дорівнює 0, рядок , де \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 секунди в Перлі; з певної історичної причини, стандартна sleepкоманда Perl має роздільну роздільну здатність, і імпорт кращого sleepз Time::HiResмодуля займає більше знаків, ніж зловживання 4-аргументомselect .


Здається, незначна помилка: 24-й рядок не використовується, а нижній рядок мистецтва ASCII - це 23-й рядок. І він дійсно повинен заповнювати нижні пробіли, перш ніж заповнити верхні, хоча я спочатку цього не вказував.
Прошу продовжити

@PleaseStand: Я змінив код, щоб використовувати всі 24 рядки (і, до речі, позбувся say), ціною 2 додаткових символів. Я не думаю, що я можу легко змінити порядок заповнення; моя реалізація досить принципово пов'язана з цим.
Ільмарі Каронен

Чудове пояснення! Я б хотів, щоб я міг знову проголосувати за цей запис.
Dillon Cower

@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>

Дивіться його на кожному прикладі: гольф коду , логотип стека переповнення , ялинки . Користувачам 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 виправляє проблему. (Звичайно, цілком може бути коротший виправлення; мені не подобається Python golf.)
Ілмарі Каронен

Якщо ви хочете підтримати 81 лінійку знаків, просто змініть цифри, і це вийде чудово. Поки ви залишаєтесь менше 100 символів на лінію, це не змінить кількість знаків :-)
hallvabo

Якщо я зміню ваш код на використання 81-char рядків, він не буде працювати належним чином на 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.