Take the 2-minute tour ×
Programming Puzzles & Code Golf Stack Exchange is a question and answer site for programming puzzle enthusiasts and code golfers. It's 100% free, no registration required.

Context

It's Valentines Day. The only one you ever loved left you yesterday for this guy she always found "stupid and uninteresting". On your way home, you've been stuck in traffic, listening to old songs on the radio, the rain hitting the windscreen is rocking you. After a while in your car, you find yourself alone in your small apartment being unable to think of something else but her. There is no light and you stare trough the window, letting darkness surrounds you. There is no one to talk to, your friends are now gone a long time ago after warning you about this new girl haunting your mind. You start up your computer, as it's the only thing you can do, open your browser and post a new programming puzzle to stackexchange, in an attempt to change your mind.

Challenge

Write a program in the language of your choice simulating the rain which falls on the ground. The output can be composed of ASCII characters or 2D/3D rendered. The camera is fixed : you are above looking straight to the ground. Your program must includes some kind of animation like refreshing the console or the page each time you generate a new "frame". It has to be realistic, I know it's a bit subjective but let's say you can't just fill all the ground in only one big drop.

The output don't have to be an image but if you're using cryptic language it's better to provide a .gif to illustrate how it looks in action (but of course, it will not affect your score if you don't).

Rules

  • Your score is the total of bytes used
  • -20 points if you use colours
  • -50 if you achieve to render some kind of wind
  • Lowest score wins

Example

A very basic example of what should be rendered:

I hope you'll do better and enjoy this challenge.

Sorry for my english, feel free to edit my post if you want to correct some errors

Leader board

If your name is not listed it's because your attempt has been considered not fitting the rules.

Tobia - APL - 35  
j6m8 - Processing.js - 38
The Guy with The Hat - Processing - 42  
ace - Processing - 74  
kelunik - JS/CSS - 89  
Riot - Bash - 91  
Michael - JS/jQuery - 105  
Florent - HTML/JS - 123  
David Carraher - Mathematica - 134  
Doorknob - HTML/JS - 150  
undergroundmonorail - Python - 175

Congratulations to Tobia !

share|improve this question
44  
I sincerely hope the first paragraph isn't a true story. –  Kendall Frey Feb 13 at 20:53
    
@KendallFrey Indeed. –  cjfaure Feb 13 at 21:01
8  
I don't think this should be code-golf as there's too much leniency on the potential outputs –  Cruncher Feb 14 at 13:56
3  
The first paragraph along with the camera position for this rendering is quite macabre. –  Tobia Feb 14 at 19:09
2  
"stupid and uninteresting" –  Austin Feb 14 at 21:52

14 Answers 14

up vote 19 down vote accepted

APL, 105 chars/bytes* – 20 – 50 = 35 score

e←{⍞←∊'␛['⍵}
e¨'36m' '?25l' '2J'
{⍵←3⌊⍵+3×0=?t⍴50
⍵{(⍵c)←⍕¨⍵+1
e⍵';'c'H',' .∘⍟'[⍺]}¨⍳t
∇0⌈⍵-1}0⍴⍨t←24 80

*: Most APL implementations support some form of (legacy) single-byte charset, that maps APL symbols to the upper 128 byte values. Therefore, for the purpose of golfing, a program that only uses ASCII characters and APL symbols can be scored as chars = bytes.

I tested it on Nick's latest apl.js on Node.js in an OS X terminal. But I haven't used anything specific to his dialect, so it should work on any modern APL that can be run on an ANSI terminal and supports d-funs {...}, strand assignment (a b)←..., and commute , such as Dyalog for Linux or for Raspberry PI (with ⎕IO←0)

The in line 1 is a literal escape character (which is 1 byte). You can input it using Ctrl-V Esc in a Linux terminal or in Vim, or supposedly something like Alt-027 in Windows. Also, I couldn't find a reliable way to discover the terminal size, so you might want to edit the number of rows and columns at the end of the last line.

I defend the 50 bonus by the fact that each raindrop goes through the following shapes: ⍟∘. which give the impression of a slight downwards wind, given that the scene is being looked at from above. In fact, looking at the gif below, you should get the impression that each drop is gently moving downwards and to the left, before disappearing on the ground.

Ungolfed version:

e←{⍞←∊"␛["⍵}                  # utility to print escape sequence
e¨'36m' '?25l' '2J'            # set cyan, hide the cursor and clear screen
{                              # repeat (⍵=current board of raindrops)
  ⍵←3⌊⍵+3×0=?t⍴50              #   add some new drops (=3) in random places
  ⍵{                           #   print the drops (⍺=drop value, ⍵=coords)
    (r c)←⍕¨⍵+1                #     convert the coordinates to string
    e r';'c'H',' .∘⍟'[⍺]       #     print or clear the drop
  }¨⍳t                         #   ..
  ∇0⌈⍵-1                       #   remove 1 from every drop and repeat
}0⍴⍨t←24 80                    # ..starting with an empty board

Output:

enter image description here


APL, different style

Out of competition.

m←×/t←1+(ζη)←2×(βγ)←24 80
e←{⍞←∊(⎕UCS 27)'['⍵}
s←{⍵[β-1-⍳β;1+⍳γ]}
p←{⍺{e'H'⍺,⍨{⍺,';',⍵}/⍕¨⍵}¨(,s⍵)/,1+⍳βγ}
e¨'2J' '36m' '?25l'
{'/'p⍵←(200<m÷?t⍴m)∨0⍪⍵[⍳ζ;1+⍳η],0
' 'p(~⍵)∧0,⍵[1+⍳ζ;⍳η]⍪0
'.∘°'[?(+/,sδ)/3]pδ←⍵∧~d←.2<m÷⍨?t⍴m
∇⍵∧d}t⍴0

Here my aim was to give the impression of raindrops falling with a slant and accumulating on the ground, while trying to keep the number of visible drops (either falling or splattered) constant on average. The trick was to create a number of new falling drops / at every cycle and having the falling drops "wipe out" any splattered ones they travel across.

The result is strangely reminiscent of the Matrix code.

Output
(the jerk every 5s is the gif looping)

enter image description here

share|improve this answer
    
This is nice, but I don't think the ESC character is being printed right. i.stack.imgur.com/vLERQ.png I tried pasting the code with gedit too, didn't work. –  Riking Feb 15 at 2:11
    
You got your score wrong. The question says "bytes", not "chars". –  jazzpi Feb 15 at 9:37
1  
@Riking I edited that part. If you try it again it should work. –  Tobia Feb 15 at 12:44
    
@Tobia Show me a picture of you sitting at your IBM 5100, and I'll buy the chars = bytes argument. –  primo Feb 15 at 14:26
3  
@primo imageshack.com/a/img811/314/86dt.jpg –  Tobia Feb 15 at 17:25

Bash: 111 bytes - 20 = 91 points!

A contemplative gentle drizzle in your terminal. Adjust the numbers 819 and 41 for different height and width respectively.

e='printf \e';while :;do for i in {0..819};do((RANDOM<9))&&$e[1\;36m.||$e[1C;((i%41<1))&&$e'
';done;$e[20A;done

Screenshot

A pleasant bonus is the way the cursor pitter-patters across the rain area.

Edit: shortening from 140 bytes to 129 bytes thanks to @manatwork's suggestions. 2nd edit: shortening from 129 bytes to 111 bytes thanks to @manatwork's and @Tobia's suggestions, plus additional inspiration - see comments.

(Note: screenshot shows previous less-golfed version of the code, which is functionally identical)

share|improve this answer
1  
You can spare: 2 characters by joining echo's options; 2 characters by using \e instead of \033; 3 characters by using : instead of true; 5 characters by using arithmetic evaluation (((…))): e='echo -ne \e';while :;do for i in {0..19};do for i in {0..40};do ((RANDOM<9))&&$e"[1;36m".||$e[1C;done;$e' ';done;$e[20A;done. –  manatwork Feb 14 at 9:46
1  
You not need the sigil inside arithmetic evaluation. And there must be a trailing newline at the end of your code. Should be only 127 characters. –  manatwork Feb 14 at 17:46
1  
I believe what @manatwork means is that you don't need to use the dollar sign inside double parentheses: ((RANDOM<9)) works just as well. Also, you could try shrinking your code by combining the two for {0..19} and {0..40} into a single for {0..819}, using something like $((i%41)) inside it. –  Tobia Feb 14 at 19:24
1  
Someone stop me!! e='printf \e' is 2 char shorter than e='echo -ne \e'! –  Tobia Feb 14 at 19:34
1  
And trim another char by using $e[C –  Tobia Feb 14 at 19:47

Python, 312 bytes - 50 (wind) = 262

from pygame import*
R=__import__('random').randint
t,u=640,480;init();d=display;s=d.set_mode((t,u))
w=[255]*3;r=range(t)
a=[[R(0,t),R(0,u),R(3,6)]for i in r]
while time.wait(9):
 d.flip();event.get();s.fill(R(0,99)<1and w)
 for i in r:x,y,z=a[i];draw.line(s,w,(x,y),(x+z,y+2*z));a[i][0]=(x+z)%t;a[i][1]=(y+z*2)%u

Sample output (a 50-frame loop):

Actual playpack is significantly faster than gifs allow.

share|improve this answer
3  
@ChristianCareaga I could, but I think white looks better. –  primo Feb 14 at 7:53
7  
Only problem in my eyes: You're viewing the rain from the side, while the rules state a fixed camera looking straight down. –  Johannes H. Feb 14 at 9:13
6  
@JohannesH. Or, it's a very, very windy day. –  primo Feb 14 at 9:16
14  
I like the occasional flashes of lightning! :P –  The Guy with The Elf Hat Feb 14 at 16:56
2  
One character gets saved by using R=__import__("random").randint instead of the from random... line. –  SimonT Feb 15 at 6:29

HTML / JS, 170 chars - 20 = 150 points

<canvas id=c></canvas><script>d=400;with(c)width=height=d,t=getContext('2d');t.fillStyle='blue';setInterval("t.fillRect(Math.random()*d,Math.random()*d,5,5)",50)</script>

(sidenote: golfed further by passing a string to setInterval, with, automatic ID variable names... it feels so wrong! shudders)

Sample GIF (sorry for terrible quality ;)):

It just draws random blue rectangles.

HTML / JS, 309 chars - 20 - 50 = 239 points

Now with wind!

<canvas id=c></canvas><script>s=400;r=Math.random;with(c)width=height=s,t=getContext('2d');t.fillStyle='blue';o=[];setInterval("t.clearRect(0,0,s,s);for(i=0;++i<o.length;)d=o[i],t.fillRect(d[0],d[1],d[2],d[2]),d[0]+=1,d[1]+=2,d[2]-=1,d[2]<0?o.splice(i,1):0;if(r()<.6)o.push([r()*400,r()*400,20])",50)</script>

Output:

share|improve this answer
    
163 bytes: <canvas id=c /><script>d=400;with(c)width=height=d,t=getContext('2d');t.fillStyle='blue';setI‌​nterval("t.fillRect(Math.random()*d,Math.random()*d,5,5)",50)</script> AAAHHH! I used with! I feel dirtier than passing a string to setInterval :p –  Niet the Dark Absol Feb 13 at 23:23
    
@NiettheDarkAbsol Thanks ;) It seems that (on Chrome at least) canvas can't auto-close, but other than that that works perfectly! (Also, using automatic ID variable names feels so dirty as well :D) –  Doorknob 冰 Feb 13 at 23:29
    
Some browsers seem more lenient in that area... But it avoids having to use onload. I must say I like @Florent's way of avoiding the duplicate Math.random() –  Niet the Dark Absol Feb 13 at 23:30
    
@Doorknob canvas autoclose in Chrome! My answer was developed/tested in this browser. –  Florent Feb 14 at 7:57
1  
Upvote for the second one, with the drips sinking into the ground/puddle. –  GreenAsJade Feb 16 at 7:58

JS + jQuery (172-20-50 = 102)

Copy/Paste that line in the browser console (generally press F12 key) :

r=Math.random;w=$(window);setInterval("$('<b>♥</b>').css({color:'red',position:'fixed',top:r()*w.height(),left:r()*w.width()}).appendTo('body').animate({fontSize:0},3e3)",9)

Animated red hearts rain for Valentine's day !

enter image description here

share|improve this answer
1  
well, it's not realistic. Hearts are coming from the top of the screen and stick to random positions of the page. That does not really match the point of view condition. –  BenH Feb 14 at 10:39
9  
AUGGGGHH! How do I stop it?! –  The Guy with The Elf Hat Feb 14 at 15:54
1  
@user2509848 But is there any other way? –  The Guy with The Elf Hat Feb 14 at 16:54
2  
@TheGuywithTheHat, reload the page –  Mig Feb 14 at 18:42
17  
Brilliant! I think this captures the heart of the question. –  andrewb Feb 14 at 23:17

Mathematica

134 - 20 = 114

2D

n = 99; m = Array[0 &, {n, n}]; r := RandomInteger[{1, n}, {2}]
Table[ArrayPlot[m = ReplacePart[m, r ->  1], ColorRules -> {1 -> Blue}], {k, 250}];
Export["d.gif", d]

2D


3D

The raindrop shape is made via a revolution plot around the z axis.

Initially, rain is generated for a region that extends well above the display region. The appearance of falling rain is achieved by shifting the Viewpoint upwards along the z-axis. (It is more efficient than recalculating the position of each raindrop.)

rain

r = RandomInteger; z = Table[{r@30, r@30, r@160}, {100}];
w = RevolutionPlot3D[{.7 Sin[x] Cos[x], 0,   1.4 Sin[x] }, {x, 0, -Pi/2}, 
PerformanceGoal -> "Speed"][[1]];
c = Map[Translate[w, #] &, z]; 
p = Table[Graphics3D[c, PlotRange -> {k, k + 50}], {k, 1, 100}]
Export["p.gif", p]

With Wind

There is considerable overhead to making the rain fall with wind. But I'm including here anyway.

The blue floor pretty much keeps {x,y} view region confined to the area of interest. There are some glitches, but, oh well,

r = RandomInteger;
z = Table[{r@120, r@30, r@180}, {800}];
w = RevolutionPlot3D[{.7 Sin[x] Cos[x], 0,   1.4 Sin[x] }, {x, 
     0, -Pi/2}, PerformanceGoal -> "Speed"][[1]];
c = Map[Translate[w, #] &, z];
g[k_, z1_, w_, c1_] :=
 Module[{z2},
  z2 = Cases[z, {x_, _, _} /; 0 + k < x < 30 + k];
  c = Map[Translate[w, #] &, z2];
  Graphics3D[{Polygon[{{0 + k, 0, 1 + k}, {30 + k, 0, 1 + k}, {30 + k,
        30, 1 + k}, {0 + k, 30, 1 + k}}], c}, 
   PlotRange -> {k, k + 50}]]

p = Table[g[k, z, w, c], {k, 1, 100, 1}];
Export["p.gif", p]

with wind


From Directly Above

The closest raindrops are clipped but I'll overlook that.

from above 3D

m=40;
r=RandomInteger;
positions=Table[{r@m,r@m,r@1000},{800}];
g[lowZ_,pos_]:=
Module[{hiZ=lowZ+103},
Graphics3D[{PointSize[Small],White,Point[{{0,0,lowZ},{0,m,lowZ},{m,0,lowZ},{m,m,lowZ},{0,0,hiZ},{0,m,hiZ},{m,0,hiZ},{m,m,hiZ}}],
ImageSize-> 350,Sphere/@Cases[pos,{_,_,z1_}/;lowZ<z1<hiZ-2]},PlotRange->{lowZ,hiZ}, 
ViewPoint-> {0,0,1},ImagePadding->5]]
share|improve this answer
    
Shift it to the side in under 50 characters and you lower your score. :) –  Alizter Feb 14 at 18:47
1  
I love this one –  BenH Feb 15 at 16:37
    
+1 for the last one, with the correct camera angle BUT you forgot that the camera blocks some of the rain.... ;) –  GreenAsJade Feb 16 at 7:59
    
@GreenAsJade, The clipping for the 3D (from above) viewpoint is now fixed. –  David Carraher Feb 17 at 16:54
    
Sweet, but I was being more frivolous than you thought. I was meaning that there is a physical camera looking down at this, so it should be blocking some of the raindrops :%) –  GreenAsJade Feb 18 at 0:06

HTML / JavaScript, 156 123 (143 - 20)

<body bgcolor=0 onload="t=c.getContext('2d');t.fillStyle='#07d';setInterval('n=Math.random()*4e4;t.fillRect(n%270,n/150,1,1)',1)"><canvas id=c>

Testable version in JSFiddle.
Preview:

raindrop

Annotated version:

<body bgcolor="#000">
<canvas id="c"></canvas>
<script>
  onload = function() {
    // Retrieve the rendering context
    t=c.getContext('2d');
    // Set rain color
    t.fillStyle='#07d';
    // Render whenever it is possible
    setInterval(function() {
      // Generate a number between 0 and 40,000
      // 40,000 ~= 270 * 150
      n=Math.random()*4e4;
      // Draw a raindrop.
      // Since x and y are not rounded, the raindrop looks blurry!
      t.fillRect(n%270,n/150,1,1)
    }, 1);
  };
</script>
</body>
share|improve this answer

Smalltalk (Smalltalk/X)

with random wind ;-)

|BG CLR N1 H W v WIND drops gen newDrops draw remove move buffer|


BG := Color black.
CLR := Color blue lightened.
H := 100.
W := 100.
N1 := 10.
WIND := 0.
drops := OrderedCollection new.

gen := [:n | ((1 to:n) collect:[:i | Random nextIntegerBetween:1 and:W] as:Set) collect:[:x | x@0]].
newDrops := [drops addAll:(gen value:N1)].
draw := [buffer fill:BG; paint:CLR. drops do:[:d | buffer displayPoint:d]].
remove := [drops := drops reject:[:d | d y > H]].
move := [:wind | drops := drops collect:[:d| (d x + wind)\\W @ (d y + 1)]].
v := View new openAndWait.
buffer := Form extent:(v extent) depth:24 onDevice:v device.

[
    [v shown] whileTrue:[
        draw value.
        v displayForm:buffer.
        move value:WIND.
        remove value.
        newDrops value.
        WIND := (WIND+(Random nextBetween:-1 and:1)) clampBetween:-5 and:5.
        Delay waitForSeconds:0.1.
    ]
] fork.

output in view: enter image description here

share|improve this answer
6  
It does not qualify since it's a side view of the rain falling, and not the rain hitting the ground. Still a nice wind effect. –  BenH Feb 14 at 10:11

Bash

while true;do echo " / / / / /";echo "/ / / / / ";done

I'm not sure this should be a code golf because there isn't a strict requirement on what the "rain" must look like.

EDIT: If you want it to look like the camera is pointing straight down use this:

while true;do echo " . . . . .";echo ". . . . . ";done
share|improve this answer
7  
"The camera is fixed : you are above looking straight to the ground." This appears to be viewing the rain from the side? –  undergroundmonorail Feb 13 at 21:25
1  
Save 9 characters: while echo \ / / / / /;do echo / / / / /;done (or a few more with a recursive function but that'll quickly blow up the stack). @undergroundmonorail Strong wind, diagonal relative to the camera. –  Gilles Feb 14 at 4:27
    
@Gilles In that case, we can do while echo -e '\e[0;34m / / / / /';do echo / / / / /;done and get it down to -13 with bonuses :) –  undergroundmonorail Feb 14 at 5:19
4  
that's defintely not looking like rain :D –  Kiwy Feb 14 at 9:14
4  
I don't really think this should be considered as an answer. In short: It's not realistic. Detailed argument: If I interpreted the question correctly, the rain should fall randomly (or at least pseudo-randomly) and, over a long period of time, the distribution should be similar per unit area. (This is how rain works, right?) However in this answer, it is guaranteed that there will be no rain drops next to any rain drop, so if we take the unit area to be the area of one character, we see that the distribution is uneven. –  ace Feb 14 at 17:07

Processing, 94 - 20 = 74

void setup(){background(0);fill(0,0,255);}void draw(){ellipse(random(0,99),random(0,99),3,3);}

See the output here.

Edit: If for any reason you cannot, or do not want to, run the above java applet, you can see a javascript version here.

share|improve this answer
1  
Processing is really good for problems like these. –  cjfaure Feb 13 at 21:01
    
Cannot view it in Firefox, default security settings block the Java applet and it doesn't tell me how to override. AFAIK there is a Processing.js port to JavaScript, if you could convert it to JS and post a link I'd be thankful. –  marczellm Feb 14 at 17:06
    
@marczellm I'm using Firefox 27.0 on Ubuntu and it works fine. Are you sure there are no prompts asking you whether to allow or block the plugin? I will take a look at a JS version as well. –  ace Feb 14 at 17:11
    
@ace After I tell all promps to "allow", a security message still blocks it. Thanks for the JS version. –  marczellm Feb 14 at 17:21
1  
@marczellm added the javascript version into the answer, enjoy :) and just a blind guess, maybe your current java plugin version is outdated and has known security issues –  ace Feb 14 at 17:21

Python 2.7: 195 - 20 = 175

I'm sure there's more that can be done here, but this is what I've got for now:

import os,time
from random import*
l=[i[:]for i in[[' ']*100]*50]
while 1:
 os.system('clear')
 l[randint(0,49)][randint(0,99)]='.'
 print'\033[94m\n'.join(''.join(r)for r in l)
 time.sleep(.05)

I'll post a gif of the output when I remember how to do that.

This works on linux. Replacing 'clear' with 'cls' makes it work on windows, but then ANSI colours don't work and I lose the bonus.

I have a 2D array of one-character strings, initialized to . Every 0.05 seconds, one of them is chosen at random set to ., and the screen is redrawn.

from random import* saves two characters over import os,time,random and using random.randint() twice, though I'm not convinced that's the best way to choose a cell anyway. I wanted to use random.choice() but I couldn't think of a way around immutable strings that wouldn't waste more characters than it saved.

share|improve this answer
2  
l=[i[:]for i in[[' ']*100]*50], as per stackoverflow.com/a/6688361/1114687. 198 - 20 = 178 –  n.st Feb 14 at 2:10
    
Oh, wow, I've never seen that before. If I'm reading it correctly, the slice notation does nothing except ensure that it's a unique list and not another reference to the same one, correct? That's really cool! Thank you! –  undergroundmonorail Feb 14 at 5:09
    
I originally tried l=[[' ']*100]*50, but that just creates 50 references to the same 100-element list, so I searched for the shortest possible way to circumvent that and found the Stack Overflow answer linked above. –  n.st Feb 14 at 18:10

132 + 27 - 20 - 50 = 89

Javascript (132)

r=Math.random;setInterval("$('body').append($('<i>∘</i>').css({left:r()*2e3,top:r()*2e3}).animate({left:'+=70',fontSize:0},500))",1)

CSS (27)

i{color:blue;position:fixed

Demo: http://jsfiddle.net/kelunik/5WC87/4/embedded/result/

share|improve this answer
    
It's from side view, it does not qualify for the moment. –  BenH Feb 14 at 16:53
    
@BenH You're right, missed that point, new version is in my answer now. –  kelunik Feb 14 at 17:07
    
seems like nothing happens when trying your code in firefox console :/ –  BenH Feb 14 at 17:49
    
@BenH That's why there's a jsfiddle-demo. –  kelunik Feb 14 at 17:54
    
didn't see it. thanks –  BenH Feb 14 at 17:55

Processing, 62 - 20 = 42

void draw(){stroke(0,0,214);point(random(0,99),random(0,99));}

Generates blue pixels on a white background. Demonstration in a very similar language here: https://www.khanacademy.org/cs/rain2/6172053633761280

share|improve this answer
    
Obligatory +1 for extreme terseness. –  primo Feb 17 at 13:54

Processing.js, 86 - 20 = 66

...but it also slowly fades out (the ground absorbs the rain, naturally). Points for that?

g=99;r=random;void draw(){fill(0,9);rect(0,0,g,g);fill(0,g,r(g));rect(r(g),r(g),2,2);}

Bonus features include varying between greenish and blueish (it's clearly dirty 'city' rain).

Also, I was very pleased that I got to use a JavaScript hack in here; Note that, because this is processing.js, you can throw in things like the typeless declaration of g=99, or the alias of r for random (cross-language alias!).

Any other ideas to minify?

Readable version:

g = 99;
r = random;                  // Javascript trickery
void draw() {
    fill(0, 9);
    rect(0, 0, g, g);        // Fade the background
    fill(0, r(g), r);
    rect(r(g), r(g), 2, 2);  // Add a new drop
}

The whole thing can be viewed here.

...plus another version without fade: 58 - 20 = 38

If you don't like fading and don't mind grey dirt:

r=random;void draw(){fill(0,0,255);rect(r(99),r(99),2,2);}
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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