11
\$\begingroup\$

In the highly underrated Steampunk novel The Difference Engine, the equivalent of cinema houses delivered a pixelated moving image displayed by tiles which could be flipped mechanically. The control engine for orchestrating the movement of the these tiles was a large noisy machine controlled by a deck of punched cards.

Your task is to emulate such an engine and display a pixelated animation as specified by an input file. The input consists of lines in a fixed-width format, but you may assume whatever is convenient for a line-ending indication. The format is:

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)

The input is explicitly sequenced (if you ever drop your deck of cards on the floor, you'll thank me for this part). That means the program must perform a stable sort of the input lines using the sequence field as a sort key. Lines with the same sequence number must maintain their original relative ordering. (It should work with an unstable sort, if you append the actual line number to the key.) A blank sequence field should be interpreted as lower than any number (ascii collation sequence).

A single statement line can only affect a single y coordinate, but may specify a contiguous range of x values. The ending x value may be left blank or may be identical to the initial value in order to affect a single pixel.

The opcode is a hexadecimal digit which specifies the Universal Binary Function Code which is used as a rasterop. The argument is 0 or 1. The raster operation performed is

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

So the original value of the pixel enters as X in the UBF table, and the argument value from the statement enters as Y. The result of this function is the new value of the pixel. And this operation is performed upon each x,y pair from xx,YY to XX,YY specified in the statement. The range specified by xx and XX includes both end-points. So

0000 0 010F1

should set pixels 0,1,2,3,4,5,6,7,8,9,10 on row 0.

Output dimensions (m x n) should be 20 x 20 at a minimum, but may be larger if desired. But the grain should show, you know? It's supposed to be pixelated. Both graphical and ASCII-art output are acceptable.

If for example, we wanted to make an image of a pixelated figure:

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

   ###
   # #
   # #

If we draw him with a bit-flipping op, like XOR, it can be drawn and erased regardless of whether the screen is black or white.

    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

Duplicating this sequence will make the figure appear and disappear.

NMM is Not Mickey Mouse

A larger animation can be composed out-of-order, by specifying different "shots" in the sequence field.

   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

Producing:

black/white vs white/black

This is so shortest program (by byte-count) wins. Bonus (-50) if the engine makes clickity-clack noises.

\$\endgroup\$
12
  • 3
    \$\begingroup\$ Normally one requests clarification by posting to the sandbox. Are you trying to shut the sandbox down? \$\endgroup\$ Commented May 16, 2014 at 5:02
  • 5
    \$\begingroup\$ For me personally, the sandboxes are a dead-end. I'm too good at procrastinating to finish them up. Here, live, I cannot ignore the fire under my butt. \$\endgroup\$ Commented May 16, 2014 at 5:04
  • 1
    \$\begingroup\$ How does the Boolean connector work? Does it only join lines with the same sequence number? If they're mixed, is there some form of operator precedence? Do you have any test cases which rely on Boolean connectors? Why does the test case you posted not have any sequence numbers? Is the ending x coord always inclusive? \$\endgroup\$ Commented May 16, 2014 at 13:10
  • 5
    \$\begingroup\$ Here are some clickety clack noises. Do I get a bonus? ;-) \$\endgroup\$ Commented May 16, 2014 at 18:28
  • 1
    \$\begingroup\$ For sound, are you thinking something like train station flipboards? E.g. solari board at gare du nord train station in paris or Split-flap Display - DIY driver circuit. Or are you thinking more mechanical relay sounds? \$\endgroup\$ Commented May 20, 2014 at 2:10

2 Answers 2

3
\$\begingroup\$

Mathematica, 306 281 bytes

This expects the input string to be stored in variable 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}]]]

And here with some whitespace:

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}]
   ]]

This got pretty damn long. This challenge contained a lot of fiddly details, and especially the input parsing takes a lot of code in Mathematica (almost half of it, 137 bytes, are just parsing the input). I ended up switching the language twice before settling on Mathematica (I thought I could save on the input parsing by using Ruby, but then I realised the result needs to be animated, so I went back to Mathematica).

\$\endgroup\$
2
\$\begingroup\$

Ungolfed Postscript example

This is a "protocol-prolog"-style program, so the data immediately follows in the same source file. Animated gif files can be produced with ImageMagick's convert utility (uses 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
\$\endgroup\$
1
  • \$\begingroup\$ The bounding box info was discovered by running gs -sDEVICE=bbox clack.ps. \$\endgroup\$ Commented May 26, 2014 at 23:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.