Desenhe um hexa-glifo aleatório

23

insira a descrição da imagem aqui

A imagem acima é chamada de hexa-glifo. Hexa-glifos são alguns padrões legais que eu criei enquanto rabiscava durante minha aula DiffEq. Aqui está como você faz um:

  1. Considere o seguinte conjunto de pontos, em forma de um hexagrama regular. O hexágono interno é o que conterá o glifo final, enquanto os 6 pontos externos formam uma estrela e são onde começaremos a desenhar nossas linhas.

insira a descrição da imagem aqui

  1. Dos seis pontos externos, selecione aleatoriamente um par. Por questões de eficiência, deve haver pelo menos um outro ponto entre os dois pontos selecionados (caso contrário, isso não afetaria o valor final). Então, de cada um dos dois pontos, lance um raio na direção do outro. Este raio está bloqueado pelas linhas anteriores.

insira a descrição da imagem aqui

  1. Repita esse processo até que todas as 9 arestas tenham sido formadas, conforme mostrado nas próximas imagens.

insira a descrição da imagem aqui

  1. Aqui está um exemplo de raios sendo bloqueados. As extremidades do segmento de raio ainda são visíveis, mas a porção do meio é ocluída pelos dois primeiros segmentos que desenhamos.

insira a descrição da imagem aqui

  1. Esses dois raios também são "bloqueados", mas isso não causa nenhuma diferença visível porque eles são bloqueados pela mesma outra linha.

insira a descrição da imagem aqui

  1. Avanço rápido até que todas as 9 linhas sejam desenhadas. Se você quiser uma explicação mais detalhada sobre essas etapas ignoradas, posso explicar.

insira a descrição da imagem aqui

  1. Por fim, remova os pontos da estrela. Para torná-la mais bonita, os pontos grossos também são removidos.

insira a descrição da imagem aqui

O desafio

Seu desafio é gerar uma representação visual de um hexa-glifo aleatório. Isso é código-golfe, o menor número de bytes vence.

  1. Todos os hexa-glifos possíveis devem aparecer com alguma probabilidade positiva. Diferentes hexa-glifos são gerados alterando a ordem em que as 9 arestas são desenhadas.

  2. Além disso, todas as imagens geradas pelo seu programa devem ser hexa-glifos válidos. Certos padrões (como um esboço completo do hexágono interno) não podem aparecer como um hexa-glifo e, portanto, o programa não deve ser emitido.

  3. A saída deve ser uma imagem gráfica (impressa em tela ou arquivo).

  4. O hexágono deve ser regular, mas pode aparecer em qualquer orientação.

  5. Reflexões / rotações não são consideradas únicas. (Isso pode facilitar o cumprimento do requisito 1).

PhiNotPi
fonte
8
I made up while doodling during my DiffEq class. A maneira como todas as grandes descobertas acontecem ...: P
R
Quais são os requisitos mínimos para a imagem? Até que ponto uma arte ASCII deve ser reconhecida desde que cada borda seja representada e colocada vagamente no lugar certo?
John Dvorak
@JanDvorak Eu removi a opção de arte ASCII do desafio (como 2 minutos após a publicação), porque os programas que produzem arte gráfica ASCII e saídas gráficas não são facilmente comparáveis.
PhiNotPi 03/04
e quanto a pixel art então? Um cabeçalho PPM não é muito pesado e, posteriormente, a única diferença é usar o '01'espaço intercalado em vez de ' *'.
John Dvorak
O @JanDvorak Output seria um arquivo de imagem formatado corretamente, certo? Então não vejo nada de errado nisso.
PhiNotPi 03/04

Respostas:

18

Mathematica, 273 268 264 242 bytes

c=CirclePoints;b@_=k=1>0;Graphics[Line/@Cases[Append[Join@@({c@6,{3^.5/2,-Pi/6}~c~6}),{0,0}][[b@#=!k;#]]&/@TakeWhile[#,t=k;(r=t;t=b@#;r)&]&/@Join@@RandomSample[{#,Reverse@#}&/@Partition[Range@12,3,2,1]~Join~Array[{2#,13,2#+6}&,3]],{_,__}]]

É renderizado como um sobrescrito Tno Mathematica e é um operador de transposição postfix.

A solução dos bugs levou uma eternidade ... no final, juntei algumas coisas para fazer o trabalho, então isso é definitivamente subótimo. Também me pergunto se seria melhor implementar as especificações mais literalmente através das linhas através do hexágono externo e permitir que as funções de geometria do Mathematica lidem com as interseções.

Observe que este é um programa completo e se você deseja executar o código várias vezes em uma única sessão do REPL, precisará prefixá-lo Clear[b].

Aqui estão os resultados de 20 execuções:

insira a descrição da imagem aqui

Explicação

Esta solução não utiliza os pontos estelares externos. Em vez disso, trabalha diretamente com os pontos que fazem parte do hexaglyph e as linhas que cobrem três deles por vez.

Vamos rotular os pontos:

insira a descrição da imagem aqui

1começa em um canto um pouco estranho, mas isso se deve ao comportamento padrão (também um pouco estranho) de CirclePoints. Começar o hexágono a partir daí ficou mais barato.

Agora queremos encontrar as linhas relevantes através de três desses pontos que correspondem aos pontos conectados da estrela externa. Os que estão ao redor do hexágono são, obviamente, apenas 3 pontos adjacentes (módulo 12), começando de um número ímpar. Os em todo o centro consistem em um número par n, 13e n+6.

Representações dessas linhas (na forma de listas de três pontos são geradas pelo seguinte código):

Partition[Range@12,3,2,1]~Join~Array[{2#,13,2#+6}&,3]

O Partitiongera as linhas ao redor do hexágono e Arrayas linhas através do centro. Para processar os dois feixes, mapeamos esta função sobre a lista de linhas:

{#,Reverse@#}&

Agora, nós as misturamos RandomSamplepara processá-las em uma ordem aleatória. Join @@nivela a lista de pares para que tenhamos uma lista de vigas.

Curto intervalo: para acompanhar quais pontos já estão bloqueados, usamos uma função de pesquisa b, que é inicializada Truepara todos os valores por b@_=k=1>0;. Ao processar uma viga, mantemos todos os pontos até o primeiro ponto que possui b[n] == False( incluindo aquele):

TakeWhile[#,t=k;(r=t;t=b@#;r)&]&

Eu sinto que esta é a parte mais fácil de jogar agora ... o uso de duas variáveis ​​temporárias para jogar Mastermind parece realmente caro. De qualquer forma, o resultado disso nos dá os pontos em uma linha que podemos desenhar. Agora esta função está mapeada sobre cada um desses pontos:

Append[Join@@({c@6,{3^.5/2,-Pi/6}~c~6}),{0,0}][[b@#=!k;#]]&

A primeira parte gera a lista de todos os 13 pontos usando os resultados intercalados de duas chamadas para CirclePoints(com raios diferentes para os centros das arestas e os cantos do hexágono). Observe o b@#=!kque agora define o valor da tabela de pesquisa para o ponto atual para Falseque nenhum feixe adicional possa passar por ele. Finalmente, o valor é usado como um índice na lista de coordenadas para obter o ponto 2D correto.

Cases[...,{_,__}]

Isso descarta todas as listas de elemento único, porque elas renderizam como pontos individuais (e visíveis). Por fim, renderizamos o resultado:

Graphics[Line/@...]
Martin Ender
fonte
b@_=1>0=b=1>0&
CalculatorFeline
@CatsAreFluffy Não acho que funcione, porque preciso substituir valores individuais posteriormente.
Martin Ender
Bom uso de CirclePoints.
21416
Apreciei o link do Youtube.
DanTheMan
8

Sapatos (Ruby) Rev C 184 bytes

12 bytes salvos transferindo a responsabilidade de verificar se uma meia-linha específica deve ser desenhada do programa principal para o método de desenho. O programa principal ainda precisa verificar se toda a linha está completamente bloqueada.

Shoes.app{t=[]
d=->p,q{t[p]&&t[q]||line(p/6*8,p%6*14,q/6*8,q%6*14)}
%w{1I IW WM M5 5' '1 =A P. R,}.shuffle.map{|i|b=i.sum/2
c=b*2-a=i.ord
t[a]&&t[c]||(d[a,b]
d[b,c]
t[a]=t[b]=t[c]=1)}}

Sapatos (Rubi) 205 ... Rev B 196 bytes

O Shoes é uma ferramenta baseada em rubi para criar GUIs, etc. É a primeira vez que eu o uso. mothereff.in/byte-counter conta minha submissão como 196 bytes, mas, por alguma razão, o Shoes considera 202.

Além disso, Ruby permite fazer coisas como t[a=i.ord], estranhamente, parece não funcionar como esperado com o Shoes.

Shoes.app{t=[]
d=->p,q{line(p/6*8,p%6*14,q/6*8,q%6*14)}
%w{1I IW WM M5 5' '1 =A P. R,}.shuffle.map{|i|b=i.sum/2
c=b*2-a=i.ord
t[a]&&t[c]||(t[a]&&t[b]||d[a,b]
t[b]&&t[c]||d[b,c]
t[a]=t[b]=t[c]=1)}}

Explicação

Não considero as partes da linha fora do hexágono. Eu só desenho a parte que precisa ser desenhada. O importante é se as linhas cruzam as interseções (se apenas desenharmos as partes que precisam ser desenhadas, isso significa que elas começam / terminam nas interseções).

A regra básica é que, se os dois pontos finais de uma linha foram visitados, a linha é bloqueada e não deve ser desenhada. Como as linhas são desenhadas em duas metades, também precisamos verificar se o ponto médio foi visitado para ver se cada metade deve ser desenhada ou não.

Eu mantenho o controle de quais pontos foram visitados na matriz t[]. Isso acaba contendo uma entrada para cada coordenada física na grade abaixo. Não há uma matriz lógica de 13 elementos separada. No final, t[]pode ter 87 elementos, embora apenas 13 contenham dados úteis.

Internamente, as coordenadas dos pontos finais das linhas são dadas por um único número z, onde z% 6 é a coordenada y e z / 6 é a coordenada x. Neste sistema, o hexágono é achatado. Quando as linhas são plotadas, a escala x é multiplicada por 8 e a escala y é multiplicada por 14, que é uma aproximação racional muito próxima da razão correta: 14/8 = 1,75 vs sqrt (3) = 1,732.

O sistema de coordenadas interno é mostrado abaixo, com algumas saídas de amostra.

insira a descrição da imagem aqui

Ungolfed

Shoes.app{
  t=[]                                          #Empty array for status tracking
  d=->p,q{line(p/6*8,p%6*14,q/6*8,q%6*14)}      #Drawing method. Convert p and q into x,y pairs, scale and draw line.
  %w{1I IW WM M5 5' '1 =A P. R,}.shuffle.map{|i|#take an array of the coordinates of the endpoints of each line, shuffle, then for each line
    b=i.sum/2                                   #b = midpoint of line, convert ASCII sum to number (average of the two coordinates)
    a=i.ord                                     #a = first endpoint of line, convert ASCII to number (no need to write i[0].ord)
    c=b*2-a                                     #c = second endpoint of line (calculating is shorter than writing i[1].ord)
    t[a]&&t[c]||(                               #if both endpoints have already been visited, line is completely blocked, do nothing. ELSE
      t[a]&&t[b]||d[a,b]                        #if first endpoint and midpoint have not both been visited, draw first half of line
      t[b]&&t[c]||d[b,c]                        #if second endpoint and midpoint have not both been visited, draw second half of line
      t[a]=t[b]=t[c]=1                          #mark all three points of the line as visited
    )
  }
}

Mais saídas de amostra

Isso foi feito com uma versão mais antiga do programa. A única diferença é que o posicionamento do hexaglyph na janela agora é um pouco diferente.

insira a descrição da imagem aqui

Level River St
fonte
mothereff.in/byte-counter counts my submission as 196 bytes, but for some reason Shoes counts it as 202.Não sei 100% se isso é verdade, mas acho que o motivo pelo qual o Shoes contou seu código como 202 bytes em vez de 196 é porque suas novas linhas são na verdade uma sequência de dois caracteres "\ r \ n". Isso faz com que cada nova linha seja contada duas vezes. Aqui está uma resposta de excesso de pilha em relação a \ re \ n.
K Zhang
Hehe eu não consigo superar o nome de Ruby com Shoes XD
Beta Decay
3

Python, 604 591 574 561 538 531 536 534 528 493 483 452 431 420 419 415 388 385 384 bytes

I se adaptaram a ideia de nível do rio St de verificar se uma linha será bloqueado, verificando se ambos os pontos finais da linha já foram visitados antes. Isso economiza 27 bytes. Sugestões de golfe são bem-vindas.

Editar: correção de bugs e golfe g(p,q)por 3 bytes. Jogou golfe Lpor um byte.

from turtle import*
from random import*
R=range
G=goto
*L,=R(9)
shuffle(L)
a=[0]*13
ht()
T=12
c=[(j.imag,j.real)for j in(1j**(i/3)*T*.75**(i%2/2)for i in R(T))]+[(0,0)]
def g(p,q):pu();G(c[p]);a[p]*a[q]or pd();G(c[q])
for m in L:
 p=2*m;x,y,z=R(p,p+3)
 if m<6:
  if a[x]*a[z%T]<1:g(x,y);g(y,z%T);a[x]=a[y]=a[z%T]=1
 else:
  if a[p-11]*a[p-5]<1:g(p-11,T);g(p-5,T);a[p-11]=a[p-5]=a[T]=1

Ungolfing:

from turtle import*
from random import*

def draw_line(points, p_1, p_2):
    penup()
    goto(points[p_1])
    if not (a[p] and a[q]):
        pendown()
    goto(points[p_2])

def draw_glyph():
    ht()
    nine_lines = list(range(9))
    shuffle(nine_lines)
    size = 12
    center = [0,0]

    points = []
    for i in range(12):      # put in a point of a dodecagon
                             # if i is even, keep as hexagon point
                             # else, convert to hexagon midpoint
        d = 1j**(i/3) * 12   # dodecagon point
        if i%2:
            d *= .75**.5     # divide by sqrt(3/4) to get midpoint
        points += (d.imag, d.real)
    points.append(center)

    a = [0]*13
    for m in nine_lines:
        p = 2*m
        if m<6:
            x, y, z = p, p+1, p+2
            if not (a[x] and a[z%12]):
                draw_line(points, x, y)
                draw_line(points, y, z%12)
                a[x] = a[y] = a[z%12] = 1
        else:
            if not (a[p-11] and a[p-5]):
                draw_line(p-11, 12)
                draw_line(p-5, 12)
                a[p-11] = a[p-5] = a[12] = 1

Os próprios hexa-glifos são bem pequenos, pois usamos um hexágono de 12 pixels como base (por razões de golfe). Aqui estão alguns exemplos de hexa-glifos (desculpas pelo mau cultivo):

Um exemplo de hexa-glifo Um exemplo de hexa-glifo Um exemplo de hexa-glifo Um exemplo de hexa-glifo Um exemplo de hexa-glifo Um exemplo de hexa-glifo

Sherlock9
fonte
Pode salvar alguns bytes:R=range;G=goto
Tim Čas 28/08