Animate tacky light decorations

22

3

This challenge is in honor of the tacky Christmas lights at my in laws' house.


The challenge is to create a graphical output showing the decoration in "real time".

The video (gif or other format) will have n-by-m vertical and horizontal "lights". 5 <= m,n <= 40. The frame size and resolution may vary dependent on n and m, but must be at least 50x50 pixels for n,m=5 (vector graphics is OK). A picture with n=6 and m=5 will look something like this:

enter image description here


The decoration:

Colors:

All lights will have one of the following 6 RGB-colors {255,0,0}, {0,255,0}, {0,0,255}, {255,255,0}, {0,255,255} and {255,0,255}.

Animation:

  • n and m will be taken as input on any reasonable format and in the order you like
  • The image will change every dt = 25 ms. Deviations are OK if it's due to "outside factors" such as limitation in the interpreter, slow computer etc.
    • If it's impossible to set the time step manually, then the default time step is accepted.
  • All lights will be red ({255,0,0}) at t=0.
  • There's always a 5% chance that the first light (top-left) will change color. All colors (except the color it currently has) should be equally likely.
  • Each light (except the first) will get the color of the light to its left. If the light is at the far left then it will get the color of the light on the far right on the row above. The lights are numbered as shown below. Light number k will get the color of the light number k-1.

     1  2  3  4  5  6
     7  8  9 10 11 12
    13 14 15 16 17 18
    
  • The output should in theory run for ever (unless your language / interpreter has some limitation that prevents this).

  • Please provide a sample of at least 5 seconds, preferably more in the answer (this is an encouragement, not a requirement). (A link to TIO or similar is of course OK too :D )
  • Frames, axes, grid lines etc are accepted.

6-by-5

enter image description here

15-by-30

enter image description here

Stewie Griffin

Posted 2016-12-29T14:24:57.587

Reputation: 43 471

If the interpreter is slow, should be adjust the pause time so the total time between image updates is similar to that in the examples? What about if the pause is not needed (the code is already slowe enough)? That would save bytes, maybe against the spirit of the challenge – Luis Mendo – 2016-12-29T19:59:18.710

1Since you picked the colors for ease of implementation--in languages like QBasic that have a limited builtin set of colors, is it acceptable to use the closest available colors to the ones you specified? (Red, green, blue, yellow, cyan, magenta) – DLosc – 2016-12-29T21:04:22.063

If it's impossible to use the specified colours then yes, it's ok to use the closest alternatives. If it's just a bit longer then no. r,g,y,b, etc are shorter in several languages. – Stewie Griffin – 2016-12-29T21:46:17.027

@LuisMendo, apologies for the late reply. I like the way you did it in your answer. It would be OK to use 25 ms, even if that would make the animation slower. I avoided drawnow when I implemented this in MATLAB, since the result was too slow. I think the answer must be: If it's a design choice that the interpreter has a fixed minimum time resolution of >=25 ms then it's OK. If it's due to poor/simple implementation, an online interpreter that is overloaded/slow etc. then it's not OK. – Stewie Griffin – 2016-12-30T14:54:17.190

1@Stewie Got it, thanks. And nice challenge! – Luis Mendo – 2016-12-30T15:04:48.657

How exactly did you implement this in MATLAB? For me, simply calling imshow on each step takes >.07 seconds for a 15x15 image; did you use a trick to make it faster, or is my laptop dying slowly? – Sanchises – 2017-01-04T18:29:13.290

@Sanchises I wrote it to a gif using imwrite with 'DelayTime',0 and pause(0.025) (it might be the other way around (no pause, but 'DelayTime',0.025)). After timing the gif it looks like there was an additional 3 ms delay per step. 0.07 seconds was very slow, but it might be just as slow for me. I don't have MATLAB here so I can't test it.

– Stewie Griffin – 2017-01-04T19:08:25.577

@Sanchises, have you tried running Chelsea's answer? Is imagesc equally slow?

– Stewie Griffin – 2017-01-04T19:11:16.117

Oh OK. I'll check later, but at least I know what's what. Is the delay due to MATLAB's massively slow imshow acceptable or should I write to a gif? – Sanchises – 2017-01-04T19:25:38.123

Answers

6

MATLAB, 255 210 bytes

This is my first golf, so there are probably improvements to be made.

Thanks to Luis for helping me save 45 bytes :)

function f(n,m)
c=dec2bin(1:6)-48;r=1;p=c(r,:);x=zeros(1,n*m,3);x(:,:,1)=1;while 1
x=circshift(x,[0,1,0]);if rand>0.94;r=randi(6);end
x(1,1,:) = c(r,:);imagesc(permute(reshape(x,n,m,3),[2 1 3]));pause(1/40);end

Explanation:

c=dec2bin(1:6)-48  % c is the colormap
r=1;p=c(r,:);                % p is color number r (starting at 1) from the colormap c
x=zeros(1,n*m,3);x(:,:,1)=1; % 2D matrix in the third dimension. The first layer is 1
while 1                      % while true
x=circshift(x,[0,1,0]);      % shift the vector horizontally along the second dimension
if rand()>0.94;              % 5 percent chance of changing color
k=randperm(6);k=k(k~=r);r=k(1); % Create a vector with color numbers 1..6. Discard the current color, and choose the first color
x(1,1,:) = c(r,:);           % The first light gets color number r
imagesc(permute(reshape(x,n,m,3),[2 1 3]));  % Reshape the vector so that it's a 3D matrix
% Permute it so that the dimensions are correct
% Use imagesc to display
pause(1/40)  % 0.025 seconds pause

Unfortunately, this doesn't save the animation, it just runs it. In order to save it I either need a screen capture program, or rewrite it all using imwrite. Instead, I'll provide two pictures showing different times, for n=15,m=30.

enter image description here

enter image description here

CG.

Posted 2016-12-29T14:24:57.587

Reputation: 401

1Some golfing suggestions: dec2bin([4 2 1 6 3 5])-48 instead of [1 0 0;0 1 0;0 0 1;1 1 0; 0 1 1;1 0 1]. .95 instead of 0.95. You can also replace .95 by .94 and get rid of k=k(k~=r); (because 0.94 + 0.06/6 = 0.95; see my answer for a more detailed explanation) – Luis Mendo – 2016-12-29T22:14:39.170

1Better yet, c=dec2bin(1:6)-48, as order doesn't matter – Luis Mendo – 2016-12-30T00:40:01.873

Nice first answer! However, I'm afraid I have outgolfed you by 40%.

– Sanchises – 2017-01-01T17:36:15.830

Golfed it down a bit :) Thanks for the help Luis. ! :) Well done Sanchises! – CG. – 2017-01-02T12:09:06.247

6

Mathematica, 186 161 158 bytes

(b=Table[{1,0,0},1##];Dynamic@Image[Partition[Pause@.025;If[Random[]<.06,b[[1]]={1,1,0,0}~RandomSample~3];b=RotateRight@b;b[[1]]=b[[2]];b,#],ImageSize->50#])&

Explanation

b=Table[{1,0,0},1##];

Create the initial board in 1D, filled with red. Store that in b.

Pause@.025

Pause for 25ms

If[Random[]<.06,b[[1]]={1,1,0,0}~RandomSample~3]

If a (pseudo-)random real number is less than 0.06, replace the first element of b with a random sample length 3 of the list {1,1,0,0}. (i.e. any one of {1, 1, 0}, {1, 0, 1}, {1, 0, 0}, {0, 1, 1}, {0, 1, 0}, {0, 0, 1})

b=RotateRight@b

Cyclic rotate right.

b[[1]]=b[[2]]

Change the first cell value to the second cell value (i.e. undo the shift of the first cell)

Partition[ ... ;b,#]

Partition b into (height).

Dynamic@Image[ ... ,ImageSize->50#]

Make that into a dynamic (constantly updating) image, whose width is 50(width)

Cellular automaton version (186 bytes)

(b=Table[{1,0,0},1##];Dynamic@Image[Partition[Pause@.025;If[Random[]<.06,b[[1]]={1,1,0,0}~RandomSample~3];i=2;b={#[[2-Boole[i--<0],2]]&,{},{1,1}}~CellularAutomaton~b,#],ImageSize->50#])&

Sample Output (inputs: 16, 10)

enter image description here

JungHwan Min

Posted 2016-12-29T14:24:57.587

Reputation: 13 290

6

JavaScript/CSS/HTML, 436 bytes

b="red"
f=_=>{o.textContent='';o.style.width=w.value*10+"px";o.style.height=h.value*10+"px"}
setInterval(_=>o.insertBefore(document.createElement("s"),o.firstChild).style.background=b=["red","yellow","lime","aqua","blue","fuchsia"][Math.random()*100|0]||b,25)
#o{overflow:hidden;background:red}s{display:block;float:left;width:10px;height:10px}
<div oninput=f()><input id=h type=number min=1><input id=w type=number min=1></div><div id=o>

Neil

Posted 2016-12-29T14:24:57.587

Reputation: 95 035

4

MATL, 52 47 bytes

6:BoHZGKipY"`tGe!2YG50Yr3>?t1)}6Yr]0(1YS.015Y.T

Input is an array [ncols nrows]. Output is a figure with vector graphics.

Example run for 15 cols × 10 rows:

enter image description here

How it works

Pause time has been set to 15 ms (for the same byte count as 25 ms) to try compensate for the processing time.

To keep the color with 19/20 probability (change it with 1/20), we proceed as follows:

  • With 47/50 probability we keep the color.
  • With 3/50 probability we pick a new color choosen uniformly among the 6 colors. It may happen that the "new" color is the same as the old, and this occurs with probability 1/6.

Thus the probability of keeping the color is 47/50 + 3/(50*6) = 19/20.

6:        % Push [1 2 3 4 5 6]
B         % Convert to binary. This gives a 6×3 matrix, where each row 
          % corresponds to a number. First row is [0 0 1] (blue), second is
          % [0 1 0] (green), etc
o         % Convert to double
HZG       % Set as colormap (creates a figure)
K         % Push 4
i         % Take input array
p         % Product of array. This gives the total number of squares
Y"        % Repeat 4 that many times. This gives a row vector representing the
          % image. The initial value, 4, corresponds to red in the colormap
`         % Do...while
  t       %   Duplicate
  Ge      %   Reshape to size given by the input. Gives a matrix where each
          %   entry  will be interpreted as a pointer to the colormap
  !       %   Transpose. This is required because the color shifting will be
          %   done in column-major order: down, then across; whereas we want
          %   the opposite
  2YG     %   Show matrix as image using the defined colormap
  50Yr    %   Push a uniformly distributed random integer between 1 and 50
  3>      %   True if greater than 3. This happens with probability 47/50
  ?       %   If true
    t1)   %     Duplicate and get first entry (to repeat the first color)
  }       %   Else
    6Yr   %     Push a uniformly distributed random integer between 1 and 6.
          %     This is the new color (possibly the same as the old)
  ]       %   End
  0(      %   Assign that color (repeated or new) to the last entry of the row
          %   vector representing the image
  1YS     %   Circularly shift to the right. The last value becomes the first
 .015Y.   %   Pause 0.015 ms
 T        %   Push true
          % End (implicit). Since the top of the stack is true, this creates
          % an infinite loop

Luis Mendo

Posted 2016-12-29T14:24:57.587

Reputation: 87 464

3

MATLAB, 153 147 bytes

Note: The GIF shown is of the older version, which is nice as it does not display axes (see edit history), but was extremely slow due to the implementation of imshow. For the current version, Chelsea G.'s MATLAB answer or Luis Mendo's MATL answer show the same result as my current version.

Size is taken as a 2x1 vector, so call as e.g.:

>> l([5 5])

function l(s)
c=eye(3);x=eye(s);while 1
a=rand>.94;x=[a*randi(6)+~a*x(1),x(1:end-1)];imagesc(reshape(x,s)',[1,6]);colormap([c;1-c])
pause(.025)
end

This answer exploits the subtleties of the MATLAB language. For example, x is initalized as an m x n zero matrix, but so-called linear indexing allows for circular shifting with 1-dimensional indices. Weak typing allows for multiplication with logicals, so that if statements are avoided (a trick I heavily used back in the days of programming on a TI-84 calculator). Even though a colormap is read row-wise, MATLAB treats it as a normal matrix, so that eye(3) can be used to create red, green and blue, and 1-eye(3) the other three colours. A simple reshape brings the linear vector back into matrix form, which is mapped to the desired colours using ind2rgb. Finally, imagesc, shows the image, shown at default figure size (which is big enough for the requirements). As luck would have it, imagesc does not mind values being outside the specified range, so eye can be used to initialize the matrix since both 1 and 0 are considered red.

enter image description here

Sanchises

Posted 2016-12-29T14:24:57.587

Reputation: 8 530

1Oops, forgot to upvote you... I love all your small tricks :-) – CG. – 2017-01-04T22:30:51.610

Is it correct that the random color never is red in the updated version? It appears that way in Octave at least (don't have MATLAB). – Stewie Griffin – 2019-03-16T22:00:43.340

@StewieGriffin I must've been sleeping when I made this. You are of course perfectly right - and it saved me a byte as well... – Sanchises – 2019-03-17T07:31:34.493

(make that two bytes) – Sanchises – 2019-03-17T07:37:09.677

2

Python 3.6 (316 Bytes)

Using ANSI colour codes and the new formatted string literals of Python 3.6 (PEP 489) (the f"{X}" magic).

Otherwise it’s just pretty basic, but obfuscated python. Width and Height are passed as arguments on the command line.

import random as n,time,sys
r=range
X="\x1b["
C=[f"{X}1;4{x}m " for x in r(1,7)]
w,h=list(map(int,sys.argv[1:]))
a=[C[0]for i in r(w*h)]
while 1:
 print(f"{X}0m{X}2J{X}f");f=a[0];a.pop();a[:0]=n.choice([x for x in C if x!=f])if n.random()<=.05 else f,
 for i in r(0,h*w,w):print(*a[i:i+w],sep="")
 time.sleep(.025)

enter image description here

Jonas Schäfer

Posted 2016-12-29T14:24:57.587

Reputation: 200

You can spare 6 bytes by using w,h=map(int,sys.argv[1:]), unpacking works with any iterable (of the right size), the call to list is superfluous. – Sebastian Riese – 2018-07-19T23:22:30.317

Another few byte down: "\x1b[" => "\33[" (using octal instead of hex escapes), then the X abbreviation and the format strings actually make it longer (and by getting rid of f"" you get compatibility with any python3). (this will bring it down to 301 bytes). – Sebastian Riese – 2018-07-19T23:28:38.823

Oops, you use {x} once ... but you still win with getting rid of X. – Sebastian Riese – 2018-07-19T23:32:31.993