18
\$\begingroup\$

Joe lives at the Bahamas. It is winter. His children are disappointed that there is no snow. Joe needs to make snow for his children. Fortunately, he has a 3-d printer. He plans to make snowflakes with it. Unfortunately he has no idea how a snowflake would look like. In fact, he has never seen a snowflake! Let us help him by creating a program that automatically generates a 2d-image of a snowflake for him.

Input

The diameter of the image(in pixels), the percentage of the image that is actually a snowflake.

Output

An image of a snowflake with the required diameter. It can be saved to a file or displayed to the user.

Specifications

Create a wedge that has a 30 degree angle. Create a Brownian Tree with initial seed at the point of the wedge. Reflect the wedge around the center of the image 12 times to generate the rest of the image. The snowflake has the color White. The background has the color Black.

Scoring

Due to the fact there are different ways to generate a Brownian Tree, the score is 10 * number of upvotes - golf score.

Golf score is defined as the number of bytes in the program with the following bonuses:

-20% Can arbitrarily specify the symmetry of the snowflake.

-50% Can specify the shape of the snowflake. (By being able to specify the ratio of the lengths of the sides of the wedge.)

Highest score wins.

Here is a picture what the shape of the wedge would be with the ratio approximately 2:

Wedge

Scoreboard:

Martin Buttner: 10 * 14 - 409 = -269

Nimi: 10 * 1 - 733 * .5 = -356.5

Optimizer: 10 * 5 - 648 = -598

The winner is Martin with score -269!

\$\endgroup\$
14
  • \$\begingroup\$ Related \$\endgroup\$ Dec 18, 2014 at 20:05
  • 9
    \$\begingroup\$ I cannot understand why, if we're allegedly helping someone who has never seen a snowflake know what they look like, we're supposed to make them have rotational symmetry of order 4. Are we supposed to be trolling the poor guy? \$\endgroup\$ Dec 18, 2014 at 20:46
  • 1
    \$\begingroup\$ @Conor "The score is 10 * number of upvotes - golfscore." That program would have a score of -300000000. That is very low. \$\endgroup\$ Dec 19, 2014 at 0:05
  • 1
    \$\begingroup\$ 6x60deg wedges! an improvement on what it said at the time of @PeterTaylor 's comment , but actually you need 12x30deg wedges.. 6 for the right hand side of each of the 6 points, and 6 reflected ones for the left hand side of each point. BTW, I don't understand the second bonus \$\endgroup\$ Dec 19, 2014 at 15:20
  • 2
    \$\begingroup\$ @Optimizer Done, should be clearer now. \$\endgroup\$ Dec 30, 2014 at 4:38

4 Answers 4

16
+50
\$\begingroup\$

Mathematica, 409 bytes

{n,p}=Input[];m=999;Clear@f;_~f~_=0;0~f~0=1;r=RandomInteger;For[i=0,i<m,++i,For[x=m;y=0,f[x+1,y]+f[x-1,y]+f[x,y+1]+f[x,y-1]<1,a=b=-m;While[x+a<0||y+b<0||(y+b)/(x+a)>Tan[Pi/6],a=-r@1;b=r@2-1];x+=a;y+=b];x~f~y=1];Graphics[{White,g=Point/@Join@@{c=Cases[Join@@Table[{i,j}-1,{i,m},{j,m}],{i_,j_}/;i~f~j>0],c.{{1,0},{0,-1}}},Array[Rotate[g,Pi#/3,{0,0}]&,6]},Background->Black,ImageSize->n*p,ImageMargins->n(1-p)/2]

Ungolfed:

{n,p}=Input[];
m = 999;
ClearAll@f;
_~f~_ = 0;
0~f~0 = 1;
r = RandomInteger;
For[i = 0, i < m, ++i,
  For[x = m; y = 0, 
   f[x + 1, y] + f[x - 1, y] + f[x, y + 1] + f[x, y - 1] < 1,
   a = b = -m;
   While[x + a < 0 || y + b < 0 || (y + b)/(x + a) > Tan[Pi/6],
    a = -r@1;
    b = r@2 - 1
    ];
   x += a;
   y += b
   ];
  x~f~y = 1
  ];
Graphics[
 {White, g = 
   Point /@ 
    Join @@ {c = 
       Cases[Join @@ Table[{i, j} - 1, {i, m}, {j, m}], {i_, j_} /;
          i~f~j > 0], c.{{1, 0}, {0, -1}}}, 
  Array[Rotate[g, Pi #/3, {0, 0}] &, 6]},
 Background -> Black,
 ImageSize -> n*p,
 ImageMargins -> n (1 - p)/2
 ]

This expects input the form {n,p} where n is the image size in pixels, and p is the percentage of the image to be covered by the snowflake.

It takes something like half a minute to generate a snowflake with the given parameters. You can speed it up by changing the value of m from 999 to 99, but then the result looks a bit sparse. Likewise, you can crank up the quality by using larger numbers, but then it'll take very long.

I'm forming the Brownian tree on an integer lattice, placing new particles at {999, 0}, and moving the randomly to the left and up or down (not to the right), until they hit the existing particles. I'm also constraining the motion to the wedge between 0 and 30 degrees. Finally, I reflect that wedge on the x-axis, and display it with its 5 rotations.

Here are some results (click for larger version):

enter image description here enter image description here enter image description here enter image description here enter image description here enter image description here enter image description here

And here are two animations of the Brownian tree growing (10 particles per wedge per frame):

enter image description hereenter image description here

\$\endgroup\$
1
  • 2
    \$\begingroup\$ Wow I like... all of them, actually. The results are nice! \$\endgroup\$
    – Sp3000
    Dec 26, 2014 at 1:13
6
\$\begingroup\$

JavaScript, ES6, 799 740 695 658 648

I am only counting the two canvas tags and the function f from the snippet below as part of the byte count. Rest of the stuff is for live demo

To watch it in action, just run the snippet below in a latest Firefox giving the size and ratio via the input boxes

Note that you will have to hide the result then show again before a consecutive snowflake

f=(N,P)=>{E.width=E.height=D.width=D.height=N
E.style.background="#000"
C=D.getContext("2d"),F=E.getContext("2d")
C.strokeStyle='#fff'
M=Math,r=M.random,I=0,n=N/2
C.beginPath()
C.rect(n,n,2,2)
C.fill()
B=_=>{x=n*P/100,y=0,w=[]
do{w.push([x,y])
do{X=2*((r()*2)|0)
Y=2*(((r()*3)|0)-1)
}while(x-X<0||y-Y<0||(y-Y)/(x-X)>.577)
x-=X,y-=Y}while(!C.isPointInPath(n+x,n+y))
I++
w=w.slice(-4)
x=w[0]
C.moveTo(x[0]+n,x[1]+n)
w.map(x=>C.lineTo(n+x[0],n+x[1]))
C.stroke()
E.width=E.height=N
for(i=0;i<12;i++){F.translate(n,n)
i||F.rotate(M.PI/6)
i-6?F.rotate(M.PI/3):F.scale(1,-1)
F.translate(-n,-n)
F.drawImage(D,0,0)}
I<(n*n*P*.22/100)&&setTimeout(B,15)}
B()}
<input placeholder="Input N" id=X /><input placeholder="Input percentage" id=Y /><button onclick="f(~~X.value,~~Y.value)">Create snowflake</button><br>
<canvas id=E><canvas id=D>

Here are a few example renders with different size and percentages. The best one is called SkullFlake (first in the list). Click the images to see them in full resolution.

enter image description here enter image description here enter image description here enter image description here enter image description here enter image description here

A lot of help and input from Martin and githubphagocyte.

\$\endgroup\$
3
  • \$\begingroup\$ This doesn't take the percentage of the image that is filled as input. \$\endgroup\$ Jan 1, 2015 at 15:52
  • \$\begingroup\$ @TheBestOne it takes percentage into account now. Do note that since this is a Brownian tree based snowflake, neither the percentage nor the ratio for wedge lengths can be accurate as a role of randomness is in play. \$\endgroup\$
    – Optimizer
    Jan 1, 2015 at 23:32
  • \$\begingroup\$ This qualifies now. \$\endgroup\$ Jan 2, 2015 at 0:05
1
\$\begingroup\$

Haskell, 781 733 Bytes

The program features the „specify the ratio of the lengths of the sides of the wedge“ option, so you have to call it with three command line arguments:

./sf 150 50 40

Argument #1 is the size of the image, #2 the % of pixels in the wedge and #3 the length (in %) of the shorter side of the wedge. The image is saved in a file called „o.png“.

150-50-40: 150-50-40

My program produces snowflakes with cut-off spikes, because new pixels start on the middle axis of the wedge (green dot, see below) and tend to stay there, because they move equally random to the left, up or down. As pixels outside the wedge are discarded, straight lines appear on the boundary of the wedge (green arrow). I was too lazy to try out other paths for the pixels.

150-50-40: 150-40-40e

When the wedge is big enough (3rd argument 100) the spikes on the middle axis can grow and then there are 12 of them.

150-40-100: 150-40-100

Few pixels make round shapes (left: 150-5-20; right 150-20-90).

150-5-20 150-20-90

The program:

import System.Environment;import System.Random;import Graphics.GD
d=round;e=fromIntegral;h=concatMap;q=0.2588
j a(x,y)=[(x,y),(d$c*e x-s*e y,d$s*e x+c*e y)] where c=cos$pi/a;s=sin$pi/a
go s f w p@(x,y)((m,n):o)|x<1=go s f w(s,0)o|abs(e$y+n)>q*e x=go s f w p o|elem(x-m,y+n)f&&(v*z-z)*(b-q*z)-(-v*q*z-q*z)*(a-z)<0=p:go s(p:f)w(s,0)o|1<2=go s f w(x-m,y+n)o where z=e s;a=e x;b=e y;v=e w/100
main = do 
 k<-getArgs;g<-getStdGen;let(s:p:w:_)=map read k
 i<-newImage(2*s,2*s);let t=h(j 3)$h(\(x,y)->[(x,y),(d$0.866*e x+0.5*e y,d$0.5*e x-0.866*e y)])$take(s*d(q*e s)*p`div`100)$go s[(0,0)]w(s,0)$map(\r->((1+r)`mod`2,r))(randomRs(-1,1)g)
 mapM(\(x,y)->setPixel(x+s,y+s)(rgb 255 255 255)i)((h(j(-3/2))t)++(h(j(3/2))t));savePngFile "o.png" i
\$\endgroup\$
7
  • \$\begingroup\$ @Optimizer: the spike is on the middle axis of the wedge. The wedge goes up and down 15 degrees to the x-axis. In a *-*-100 image both of it's sides reach to the left border of the image (see the second image for the position of the wedge). There are pixels on approximately half of the sides - the other halves are empty. \$\endgroup\$
    – nimi
    Dec 31, 2014 at 14:42
  • 1
    \$\begingroup\$ Using this counter your program has length 841 bytes. \$\endgroup\$ Jan 1, 2015 at 15:40
  • \$\begingroup\$ @TheBestOne: Tabs vs. Spaces when indenting. I've mixed them up when adding additional 4 spaces for code style. I've edited my post and set Tabs, but they still appear as spaces. Does anyone know how to fix it? \$\endgroup\$
    – nimi
    Jan 1, 2015 at 15:54
  • \$\begingroup\$ @nimi On the site TheBestOne linked there's a little hash # link which you can click. You can paste your tabbed code there and link it. \$\endgroup\$
    – Sp3000
    Jan 1, 2015 at 16:02
  • \$\begingroup\$ You could probably make a link to the code somewhere. You could use spaces instead of tabs to indent. You could manually get code style by indenting every line 4 spaces. \$\endgroup\$ Jan 1, 2015 at 16:05
0
\$\begingroup\$

Processing 2 - 575 characters

Takes in a file f whose first line is the image size and the second is the flakes radius. Every time a new point is placed it is rotated around the center 12 times. This creates a very similar effect as a rotated wedge, but not exactly the same.

  int d,w,h,k,l,o,p,x,y;
  String n[] = loadStrings("f.txt");
  d=Integer.parseInt(n[0]);
  h=Integer.parseInt(n[1]);
  size(d,d);
  w=d/2;
  k=l=(int)random(d); 
  background(0);
  loadPixels();
  o=p=0;
  pixels[w*w*2+w]=color(255);
  while(true)
  {
    o=k+(int)random(-2,2);
    p=l+(int)random(-2,2);
    if(p*d+o>d*d-1 || p*d+o<0 || o<0 || o>d){
      k=l=(int)random(d);
    }
    else
    {
      if(pixels[p*d+o]==color(255))
      {
        p=l-w;
        o=k-w;
        if(o*o+p*p>h*h){break;}
        float s,c;
        for(int j=0;j<12;j++)
        {
          s=sin(PI*j/6);
          c=cos(PI*j/6);         
          x=(int)((o*c)-(p*s));
          y=(int)(((p*c)+(o*s)));
          pixels[(int)(d*y+x+w+(w*d))]=color(255);
        }
        k=l=(int)random(d);  
      }
      else
      {
        k=o;
        l=p;
      }
    }
  }
  updatePixels(); 

enter image description here enter image description here

you can get processing here

\$\endgroup\$
2
  • 3
    \$\begingroup\$ This doesn't quite fit the specifications. This would qualify if you reflected the point around the center instead of rotating it. \$\endgroup\$ Dec 25, 2014 at 5:03
  • \$\begingroup\$ color(255) can become color(-1) to save one byte \$\endgroup\$
    – user41805
    Apr 23, 2017 at 15:47

Your Answer

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

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