Dibujar un copo de nieve

18

Joe vive en las Bahamas. Es invierno. Sus hijos están decepcionados porque no hay nieve. Joe necesita hacer nieve para sus hijos. Afortunadamente, tiene una impresora tridimensional. Planea hacer copos de nieve con él. Lamentablemente, no tiene idea de cómo se vería un copo de nieve. De hecho, ¡nunca ha visto un copo de nieve! Permítanos ayudarlo creando un programa que genere automáticamente una imagen 2D de un copo de nieve para él.

Entrada

El diámetro de la imagen (en píxeles), el porcentaje de la imagen que en realidad es un copo de nieve.

Salida

Una imagen de un copo de nieve con el diámetro requerido. Puede guardarse en un archivo o mostrarse al usuario.

Especificaciones

Crea una cuña que tenga un ángulo de 30 grados. Crea un árbol browniano con semilla inicial en el punto de la cuña. Refleje la cuña alrededor del centro de la imagen 12 veces para generar el resto de la imagen. El copo de nieve tiene el color Blanco. El fondo tiene el color negro.

Puntuación

Debido al hecho de que hay diferentes formas de generar un árbol browniano, el puntaje es 10 * número de votos a favor: puntaje de golf.

El puntaje de golf se define como el número de bytes en el programa con los siguientes bonos:

-20% Puede especificar arbitrariamente la simetría del copo de nieve.

-50% Puede especificar la forma del copo de nieve. (Al poder especificar la relación de las longitudes de los lados de la cuña).

La puntuación más alta gana.

Aquí hay una imagen de cuál sería la forma de la cuña con una relación de aproximadamente 2:

Cuña

Marcador:

Martin Buttner: 10 * 14-409 = -269

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

Optimizador: 10 * 5 - 648 = -598

¡El ganador es Martin con puntaje -269!

El numero uno
fuente
Relacionado
Martin Ender
99
No puedo entender por qué, si supuestamente estamos ayudando a alguien que nunca ha visto un copo de nieve a saber cómo son, se supone que debemos hacer que tengan una simetría rotacional de orden 4. ¿Se supone que debemos estar trolleando al pobre tipo?
Peter Taylor
1
@Conor "El puntaje es 10 * número de votos a favor - puntaje de golf". Ese programa tendría un puntaje de -300000000. Eso es muy bajo.
TheNumberOne
1
¡Cuñas de 6x60 grados! una mejora de lo que decía en el momento del comentario de @PeterTaylor, pero en realidad necesita 12x30deg wedges .. 6 para el lado derecho de cada uno de los 6 puntos, y 6 reflejados para el lado izquierdo de cada punto. Por cierto, no entiendo el segundo bono
Level River St
2
@Optimizer Hecho, debería ser más claro ahora.
TheNumberOne

Respuestas:

16

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]

Sin golf:

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

Esto espera ingresar el formulario {n,p}donde nestá el tamaño de la imagen en píxeles, y pes el porcentaje de la imagen que cubrirá el copo de nieve.

Se necesita algo así como medio minuto para generar un copo de nieve con los parámetros dados. Puede acelerarlo cambiando el valor de mde 999a 99, pero luego el resultado parece un poco escaso. Del mismo modo, puede aumentar la calidad usando números más grandes, pero luego tomará mucho tiempo.

Estoy formando el árbol browniano en una red de enteros, colocando nuevas partículas en {999, 0}, y moviendo al azar a la izquierda y arriba o abajo (no a la derecha), hasta que golpean las partículas existentes. También estoy restringiendo el movimiento a la cuña entre 0 y 30 grados. Finalmente, reflejo esa cuña en el eje x y la muestro con sus 5 rotaciones.

Aquí hay algunos resultados (haga clic para una versión más grande):

ingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí

Y aquí hay dos animaciones del crecimiento del árbol browniano (10 partículas por cuña por cuadro):

ingrese la descripción de la imagen aquíingrese la descripción de la imagen aquí

Martin Ender
fuente
2
Wow me gustan ... todos ellos, en realidad. ¡Los resultados son buenos!
Sp3000
6

JavaScript, ES6, 799 740 695 658 648

Solo cuento las dos etiquetas de lienzo y la función fdel fragmento a continuación como parte del recuento de bytes. El resto de las cosas es para demostración en vivo

Para verlo en acción, simplemente ejecute el fragmento a continuación en un Firefox más reciente que proporcione el tamaño y la relación a través de los cuadros de entrada

Tenga en cuenta que tendrá que ocultar el resultado y luego volver a mostrarlo antes de un copo de nieve consecutivo

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>

Aquí hay algunos ejemplos de renders con diferentes tamaños y porcentajes. El mejor se llama SkullFlake (primero en la lista). Haga clic en las imágenes para verlas en resolución completa.

ingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí

Mucha ayuda y aportes de Martin y githubphagocyte.

Optimizador
fuente
Esto no toma el porcentaje de la imagen que se llena como entrada.
TheNumberOne
@TheBestOne ahora tiene en cuenta el porcentaje. Tenga en cuenta que, dado que se trata de un copo de nieve basado en un árbol browniano, ni el porcentaje ni la proporción de longitudes de cuña pueden ser precisos, ya que un papel de la aleatoriedad está en juego.
Optimizador
Esto califica ahora.
TheNumberOne
1

Haskell, 781733 Bytes

El programa presenta la opción "especificar la relación de las longitudes de los lados de la cuña", por lo que debe llamarla con tres argumentos de línea de comando:

./sf 150 50 40

El argumento # 1 es el tamaño de la imagen, # 2 el% de píxeles en la cuña y # 3 la longitud (en%) del lado más corto de la cuña. La imagen se guarda en un archivo llamado "o.png".

150-50-40: 150-50-40

Mi programa produce copos de nieve con puntas de corte, porque los nuevos píxeles comienzan en el eje central de la cuña (punto verde, ver más abajo) y tienden a permanecer allí, porque se mueven igualmente al azar hacia la izquierda, arriba o abajo. A medida que se descartan los píxeles fuera de la cuña, aparecen líneas rectas en el límite de la cuña (flecha verde). Fui demasiado vago para probar otros caminos para los píxeles.

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

Cuando la cuña es lo suficientemente grande (tercer argumento 100) las espigas en el eje medio pueden crecer y luego hay 12 de ellas.

150-40-100: 150-40-100

Pocos píxeles forman formas redondas (izquierda: 150-5-20; derecha 150-20-90).

150-5-20 150-20-90

El programa:

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
nimi
fuente
@Optimizer: la espiga está en el eje central de la cuña. La cuña sube y baja 15 grados hasta el eje x. En una *-*-100imagen, sus dos lados alcanzan el borde izquierdo de la imagen (vea la segunda imagen para ver la posición de la cuña). Hay píxeles en aproximadamente la mitad de los lados; las otras mitades están vacías.
nimi
1
Al usar este contador, su programa tiene una longitud de 841 bytes.
TheNumberOne
@TheBestOne: pestañas vs. espacios al sangrar. Los mezclé al agregar 4 espacios adicionales para code style. Edité mi publicación y configuré pestañas, pero aún aparecen como espacios. ¿Alguien sabe cómo arreglarlo?
nimi
@nimi En el sitio TheBestOne vinculado hay un pequeño #enlace hash en el que puedes hacer clic. Puede pegar su código con pestañas allí y vincularlo.
Sp3000
Probablemente podría hacer un enlace al código en alguna parte. Puede usar espacios en lugar de pestañas para sangrar. Puede obtener manualmente code stylesangrando cada línea 4 espacios.
TheNumberOne
0

Procesando 2 - 575 caracteres

Toma un archivo f cuya primera línea es el tamaño de la imagen y la segunda es el radio de las escamas. Cada vez que se coloca un nuevo punto, se gira alrededor del centro 12 veces. Esto crea un efecto muy similar al de una cuña girada, pero no exactamente el mismo.

  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(); 

ingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí

puedes procesar aquí

bubalou
fuente
3
Esto no se ajusta a las especificaciones. Esto calificaría si reflejara el punto alrededor del centro en lugar de rotarlo.
TheNumberOne
color(255)puede llegar color(-1)a ahorrar un byte
Kritixi Lithos