Desenhe um floco de neve

18

Joe mora nas Bahamas. É inverno. Seus filhos estão desapontados por não haver neve. Joe precisa fazer neve para seus filhos. Felizmente, ele tem uma impressora 3D. Ele planeja fazer flocos de neve com ele. Infelizmente, ele não tem idéia de como seria um floco de neve. Na verdade, ele nunca viu um floco de neve! Vamos ajudá-lo criando um programa que gera automaticamente uma imagem 2D de um floco de neve para ele.

Entrada

O diâmetro da imagem (em pixels), a porcentagem da imagem que na verdade é um floco de neve.

Resultado

Uma imagem de um floco de neve com o diâmetro necessário. Pode ser salvo em um arquivo ou exibido para o usuário.

Especificações

Crie uma cunha que tenha um ângulo de 30 graus. Crie uma árvore browniana com a semente inicial no ponto da cunha. Reflita a cunha ao redor do centro da imagem 12 vezes para gerar o restante da imagem. O floco de neve tem a cor branca. O fundo tem a cor Preto.

Pontuação

Devido ao fato de existirem diferentes maneiras de gerar uma árvore browniana, a pontuação é 10 * número de votos positivos - pontuação de golfe.

A pontuação de golfe é definida como o número de bytes no programa com os seguintes bônus:

-20% Pode especificar arbitrariamente a simetria do floco de neve.

-50% Pode especificar a forma do floco de neve. (Ao ser capaz de especificar a proporção dos comprimentos dos lados da cunha.)

Maior pontuação ganha.

Aqui está uma imagem de qual seria a forma da cunha com a proporção de aproximadamente 2:

Cunha

Placar:

Martin Buttner: 10 * 14 - 409 = -269

Nimi: 10 * 1-733 * .5 = -356,5

Otimizador: 10 * 5 - 648 = -598

O vencedor é Martin com pontuação -269!

O número um
fonte
Relacionado
Martin Ender
9
Não consigo entender por que, se supostamente estamos ajudando alguém que nunca viu um floco de neve a saber como eles são, devemos fazê-los ter simetria rotacional de ordem 4. Deveríamos estar trollando o pobre rapaz?
Peter Taylor
1
@Conor "A pontuação é 10 * número de votos positivos - pontuação de golfe." Esse programa teria uma pontuação de -300000000. Isso é muito baixo.
TheNumberOne
1
Cunhas 6x60deg! uma melhoria no que foi dito no momento do comentário de @PeterTaylor, mas na verdade você precisa de fatias de 12x30deg. 6 para o lado direito de cada um dos 6 pontos e 6 refletidas para o lado esquerdo de cada ponto. BTW, eu não entendo o segundo bônus
Level River St
2
@Optimizer Done, deve estar mais claro agora.
TheNumberOne

Respostas:

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]

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
 ]

Isso espera inserir o formulário {n,p}onde nestá o tamanho da imagem em pixels e pé a porcentagem da imagem a ser coberta pelo floco de neve.

Demora cerca de meio minuto para gerar um floco de neve com os parâmetros fornecidos. Você pode acelerar alterando o valor de mde 999para 99, mas o resultado parece um pouco esparso. Da mesma forma, você pode aumentar a qualidade usando números maiores, mas isso levará muito tempo.

Estou formando a árvore browniana em uma rede inteira, colocando novas partículas em {999, 0}e movendo-as aleatoriamente para a esquerda e para cima ou para baixo (não para a direita), até atingirem as partículas existentes. Também estou restringindo o movimento à cunha entre 0 e 30 graus. Finalmente, reflito essa cunha no eixo x e a mostro com suas 5 rotações.

Aqui estão alguns resultados (clique para uma versão maior):

insira a descrição da imagem aqui insira a descrição da imagem aqui insira a descrição da imagem aqui insira a descrição da imagem aqui insira a descrição da imagem aqui insira a descrição da imagem aqui insira a descrição da imagem aqui

E aqui estão duas animações do crescimento da árvore browniana (10 partículas por cunha por quadro):

insira a descrição da imagem aquiinsira a descrição da imagem aqui

Martin Ender
fonte
2
Uau, eu gosto ... de todos eles, na verdade. Os resultados são bons!
Sp3000
6

JavaScript, ES6, 799 740 695 658 648

Estou contando apenas as duas tags de tela e a função fdo snippet abaixo como parte da contagem de bytes. O restante é para demonstração ao vivo

Para assisti-lo em ação, basta executar o snippet abaixo em um Firefox mais recente, fornecendo o tamanho e a proporção através das caixas de entrada

Observe que você terá que ocultar o resultado e mostrar novamente antes de um floco de neve 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>

Aqui estão alguns exemplos de renderizações com tamanhos e porcentagens diferentes. O melhor é chamado SkullFlake (o primeiro da lista). Clique nas imagens para vê-las em resolução máxima.

insira a descrição da imagem aqui insira a descrição da imagem aqui insira a descrição da imagem aqui insira a descrição da imagem aqui insira a descrição da imagem aqui insira a descrição da imagem aqui

Muita ajuda e contribuição de Martin e githubphagocyte.

Optimizer
fonte
Isso não leva a porcentagem da imagem que é preenchida como entrada.
TheNumberOne
@TheBestOne agora leva em consideração a porcentagem. Observe que, como se trata de um floco de neve baseado em árvore browniana, nem a porcentagem nem a proporção de comprimentos de cunha podem ser precisas, pois está em jogo um papel de aleatoriedade.
Optimizer
Isso está qualificado agora.
TheNumberOne
1

Haskell, 781 733 bytes

O programa apresenta a opção "especificar a proporção dos comprimentos dos lados da cunha", então você deve chamá-lo com três argumentos de linha de comando:

./sf 150 50 40

O argumento 1 é o tamanho da imagem, 2 a porcentagem de pixels na cunha e 3 a extensão (em%) do lado mais curto da cunha. A imagem é salva em um arquivo chamado „o.png“.

150-50-40: 150-50-40

Meu programa produz flocos de neve com picos de corte, porque novos pixels começam no eixo do meio da cunha (ponto verde, veja abaixo) e tendem a permanecer lá, porque se movem igualmente aleatoriamente para a esquerda, para cima ou para baixo. À medida que os pixels fora da cunha são descartados, linhas retas aparecem no limite da cunha (seta verde). Eu estava com preguiça de tentar outros caminhos para os pixels.

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

Quando a cunha é grande o suficiente (terceiro argumento 100), os picos no eixo do meio podem crescer e então existem 12 deles.

150-40-100: 150-40-100

Poucos pixels formam formas redondas (esquerda: 150-5-20; direita 150-20-90).

150-5-20 150-20-90

O 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
fonte
@ Otimizador: o pico está no eixo do meio da cunha. A cunha sobe e desce 15 graus em relação ao eixo x. Em uma *-*-100imagem, os dois lados alcançam a borda esquerda da imagem (veja a segunda imagem para a posição da cunha). Existem pixels em aproximadamente metade dos lados - as outras metades estão vazias.
nimi
1
Usando este contador, seu programa tem 841 bytes de comprimento.
TheNumberOne 01/01
@TheBestOne: Guias x Espaços ao recuar. Eu os misturei ao adicionar 4 espaços adicionais para code style. Eu editei minha postagem e defina Guias, mas elas ainda aparecem como espaços. Alguém sabe como consertar isso?
nimi
@nimi No site TheBestOne vinculado, há um pequeno #link de hash no qual você pode clicar. Você pode colar seu código com guias e vinculá-lo.
Sp3000 01/01
Você provavelmente poderia criar um link para o código em algum lugar. Você pode usar espaços em vez de guias para recuar. Você pode obter manualmente code stylerecuando cada linha 4 espaços.
TheNumberOne 01/01
0

Processando 2 - 575 caracteres

Recebe um arquivo f cuja primeira linha é o tamanho da imagem e a segunda é o raio dos flocos. Sempre que um novo ponto é colocado, ele é girado 12 vezes no centro. Isso cria um efeito muito semelhante ao de uma cunha girada, mas não exatamente o mesmo.

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

insira a descrição da imagem aqui insira a descrição da imagem aqui

você pode obter o processamento aqui

bubalou
fonte
3
Isso não se encaixa bem nas especificações. Isso se qualificaria se você refletisse o ponto ao redor do centro em vez de girá-lo.
TheNumberOne
color(255)pode tornar-se color(-1)para salvar um byte
Kritixi Lithos