# Steampunk: Clacker animation

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

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:

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

• Normally one requests clarification by posting to the sandbox. Are you trying to shut the sandbox down? – John Dvorak May 16 '14 at 5:02
• 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. – luser droog May 16 '14 at 5:04
• 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? – Peter Taylor May 16 '14 at 13:10
• Here are some clickety clack noises. Do I get a bonus? ;-) – Digital Trauma May 16 '14 at 18:28
• 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? – Scott Leadley May 20 '14 at 2:10

## 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).

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

• The bounding box info was discovered by running gs -sDEVICE=bbox clack.ps. – luser droog May 26 '14 at 23:25