Steampunk: animacja Clackera

11

W bardzo niedocenianej powieści Steampunk The Difference Engine odpowiednik domów kinowych zapewniał pikselowany ruchomy obraz wyświetlany za pomocą płytek, które można było odwracać mechanicznie. Silnikiem sterującym ruchem tych płytek była duża, hałaśliwa maszyna kontrolowana przez talię perforowanych kart.

Twoim zadaniem jest emulowanie takiego silnika i wyświetlanie animacji pikselowej określonej przez plik wejściowy. Dane wejściowe składają się z linii w formacie o stałej szerokości, ale można założyć, co jest wygodne dla wskazania końca linii. Format to:

SSSSYYxxXXOA
SSSS: 4 digit sequence no. may be padded by blanks or all blank
    YY: the y coordinate affected by this line (descending, top is 0, bottom is m-1)
      xx: the starting x coordinate
        XX: the ending x coordinate
          O: hexadecimal opcode
           A: argument (0 or 1)

Dane wejściowe są wyraźnie uporządkowane (jeśli kiedykolwiek upuścisz swoją talię kart na podłogę, podziękujesz mi za tę część). Oznacza to, że program musi wykonać stabilny rodzaj linii wejściowych, używając pola sekwencji jako klucza sortowania. Linie o tym samym numerze sekwencji muszą zachować swoje pierwotne względne uporządkowanie. (Powinno to działać z niestabilnym sortowaniem, jeśli do klawisza zostanie dołączony rzeczywisty numer linii.) Puste pole sekwencji powinno być interpretowane jako niższe niż dowolna liczba (sekwencja sortowania ascii).

Pojedyncza linia instrukcji może wpływać tylko na jedną współrzędną y, ale może określać ciągły zakres wartości x. Końcowa wartość x może pozostać pusta lub może być identyczna z wartością początkową, aby wpłynąć na pojedynczy piksel.

Kod op jest cyfrą szesnastkową, która określa uniwersalny binarny kod funkcji, który jest używany jako rasterop. Argument wynosi 0 lub 1. Wykonywana operacja rastrowa to

pixel = pixel OP argument          infix expression
         --or-- 
        OP(pixel, argument)        function call expression

Tak więc oryginalna wartość piksela jest wprowadzana jako X w tabeli UBF, a wartość argumentu z instrukcji jest wprowadzana jako Y. Wynikiem tej funkcji jest nowa wartość piksela. Ta operacja jest wykonywana na każdej parze x, y od xx, YY do XX, YY określonej w instrukcji. Zakres określony przez xx i XX obejmuje oba punkty końcowe. Więc

0000 0 010F1

powinien ustawić piksele 0,1,2,3,4,5,6,7,8,9,10 w rzędzie 0.

Wymiary wyjściowe ( m x n ) powinny wynosić co najmniej 20 x 20, ale w razie potrzeby mogą być większe. Ale ziarno powinno pokazać, wiesz? Ma być pikselowany . Dopuszczalne są zarówno wyjścia graficzne, jak i ASCII-art.

Jeśli na przykład chcieliśmy stworzyć obraz pikselowanej figury:

  #   #
   ###
   ##
   ####
    #
#### ####
   # #

   ###
   # #
   # #

Jeśli narysujemy go za pomocą operacji odwracania, takiej jak XOR, można go narysować i usunąć bez względu na to, czy ekran jest czarny, czy biały.

    00020261
     0 6 661
     1 3 561
     2 3 461
     3 3 661
     4 4 461
     5 0 361
     5 5 861
     6 3 361
     6 5 561
     8 3 561
     9 3 361
     9 5 561
    10 3 361
    10 5 561

Powielenie tej sekwencji spowoduje, że postać pojawi się i zniknie.

NMM to nie Myszka Miki

Większą animację można komponować poza kolejnością, określając różne „strzały” w polu sekwencji.

   100 016F0
   101 016F0
   102 016F0
   103 016F0
   104 016F0
   105 016F0
   106 016F0
   107 016F0
   108 016F0
   109 016F0
   110 016F0
   111 016F0
   112 016F0
   113 016F0
   114 016F0
   115 016F0
   200020261
   2 0 6 661
   2 1 3 561
   2 2 3 461
   2 3 3 661
   2 4 4 461
   2 5 0 361
   2 5 5 861
   2 6 3 361
   2 6 5 561
   2 8 3 561
   2 9 3 361
   2 9 5 561
   210 3 361
   210 5 561
    00020261
     0 6 661
     1 3 561
     2 3 461
     3 3 661
     4 4 461
     5 0 361
     5 5 861
     6 3 361
     6 5 561
     8 3 561
     9 3 361
     9 5 561
    10 3 361
    10 5 561
   300020261
   3 0 6 661
   3 1 3 561
   3 2 3 461
   3 3 3 661
   3 4 4 461
   3 5 0 361
   3 5 5 861
   3 6 3 361
   3 6 5 561
   3 8 3 561
   3 9 3 361
   3 9 5 561
   310 3 361
   310 5 561
    00020261
     0 6 661
     1 3 561
     2 3 461
     3 3 661
     4 4 461
     5 0 361
     5 5 861
     6 3 361
     6 5 561
     8 3 561
     9 3 361
     9 5 561
    10 3 361
    10 5 561

Produkcja:

czarny / biały vs. biały / czarny

To jest więc wygrywa najkrótszy program (według liczby bajtów). Premia (-50), jeśli silnik wydaje odgłosy kliknięcia.

luser droog
źródło
3
Zwykle żąda się wyjaśnień, wysyłając wiadomość do piaskownicy. Czy próbujesz wyłączyć piaskownicę?
John Dvorak
5
Dla mnie osobiście piaskownice są ślepym zaułkiem. Jestem zbyt dobry w zwlekaniu, aby je skończyć. Tutaj, żyj, nie mogę zignorować ognia pod tyłkiem.
luser droog
1
Jak działa łącznik logiczny? Czy łączy tylko linie o tym samym numerze porządkowym? Jeśli są mieszane, czy istnieje jakaś forma pierwszeństwa operatora? Czy masz jakieś przypadki testowe, które opierają się na złączach Boolean? Dlaczego opublikowany przypadek testowy nie ma żadnych numerów sekwencji? Czy xkoordynacja końcowa jest zawsze włączająca?
Peter Taylor
5
Oto niektóre odgłosy kliknięcia . Czy dostanę bonus? ;-)
Cyfrowa trauma
1
Jeśli chodzi o dźwięk, czy myślisz o czymś takim jak tablice na dworzec kolejowy? Np. Tablica solari na dworcu Gare du Nord w Paryżu lub wyświetlacz z klapą dzieloną - obwód dla majsterkowiczów . A może myślisz bardziej mechaniczne dźwięki przekaźników?
Scott Leadley,

Odpowiedzi:

3

Mathematica, 306 281 bajtów

To oczekuje, że ciąg wejściowy zostanie zapisany w zmiennej i

ListAnimate[ArrayPlot/@FoldList[({n,y,x,X,o,a}=#2;MapAt[IntegerDigits[o,2,4][[-1-FromDigits[{#,a},2]]]&,#,{y+1,x+1;;X+1}])&,Array[0&,{20,20}],ToExpression/@MapAt["16^^"<>#&,StringTrim/@SortBy[i~StringSplit~"\n"~StringCases~RegularExpression@"^....|..(?!.?$)|.",{#[[1]]&}],{;;,5}]]]

A tutaj z pewnymi odstępami:

ListAnimate[ArrayPlot /@ FoldList[(
     {n, y, x, X, o, a} = #2;
     MapAt[
      IntegerDigits[o, 2, 4][[-1 - FromDigits[{#, a}, 2]]] &,
      #,
      {y + 1, x + 1 ;; X + 1}
      ]
     ) &,
   Array[0 &, {20, 20}],
   ToExpression /@ 
    MapAt["16^^" <> # &, 
     StringTrim /@ 
      SortBy[i~StringSplit~"\n"~StringCases~
        RegularExpression@"^....|..(?!.?$)|.", {#[[1]] &}], {;; , 5}]
   ]]

To cholernie długo trwało. Wyzwanie to zawierało wiele dziwacznych szczegółów, a zwłaszcza parsowanie danych wejściowych zajmuje dużo kodu w Mathematica (prawie połowa z nich, 137 bajtów, po prostu analizuje dane wejściowe). Skończyłem zmieniać język dwa razy, zanim zdecydowałem się na Mathematica (myślałem, że mogę zaoszczędzić na analizie danych wejściowych za pomocą Ruby, ale potem zdałem sobie sprawę, że wynik musi być animowany , więc wróciłem do Mathematica).

Martin Ender
źródło
2

Przykład Postscript Ungolfed

Jest to program w stylu „protokołu-prologu”, więc dane natychmiast znajdują się w tym samym pliku źródłowym. Animowanych plików GIF mogą być produkowane z ImageMagick za convertużytkowego (używa Ghostscript) convert clack.ps clack.gif.

%%BoundingBox: 0 0 321 321

/t { token pop exch pop } def
/min { 2 copy gt { exch } if pop } def
/max { 2 copy lt { exch } if pop } def

/m [ 20 { 20 string }repeat ] def
/draw { change {
        m {} forall 20 20 8 [ .0625 0 0 .0625 0 0 ] {} image showpage
    } if } def

%insertion sort from https://groups.google.com/d/topic/comp.lang.postscript/5nDEslzC-vg/discussion
% array greater_function insertionsort array
/insertionsort
{ 1 1 3 index length 1 sub
    { 2 index 1 index get exch % v, j
        { dup 0 eq {exit} if
            3 index 1 index 1 sub get 2 index 4 index exec
            {3 index 1 index 2 copy 1 sub get put 1 sub}
            {exit} ifelse
        } loop
        exch 3 index 3 1 roll put
    } for
    pop
} def

/process {
    x X min 1 x X max { % change? x
        m y get exch  % row-str x_i
        2 copy get  % r x r_x 
        dup         % r x r_x r_x
        0 eq { 0 }{ 1 } ifelse  % r x r_x b(x)
        2 mul a add f exch neg bitshift 1 and   % r x r_x f(x,a)
        0 eq { 0 }{ 255 } ifelse  % r x r_x c(f)
        exch 1 index % r x c(f) r_x c(f)
        ne { /change true def } if
        put
    } for
    draw
} def

{ [ {
     currentfile 15 string
         dup 2 13 getinterval exch 3 1 roll
         readline not{pop pop exit}if
    pop
    [ exch
     /b exch dup 0 1 getinterval exch
     /n exch dup 1 1 getinterval exch
     /seq exch dup 2 4 getinterval exch
     /y exch dup 6 2 getinterval t exch
     /x exch dup 8 2 getinterval t exch
     /X exch dup 10 2 getinterval dup (  ) ne { t exch }{pop 2 index exch} ifelse
     /f exch dup 12 get (16#?) dup 3 4 3 roll put t exch
     /a exch 13 get 48 sub
     /change false def
    >>
}loop ]
dup { /seq get exch /seq get exch gt } insertionsort
true exch
{ begin
    b(A)eq{
        { process } if
    }{
        b(O)eq{
            not { process } if
        }{
            pop
            process
        }ifelse
    }ifelse
    change
    end
} forall
    draw
} exec
   100 016F0
   101 016F0
   102 016F0
   103 016F0
   104 016F0
   105 016F0
   106 016F0
   107 016F0
   108 016F0
   109 016F0
   110 016F0
   111 016F0
   112 016F0
   113 016F0
   114 016F0
   115 016F0
   200020261
   2 0 6 661
   2 1 3 561
   2 2 3 461
   2 3 3 661
   2 4 4 461
   2 5 0 361
   2 5 5 861
   2 6 3 361
   2 6 5 561
   2 8 3 561
   2 9 3 361
   2 9 5 561
   210 3 361
   210 5 561
    00020261
     0 6 661
     1 3 561
     2 3 461
     3 3 661
     4 4 461
     5 0 361
     5 5 861
     6 3 361
     6 5 561
     8 3 561
     9 3 361
     9 5 561
    10 3 361
    10 5 561
   300020261
   3 0 6 661
   3 1 3 561
   3 2 3 461
   3 3 3 661
   3 4 4 461
   3 5 0 361
   3 5 5 861
   3 6 3 361
   3 6 5 561
   3 8 3 561
   3 9 3 361
   3 9 5 561
   310 3 361
   310 5 561
    00020261
     0 6 661
     1 3 561
     2 3 461
     3 3 661
     4 4 461
     5 0 361
     5 5 861
     6 3 361
     6 5 561
     8 3 561
     9 3 361
     9 5 561
    10 3 361
    10 5 561
0000 0 515F1
0000 1 11501
0000 1 115F1
luser droog
źródło
Informacje o obwiedni zostały odkryte przez uruchomienie gs -sDEVICE=bbox clack.ps.
luser droog